前段时间通过学习socket编程实现了客户端与服务器之间的大尺寸文件的传输,最近在参加一些笔试时,也遇到了socket的问题,遂整理出一些东西出来,和大家分享,也便于对这方面不了解的朋友迅速建立概念。
首先介绍一下基于TCP协议的客户端/服务器程序的一般流程:
服务器调用socket()、bind()、listen()完成初始化后,调用accept()阻塞等待,处于监听端口的状态,客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答,服务器应答一个SYN+ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从accept()返回。
数据传输的过程:
建立连接后,TCP协议提供全双工的通信服务,但是一般的客户端/服务器程序的流程是由客户端主动发起请求,服务器被动处理请求,一问一答的方式。因此,服务器从accept()返回后立刻调用read(),读socket就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用write()发送请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,在此期间客户端调用read()阻塞等待服务器的应答,服务器调用write()将处理结果发回给客户端,再次调用read()阻塞等待下一条请求,客户端收到后从read()返回,发送下一条请求,如此循环下去。
如果客户端没有更多的请求了,就调用close()关闭连接,就像写端关闭的管道一样,服务器的read()返回0,这样服务器就知道客户端关闭了连接,也调用close()关闭连接。注意,任何一方调用close()后,连接的两个传输方向都关闭,不能再发送数据了。如果一方调用shutdown()则连接处于半关闭状态,仍可接收对方发来的数据。
下图是典型的UDP客户端/服务器通讯过程,由于UDP较为简单,不再解释:
最后谈谈我在编程通过socket实现收发比较大的文件时遇到的问题。我本以为系统会自动为我拆分和合并报文,我可以直接在应用层发送和接受完整的文件。但实际测试结果告诉我,并不是这样。如果收发在一个主机上,一次只能接收最大65535字节的文件;如果不在同一主机,则只有1500字节。而发送可以将整个大文件直接送交文件描述符发送,无需自己分割,系统会自动分割;但接收时,需人为多次接收,才能把整个文件收完整。这一点请大家尤其要注意!下面提供给大家一个可以一直循环收到你所需要的数据的函数:
int read_data(int s, char *buf, int n)
{ int bcount;
int br;
bcount= 0;
br= 0;
while (bcount < n) {
if ((br= read(s,buf,n-bcount)) > 0) {
bcount += br;
buf += br;
}
else if (br < 0)
return(-1);
}
return(bcount);
}