UNIX网络编程读书笔记:基本TCP套接口编程

 

编写一个完整的TCP客户和服务器程序所须要的基本套接口函数

一、socket函数(客户端、服务器端都必须调用)

image

参数family指明协议族(family),该参数也每每被称为协议域(domain)。因此有的书上声明以下:html

image

并且对于socket函数第一个参数,在不一样书籍上可能会看到不一样前缀的取值常量列表,以下两图所示:服务器

image

image

AF_xxx与PF_xxx:网络

AF_前缀表地址族,PF_前缀表示协议族。历史上曾有这样的想法:单个协议族能够支持多个地址族,PF_值用来建立套接口,而AF_值用于套接口地址结构。但实际上,支持多个地址族的协议族历来就未实现过,并且头文件<sys/socket.h>中为一给定协议定义的PF_值老是与此协议的AF_值相等并发

二、connect函数(TCP客户端调用)

image

客户在调用函数connect前没必要非得调用bind函数,由于若是须要的话,内核会肯定源IP地址,并选择一个临时端口做为源端口。dom

若是是TCP套接口,调用connect函数将激发TCP的三路握手过程,并且仅在链接创建成功或出错时才返回,其中出错返回可能有如下几种状况:socket

(1)若TCP客户没有收到SYN分节的响应,则返回ETIMEDOUT错误。函数

(2)若对客户的SYN的响应是RST(表示复位),则代表该服务器主机在咱们指定的端口上没有进程在等待与之链接(例如服务器进程也许没在运行)。这是一种硬错误(hard error),客户一接收到RST就立刻返回ECONNREFUSED错误。spa

(3)若客户发出的SYN在中间的某个路由器上引起了一个“destination unreachable”(目的地不可达)ICMP错误,则认为是一种软错(soft error)。客户主机内核保存该消息,并按必定时间间隔延迟重发SYN。若在某个规定的时间(4.4BSD规定为75秒)后仍未收到响应,则 把保存的消息(即ICMP错误)做为EHOSTUNREACH或ENETUNREACH错误返回给进程。3d

若connect失败则该套接口再也不可用,必须关闭,咱们不能对这样的套接口再次调用connect函数。当循环调用函数connect尝试给定主机的各个IP地址直到有一个成功时,每次connect失败后,都必须close当前的套接口描述字,从新调用socket。指针

三、bind函数(服务器调用、客户端能够调用也可不调用)

image

进程能够把一个特定的IP地址捆绑到它的套接口上,不过这个IP地址必须属于其所在主机的网络接口之一。对于TCP客户,这就为在该套接口上发送的IP数据报指派了源IP地址。对于TCP服务器,这就限定该套接口只接收那些目的地为这个IP地址的客户链接。

TCP客户一般不把IP地址捆绑到它的套接口上。当链接套接口时,内核将根据所用外出网络接口来选择源IP地址,而所用外出接口则取决于到达服务器所需的路径。

image

若是指定端口号为0,那么内核就在bind被调用时选择一个临时端口;

若是指定IP地址为通配地址,那么内核将等到套接口已链接(TCP)或已在套接口上发出数据报(UDP)时才选择一个本地IP地址。

对于IPv4来讲,通配地址由常值INADDR_ANY来指定,其值通常为0. 它告知内核去选择IP地址。

不管是网络字节序仍是主机字节序,INADDR_ANY的值(为0)都同样,所以使用htonl并不是必须。不过既然头文件<netinet/in.h>中定义的全部INADDR_常值都是按照主机字节序定义的,咱们应该对全部这些常值都使用htonl。

四、listen函数(TCP服务器调用)

image

listen函数作两件事情:

(1)当socket函数建立一个套接口时,它被假设为一个主动套接口,也就是说,它是一个将调用connect发起链接的客户套接口。listen函数把一个未链接的套接口转换成一个被动套接口,指示内核应该接受指向该套接口的链接请求。调用listen致使套接口从CLOSED状态转换到LISTEN状态。

