Unix网络编程-基本TCP套接字编程(2)

listen函数

每当在未完成连接队列中创建一项时,来自监听套接字的参数就复制到即将建立的连接中。连接的创建机制是完全自动的。无需服务器进程插手。下图展示了这两个队列建立连接时所交换的分组。

当来自客户的SYN到达时,TCP在未完成连接队列中创建一个新项,然后响应以三路握手的第二个分节:服务端的SYN响应,其中捎带对客户端SYN的ACK。这一项一直保留在未完成连接队列中,直到三路握手的第三个分节到达或者超时未止。如果三路握手正常完成,该项就从未完成连接队列移到已完成连接队列的队尾。当进程调用accept时,已完成连接队列中的队头将返回给进程,或者如果该队列为空,那么进程将被投入睡眠,直到TCP在该队列中放入一项才唤醒他。

关于这两个队列的处理,以下几点需要考虑

1) listen函数的backlog参数曾被规定为这两个队列总和的最大值

2) 源自Berkeley的实现给backlog增设了一个模糊因子(fudge  factor)。把他乘以1.5得到未处理队列最大长度。举例来说,通常指定为5的backlog值实际上允许最多有8项在排队。

3) 不要把backlog定义为0,因为不同的实现对此有不同的解释。如果你不想让任何客户连接到你的监听套接字上,那就关掉该监听套接字。

4) 在三路握手正常完成的前提下(也就是说没有丢失分节,从而没有重传),未完成连接队列中的任何一项在其中的留存时间就是一个RTT,而RTT的值取决于特定的客户与服务器

5) 当一个客户SYN到达时,若这些队列是满的,TCP就忽略该分节,也就是不发送RST。这么做是因为:这种情况是暂时的,客户TCP将重发SYN,期望不久就能在这些队列中找到可用的空间。要是服务器TCP立刻响应一个RST,客户端connect调用就会立即返回一个错误,强制应用进程处理这种情况,而不是让TCP的正常重传机制来处理。另外,客户无法区分响应SYN的RST究竟意味着“该端口没有服务器在监听”,还是意味着“该端口有服务器在监听,不过他的队列满了”。

6) 在三次握手完成之后,但在服务器调用accept之前到达的数据应由服务器TCP排队,最大数据量为相应已连接套接字的接收缓冲区大小。

因特网受到一种称之为SYN泛滥(SYN  flooding)的攻击。编写一个以高速率给受害主机发送SYN的程序,以装填一个或者多个TCP端口的未完成连接队列。该程序把每个SYN的源IP地址都置成随机数(称为IP欺骗 ip  spoofing)这样服务器的SYN/ACK就发往不知道什么地方。这样使合法的SYN排不上队,导致针对合法客户的服务被拒绝(denial  of  service)。

accept函数

accept函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已经完成连接。如果已完成连接队列为空,那么进程被投入睡眠(假定套接字为默认的阻塞方式)。

#include <sys/socket.h>

int  accept(int  sockfd,  struct  sockaddr*  cliaddr,  socklen_t*  addrlen);

                                                      返回:若成功则为非负描述符,若出错则为-1

参数cliaddr和addrlen用来返回已连接的对端进程的协议地址。addrlen是值-结果参数:调用前,我们将由*addrlen所引用的整数值置为由cliaddr所指的套接字地址结构的长度,返回时,该整数值即为由内核放在该套接字地址结构内的确切字节数。

如果accept成功,那么其返回值是由内核自动生成的一个全新的描述符,代表与所返回客户的TCP连接。在讨论accept函数时,我们称他的第一个参数为监听套接字描述符。他的返回值为已连接套接字描述符。一个服务器通常只创建一个已连接套接字,他在该服务器的生命周期内一直存在。内核为每个由服务器进程接收的客户连接创建一个已连接套接字。当服务器完成对某个客户的服务时,相应的已连接套接字就被关闭。如果我们对客户端地址不感兴趣的话,第二、三个参数可以都为NULL。

close函数

通常的Unix  close函数也用来关闭套接字,并终止TCP连接。

#include <unistd.h>

int  close(int  sockfd);

                     返回:若成功则为0,若出错则为-1

close一个TCP套接字的默认行为是把该套接字记成已关闭,然后立即返回到调用进程。该套接字描述符不能再由调用进程使用,也就是说他不能再作为read或者write的第一个参数。然而TCP将尝试发送已排队等待发送到对端的任何数据,发送完毕后发送正常的TCP连接终止序列。我们可以用SO_LINGER套接字选项可以改变TCP套接字的这种默认行为。

描述符引用计数,并发服务器中父进程关闭已连接套接字只是导致相应描述符的引用计数减1。如果引用计数仍大于0,这个close调用并不引发TCP的四路挥手终止序列。对于父进程与子进程共享已连接套接字的并发服务器来说,这正是所期望的。

如果我们确实想在某个TCP连接上发送一个FIN,那么可以改用shutdown函数代替close。如果父进程对每个由accept返回的已连接套接字都不调用close,那么并发服务器中将会发生什么。首先,父进程最终将耗尽可用描述符,因为任何进程在任何时刻可拥有的打开着的描述符数通常是有限的。更严重的是,没有一个客户连接将会被终止。当子进程关闭已连接套接字时,他的引用计数值由2减为1且保持为1,因为父进程永不关闭已连接套接字。这将妨碍TCP连接终止序列的发生,导致连接一直打开着。

getsockname和getpeername函数

这两个函数或者返回与某个套接字关联的本地协议地址(getsockname),或者返回某个套接字关联的外地协议地址(getpeername)。

#include <sys/socket.h>

int  getsockname(int  sockfd, struct  sockaddr*  localaddr, socklen_t *  addrlen);

int  getpeername(int  sockfd, struct  sockaddr* peeraddr, socklen_t* addrlen);

                                                                返回:若成功返回0,若出错则为-1

需要这两个函数的理由如下所述:

1) 在一个没有调用bind的TCP客户端上,connect成功返回后,getsockname用于返回由内核赋予该连接的本地IP地址和本地端口号。

2) 在以端口号0调用bind(告知内核去选择本地端口号)后,getsockname用于返回由内核赋予的本地端口号。

3) getsockname可用于获取某个套接字的地址族

4) 在一个以通配IP地址调用bind的TCP服务器上,与某个客户的连接一旦建立,getsockname就可以用于返回内核赋予该连接的本地IP地址。在这样的调用中,套接字描述符参数必须时已连接的套接字描述符,而不是监听套接字描述符。

5) 当一个服务器是由调用过accept的某个进程通过调用exec执行程序时,他能够获取客户端身份的唯一途径便是调用getpeername。inetd  fork并exec某个TCP服务器程序时就是如此情形。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容