解说基本套接字函数,这里我们不考虑适用于服务器的大吞吐量并发性技术。
socket函数
为执行网络I/O,一个进程必须做的事情就是调用socket函数,指定期望的通信协议类型。使用IPv4的TCP、使用IPv6的UDP、UNIX域字节流协议等。
l 其中family参数指明协议族,或者称为协议域;
l type参数指明套接字类型;
l protocol参数应设为某个协议类型常值,或者设为0;
AF_INET:IPv4;
AF_INET6:IPv6;
SOCK_STREAM:TCP;
SOCK_DGRAM:UDP;
往往我们更多的使用一些组合:
基本函数流程:
调用socket的目的在于获取一个新建的socket句柄,该句柄需绑定了某种或多种协议,以及套接字类型。
connect函数
使用connect函数来建立与TCP服务器的连接。
l sockfd是由socket返回的套接字描述符(句柄);
l servaddr是指一个指向套接字地址结构的指针;
l addrlen是指servaddr指向的地址结构大小;
套接字地址结构必须含有服务器的IP地址和端口号。
客户端在调用函数connect前不必非得调用bind函数,因为如果需要的话,内核会确定源IP地址,并选择一个临时端口作为源端口。
如果是TCP套接字,调用connect函数将激发TCP的三路握手过程,而且仅在连接建立成功或出错后才返回,其中出错返回可能有以下几种情况。
l 若TCP客户端没有收到SYN分节的响应(ACK),则返回ETIMEDOUT错误;
l 若对客户端的SYN的响应是RST(复位),则表明该服务器主机在我们指定的端口上没有进程在等待与之连接(简单说就是没有监听)。这是一种硬错误,则返回ECONNREFUSED错误。
l 若客户端发出的SYN在中间的某个路由器上引发了一个“destination unreachable”的ICMP错误,则任务是一个软错误。客户机内核保存该消息,并按照第一种情况中描述的时间间隔继续发送SYN。若在某个规定时间后仍未收到响应,则把保存的消息作为EHOSTUNREACH或ENETUNREACH错误返回给进程。
以下两种情况也是有可能的:一是按照本地系统的转发表,根本没有到达远程系统的路径;二是connect调用根本不等待就返回。
按照TCP状态图,connect函数导致当前套接字从CLOSED状态转移到SYN_SENT状态。若成功则再转移到ESTABLISHED状态。若connect失败则该套接字不可用,必须关闭,当然我们不能再次对同一个套接字再次调用connect函数。
bind函数
bind函数把一个本地协议地址赋予一个套接字。对于网际协议,协议地址是32位的IPv4地址或128位的IPv6地址,以及16位的TCP或UDP端口的组合。
l sockfd参数指的是创建的socket句柄;
l myaddr参数是一个指向特定于协议的地址结构的指针;
l addrlen参数是该地址结构的长度;
对于TCP,bind函数可以指定一个端口号,会指定一个IP地址,也可以两者都指定,还可以都不指定。
如果不绑定,当用户调用connect或listen时,内核就会自动选取一个随机的端口来和socket进行绑定。随机选择对于客户端来说是正常的,但对于服务器来说则不适合。
进程可以把一个IP绑定在套接字上,不过这个地址必须属于其所在的主机网络接口之一(即使是多网口)。
如果端口指定为0,那么内核就在bind被调用时选择一个临时端口。然而如果指定的IP地址为通配地址,那么内核将等到套接字已连接时才选择一个本地IP地址。
对于IPv4来说,通配地址由常值INADDR_ANY来指定。
从bind函数返回的一个常见的错误时EADDRINUSE(地址已经使用);
另外SO_REUSEADDR和SO_REUSEPORT另外讨论;
listen函数
listen函数仅由TCP服务器调用,它做两件事:
当socket函数创建一个套接字时,它被假设为一个主动套接字,它被假设为一个主动套接字,它是一个将调用connect发起连接的客户端套接字。listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应该接受指向该套接字的连接请求。
其中backlog参数,我们知道内核为任何一个给定的套接字维护两个队列:
未完成连接队列和已完成连接队列;所以该参数表示队列之和;所以建议这个值做的大点。
accept函数
accept函数由TCP服务器调用,用于从已完成连接队列对头返回下一个已完成的连接。
如果已完成连接队列已空,那么进程被投入睡眠(默认阻塞方式)。
cliaddr和addrlen用来返回已连接的对端进程的协议地址。如果accept成功,那么其返回值时由内核自动生成的一个全新描述符,代表与所返回客户端的TCP连接。
close/shutdown函数
用来关闭套接字,并终止TCP连接。
close一个TCP套接字的默认行为是把该套接字标记成关闭,然后立即返回到调用进程。
之后,该套接字描述符就不能在再被调用使用,也就是说不能再被作为read或write的第一个参数。
如果只是想关闭SOCKET上的某一个服务,比如读或写服务,那么可以使用shutdown函数。
l SHUT_RD 进程不能对指定套接口进行任何读操作
l SHUT_WR 进程不能对指定套接口进行任何写操作
l SHUT_RDWR 进程不能对指定套接口进行任何读、写操作