王进文(四川大学计算机学院,四川 成都 610207)
Socket通信过程原理及相关系统调用
王进文
(四川大学计算机学院,四川 成都 610207)
通过介绍Socket的基本组成结构及其发挥的作用,论述Socket通信的整个过程并用C语言加以简要实现,以阐明Socket通信过程原理及相关系统调用。
Socket;网络通信;进程通信
Socket是相同主机进程之间或者不同主机进程之间进行通信的主流手段之一,两台计算机之间的网络通信可以通过在各自的系统中创建一个Socket,进而利用它来实现相互之间的通信。
那Socket究竟是什么呢?一个基本的Socket就是由本机IP,本机进程端口,目的IP,和目的进程端口,以及输入输出缓冲组成的一个数据结构。其中前四个属性分别用来标识本机信息和目的计算机的信息,输入输出缓冲用来暂存保存通信的数据。
Socket连接建立之前主要进行两项工作,第一项是连接建立前的两个Socket的初始化工作,第二个是两台计算机通信时的“三路握手”。下面详细说明这两个过程(假设两台通信的计算机为客户端-服务器模型,调用中用到的参数,请自行查看相关API):
(1)两个Socket的初始化。
服务器端
首先,用C语言通过系统调用socket()函数来创建套接口。通过以下程序段便可以建立一个用TCP的Socket:
int listensockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)
其次,通过系统调用int bind ()函数来初始化Socket中的本地IP地址和本地端口号。通过以下语句,便可以初始化服务器端的Socket:
bind( listensockfd, (struct sockaddr*) &saServer, sizeof (saServer));
最后,我们通过系统调用listen()函数来将已经绑定了本机IP地址和程序端口号的Socket的状态由主动(positive)转换为被动(passive)(只有处于被动的Socket才会接受对方的信息)。并调用accept()函数来获取已经准备好的套接字准备收取数据。通过以下程序段,便可以启动监听Socket,并返回Q1队列中和客户端“三路握手”完成的套接字,如图1所示。
listen( listensockfd, 5 );
int acceptSocket = accept(listenSocketfd, NULL, NULL );
客户端
通过Socket系统调用建立Socket,然后初始化客户端的IP地址和端口号,并通过connect()函数将初始化好的IP地址和端口号的数据结构绑定在新建的Socket上,与服务器端不同的是这个数据结构是为了初始化客户端的IP地址和进程端口,如图1所示。
图1
(2)两台计算机通信时的“三路握手”
第一次握手:客户端调用connect()函数将目的地(服务器端)的IP地址和进程端口初始化的同时,给内核运输层发出指令,使其将封装好(其中包含完整的四元组)的数据包(同步包,下面简称SYN包),通过更底层的协议层向目的地(服务器端)传送,以发出请求。
第二次握手:当目的地(服务器端)收到客户端发送的SYN包时,如果请求可以通过,服务器端也通过运输层封装好一个包含通过请求的SYN+ACK包,否则封装一个拒绝请求的SYN+NACK包发送给客户端,于此同时,服务器内核自动创建一个Socket,并将已将创建好的监听套接口的本地IP地址和进程端口拷贝到新创建的Socket中的本地IP地址和进程端口中,将第一次握手过程中的SYN包中的客户端的IP地址和端口号拷贝在新创建的Socket中的目的地IP地址和端口号中,然后将新创建的Socket放入监听Socket中的Q0队列(用于放置内核为服务器和不同客户端通信创建的未完成“三路握手”Socket)中。
第三次握手:当客户端收到服务器端回应的SYN+ACK包时,客户端需要再返回给服务器一个SYN包表示已经收到SYN+ACK包,与此同时,服务器将刚才放入Q0队列的Socket放入监听套接口的Q1队列中(用于放置内核为服务器和不同客户端通信创建的已经完成“三路握手”Socket),而客户端通过系统调用accept()正是获取的Q1队列中的套接字。如图2所示。
服务器端通过系统调用recv()进行数据的获取,客户端可以通过调用send()进行数据的发送。
以下便是整个Socket通信的流程图,如图2所示。
图2
[1] Jesse Storimer. TCP Sockets编 程[M].北京:人民邮电出版社,2013.
TP311
A