TCP客户与服务器进程之间典型事件时间表:
socket函数
#include<sys/socket.h> int socket(int family, int type, int protocol);
family参数执行协议族,该参数也往往被称为协议域。是以下某个常值:
type参数指明套接字类型,是以下某个常值:
protocol参数应设为下面某个协议类型常值,或者设为0,以选择所给定family和type组合的系统默认值
socket函数成功时返回一个非负整数值,与文件描述符类似,称为套接字描述符。简称sockfd。
connect函数
#include<sys/socket.h> int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen); //成功返回0,出错返回-1
如果是TCP套接字,调用connect函数将激发TCP的三路握手过程。
bind函数
bind函数把一个本地协议地址赋予一个套接字。协议地址是32位的IPv4地址或128位的IPv6地址。
#include<sys/socket.h> int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
通过bind捆绑一个端口。进程可以把一个特定的IP地址捆绑到它的套接字上。
对于TCP客户端,这就为在该套接字上发送的IP数据报指派了源IP地址。
对于TCP服务器端,这就限定该套接字只接收那些目的地为这个IP地址的客户连接。
TCP客户端通常不把IP地址捆绑到它的套接字上。
listen函数
int listen(int sockfd, int backlog);
listen函数仅由TCP服务器调用:
--当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户套接字。listen函数把一个未连接的套接字转换成一个被动套接字。
--第二个参数规定了内核应该为相应套接字派对的最大连接个数。
监听套接字的两个队列:
未完成连接队列:每个这样的SYN分节对应其中一项:已由某个客户发出并到达服务器,而服务器正在等待完成相应的TCP三路握手过程。这些套接字处于SYN_RCVD状态。
已完成连接队列:每个已完成TCP三路握手过程的客户对应其中一项。这些套接字处于ESTABLISHED状态。
accept函数
accept函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠。(假设套接字为默认的阻塞)
int accept(int sockfd, struct sockaddr *clntaddr, socklen_t *addrlen);
参数clntaddr和addrlen用来返回已连接的客户端的协议地址。
accept成功:返回值是内核自动生成的一个全新描述符,代表与所返回客户端的TCP连接。
讨论accept函数时,称第一个参数为监听套接字描述符,称它的返回值为已连接套接字描述符。
fork函数和exec函数
fork函数有两种典型用法:
1.一个进程创建一个自身的副本,这样每个副本都可以在另一个副本执行其他任何的同时处理各自的某个操作。这是网络服务器的典型用法。
2.一个进程想要执行另一个程序,该进程首先调用fork创建一个自身的副本,然后其中一个副本(子进程)调用exec把自身替换成新的程序。这是诸如shell之类程序的调性用法。
exec函数族详情见博客:
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);
这两个函数的用处:
--在没有调用bind的TCP客户端上,connect成功返回后,getsockname用于返回由内核赋予该连接的本地IP地址和本地端口号。
--getsockname可用于获取某个套接字的地址族。
--在一个以通配IP地址调用bind的TCO服务器上,与某个客户连接一旦建立,getsockname就可以用于返回由内核赋予该连接的本地IP地址。
--当一个服务器是由调用过accept的某个进程通过调用exec执行程序时,能够获取客户身份的唯一途径便是调用getpeername。
例子:获取套接字的地址族
#include "unp.h" int sockfd_to_family(int sockfd) { struct sockaddr_storage ss; socklen_t len; len = sizeof(ss); if (getsockname(sockfd, (SA *) &ss, &len) < 0) return (-1); return (ss.ss_family); //返回套接字的地址族 }