Socket机制
系统调用(system call):通过用户进程和操作系统控制权的切换,向操作系统请求服务。
应用编程接口(Application Programming Interface, API):从程序设计角度定义的许多标准系统调用函数。
套接字(Socket): Berkley Unix操作系统定义的API,用来使用TCP/IP协议的功能。也称作套接字接口。微软的Windows操作系统采用了套接字接口API,进行修改,称之为WinSock(WIndows Socket)。
计算机之间的通信就是本计算机要读取另一台计算机上的数据,或者本计算机要把数据写入到另一台计算机,这其中的过程要使用系统调用。所以socket也可以看成是应用进程和传输层之间的接口,应用进程在socket上享有完全的控制权,而在socket之下只有很少的控制权,例如选择TCP还是UDP,指定缓存空间等。
当应用进程需要进行网络通信时,首先要请求socket系统调用创建一个套接字,使得操作系统把一些系统资源(存储空间,cpu时间,网络带宽等)分配给应用进程,并用一个“套接字描述符”来表示,并返回给应用进程。今后应用进程进行的任何网络通信,都需要与该套接字描述符绑定。通过close系统调用通知操作系统对系统资源进行回收。由于一个机器中可能有多个套接字,所以需要有一个套接字描述符表来存储。
套接字描述符表存储的套接字结构
{
"协议族": "PF_INET", //PF_INET表示使用Internet的TCP/IP协议族
"服务":"SOCKET_STREAM", //表示使用流式服务,也就是TCP;SOCK_DGRAM代表UDP
"本地IP地址":"",
"远程IP地址":"",
"本地端口号":"",
"远程端口号":"",
......
}
socket通信过程中的系统调用
-
连接建立阶段
套接字创建后,它的IP地址和端口号都是空的,需要调用bind把IP和端口号写到已经建立的套接字中。如果是客户端,可以不调用bind,这时由操作系统自动分配一个动态端口号。
服务端调用bind后,还需要调用listen将套接字设置为被动方式,用来接受客户端的连接请求。而UDP是无连接的,因此不需要listen调用。
随后服务端调用accept,用来把客户端进程的连接请求提取出来。通常服务器能够同时处理多个连接,其中的一种实现方法是,主进程每次调用accept,就创建一个新的套接字,并把这个新创建的套接字返回个客户端。与此同时,主进程创建一个从属进程来通过新创建的套接字与客户进程通信。而主进程仍然使用原来的套接字接受连接请求。数据通信结束后,新创建的套接字会被关闭,同时从属进程被撤销。
客户端方面,当已经创建了套接字之后,就调用connect来和服务器建立链接。
-
数据传送阶段
客户端和服务器端都使用send传送数据,recv接受数据。通常客户端是主动发起链接的一方,客户端调用send发送请求,recv接受服务端的数据;服务端调用recv接受请求,调用send发送处理完毕的回答。通常send调用把数据复制到操作系统内核的缓存中。如果系统缓存已经满了,就暂时阻塞,直到缓存可以存放新的数据。
-
连接释放阶段
一旦客户或服务器结束使用套接字,就调用close进行释放连接和撤销套接字。
如图所示,如果是UDP,那么传输数据的函数是sendto、recvfrom。服务器和客户端的流程类似,都是socket->(bind)->(connect)->sendto/recvfrom->close。调用bind将本地ip和端口绑定在socket,如果不绑定,那么系统在发送数据的时候自动分配端口。sendto需要指定目的地址。如果无连接应用程序调用了connect,则不实际建立连接,但是会在本地记录目的ip和端口,后续就可以使用send/recv进行通信。
参考资料: