C语言socket编程中关于read()和write()的思考

最近开始学习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;
		}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值