最近开始学习C的socket编程,教材上有一处依照我对IO函数的理解,我感觉有点问题,故而研究了一下。学的很浅,可能有错漏。
1.socket编程中read()和write()内部原理
每个socket应该是在内核中具有相应的send_buffer和recv_buffer,这个就是普通文件读写中常说的内核缓冲,应该大致是一样的。以下的讨论均基于阻塞模式下,实际可以设置为非阻塞模式,但暂还没详细研究。
- read()
- 如果recv_buffer中有数据,则read()将数据从recv_buffer拷贝到用户态buffer(read的参数之一)中,返回;
- 如果recv_buffer中没有数据,则read()阻塞;
- write()
- 如果send_buffer剩余空间足以容纳相应数据,则write()将数据从用户态buffer拷贝到send_buffer中,返回;
- 如果send_buffer剩余空间不足以容纳相应数据,则write()阻塞;
2.发送方和接收方交接问题
- 类比到硬盘,可以将发送方的send_buffer理解成接收方的recv_buffer;
- tcp传输不存在数据边界的特点,有可能发送方将msg使用1次write()就写入了send_buffer,但是可能分3次才能完全传输到接收方的recv_buffer中,然后由于有数据就会读取并返回的特点,接收方应该调用3次read();
- 造成的问题就是,tcp的接收方根本不知道到底要分几次才能完全接收发送方 write()的内容,因为一次write()也可能分成几个包先后传输到接收方的recv_buffer中,就仿佛发送方调用好几次write()一样。因此接收方应该利用while循环不断进行读取,才能确保已经将某次发送方send**的内容完全读取了;
- 书中说server可能接受到client多次write()的结果,我认为是有问题的,可能是翻译错误或者笔误;
- 其实理解这个问题而言,可以忽略底层的tcp传输具体细节,单就理解作为端点的send_buffer和recv_buffer,其更新是非同步的,时间足够后才会一致;
3.问题代码分析
client:
write(clntSock, message, strlen(message));
str_len = read(clntSock, message, BUF_SIZE - 1);
server:
while ((strLen = read(accSock, msgBuf, BUF_SIZE)) != 0)
{
write(accSock, msgBuf, strLen);
}
具体的执行情况为:
1. Clinet:调用write()将数据写入send_buffer,read()因为recv_buffer为空阻塞;
2. Client:一段时间后,内核将send_buffer中数据分N次发送到server的recv_buffer中;
3. Server:read()阻塞,直到recv_buffer不为空便立刻读取到用户态的msgBuf中,不管此时有多少数据和是否完整;
4. Server:调用writer()将read()获取的数据写入到send_buffer中;
5. Server:将send_buffer中数据传输到client的recv_buffer中,有可能client调用write()发送的数据被分成N份发送到了server中,到client检测到有recv_buffer有数据时,只返回了M份;
6. Client:调用read()读取到用户态的msgBuf中,由于5中的原因,这次读取到内容可能只有调用write()发送数据中的M/N;
程序本身的需求是实现回声服务器,输入字符串,server将client发送的字符串完整地返回,然后client接受并输出相同的字符串。依据上面的执行情况分析,明显不能满足。具体的原因为每次循环中,client只调用一次read(),然后就直接输出。
于是应该修改为:
client:
int send_size, recv_size, recv_cnt;
send_size = strlen(message)
recv_size = 0;
write(clntSock, message, strlen(message));
while(recv_size < send_size)
{
recv_cnt = read(clntSock, message, BUF_SIZE - 1);
if (-1 == recv_cnt)
exit(1);
recv_size += recv_cnt;
}
4881

被折叠的 条评论
为什么被折叠?