(2)backlog参数规定了内核应该为相应套接口排队的最大链接个数。

为了理解其中的backlog参数,咱们必须认识到内核为任何一个给定的监听套接口维护两个队列:

(1)未完成链接队列(incomplete connection queue),每一个这样的SYN分节对应其中一项:已由客户发出并到达服务器,而服务器正在等待完成相应的TCP三路握手过程。这些套接口处于SYN_RCVD状态。

(2)已完成链接队列(completed connection queue),每一个已完成TCP三路握手过程的客户对应其中一项。这些套接口处于ESTABLISHED状态。

image

当进程调用accept时,已完成链接队列中的队头项将返回给进程,或者若是该队列为空,那么进程将被投入睡眠,直到TCP在该队列中放入一项才唤醒它。

不要把backlog定义为0,由于不一样的实现对此有不一样的解释。若是不想让客户链接到你的监听套接口上,那就关掉该监听套接口。

五、accept函数(TCP服务器调用)

image

accept函数由TCP服务器调用,用于从已完成链接队列队头返回下一个已完成链接。若是已完成链接队列为空,那么进程被投入睡眠(假定套接口为缺省的阻塞方式)。

参数cliaddr和addrlen用来返回已链接的对端进程(客户)的协议地址。

若是accept成功,那么其返回值是由内核自动生成的一个全新描述字,表明与所返回客户的TCP链接。

在讨论accept函数时,咱们称它的第一个参数为监听套接口(listening socket)描述字(由socket建立,随后用做bind和listen的第一个参数的描述字),称它的返回值为已链接套接口(connected socket)描述字。区分这两个套接口很是重要。一个服务器一般仅仅建立一个监听套接口,它在服务器的生命期内一直存在。内核为每一个由服务器进程接受的客户链接建立一个已链接套接口(也就是说对于它的TCP三路握手过程已经完成)。当服务器完成对于某个给定客户的服务时,相应的已链接套接口就被关闭。

本函数最多返回三个值:一个既多是新套接口描述字也多是出错指示的整数、客户进程的协议地址(由cliaddr指针所指)以及该地址的大小(由addrlen指针所指)。若是咱们对返回客户协议地址不感兴趣,那么能够把cliaddr和addrlen均设置为空指针。

六、fork和exec函数(构建并发服务器)

fork函数:http://www.cnblogs.com/nufangrensheng/p/3509492.html

exec函数:http://www.cnblogs.com/nufangrensheng/p/3510821.html

七、close函数

UNIX一般的close函数也用来关闭套接口,并终止TCP链接。

image

close一个TCP套接口的缺省行为是把该套接口标记成已关闭,而后当即返回到调用进程。该套接口描述字不能再由调用进程使用,也就是说它不能再做为read或write的第一个参数。然而TCP将尝试发送已排队等待发送到对端的任何数据,发送完毕后发生的是正常的TCP链接终止序列。

八、getsockname和getpeername函数

image

getsockname:返回与某个套接口关联的本地协议地址。

getpeername:返回与某个套接口关联的远地协议地址。

这两个函数返回与某个网络链接的两端中任何一端相关联的协议地址,对于IPv4和IPv6来讲,就是IP地址和端口号的组合。

须要这两个函数的理由以下:

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

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

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

(4)在一个以通配IP地址调用bind以后的TCP服务器上,与某个客户的链接一旦创建(accept成功返回),getsockname就能够用于返回由内核赋予该链接的本地IP地址。在这样的调用中,套接口描述字必须是已链接套接口的描述字,而不是监听套接口的描述字。

(5)当一个服务器是由调用过accept的某个进程经过调用exec更换了程序时,它可以获取客户身份的惟一途径即是调用getpeername。

 

总结:

全部客户和服务器都从调用socket开始,它返回一个套接口描述字。客户随后调用connect,服务器则调用bind、listen和accept。套接口一般使用标准的close函数关闭。

image

相关文章
相关标签/搜索