05 | 使用套接字进行读写:开始交流吧
该思维导图由 AI 生成,仅供参考
发送数据
- 深入了解
- 翻译
- 解释
- 总结
本文深入浅出地介绍了套接字的读写操作,着重介绍了发送数据时常用的三个函数:write、send和sendmsg,并详细解释了它们的使用场景和区别。同时,重点讲解了发送缓冲区的概念,以及在不同情况下数据的处理方式。读取数据的方法也得到了详细解释,特别是对read函数的使用和非阻塞I/O的特点进行了重点讲解。通过一个客户端-服务器的例子,展示了读取缓冲区和发送缓冲区的概念,并给出了服务器端读取数据的程序。总结中强调了send和read的使用要点,并提出了思考题,引发读者深入思考。整篇文章以实际案例为基础,适合网络编程初学者快速了解套接字的基本知识和操作方法。
《网络编程实战》,新⼈⾸单¥59
全部留言(116)
- 最新
- 精选
- 破晓^_^无限增大缓冲区肯定不行,文章中已经说过write函数发送数据只是将数据发送到内核缓冲区,而什么时候发送由内核觉定。内核缓冲区总是充满数据时会产生粘包问题,同时网络的传输大小MTU也会限制每次发送的大小,最后由于数据堵塞需要消耗大量内存资源,资源使用效率不高。 用户缓冲区到内核缓冲区 内核缓冲区IP报文,一次三拷贝,总共6次。不知对否?
作者回复: 都是强人😄
2019-08-121973 - 莫珣无限大肯定是不行的,这要从为什么使用缓存这个角度考虑。内核协议栈不确定用户一次要发多少数据,如果用户来一次就发一次,如果数据多还好说,如果少了,那网络I/O很频繁,而真正发送出去的数据也不多,所以为了减少网络I/O使用了缓存的策略。但为啥不呢无限大呢,网卡一次发出去的数据报它是有一个最大长度的,所以你不管累积再多数据最后还是要分片发送的,这样一来缓冲区太大也没什么意义,而且数据传输也是有延时要求的,不可能总是在缓冲区里待着等数据,这样就总会有空出来的缓冲区存放新数据,所以无限大缓冲区也没意义,反而还浪费资源。 发送端,假设数据能一次性复制完,那么从用户态内存拷贝到内核态内存是一次(这里应该直接拷贝到发送换冲区了),传输层组TCP包是第二次拷贝,因为要加包头,而发送缓冲区的都是紧凑内存全是应用层数据,那么分装包就需要一次拷贝,第三次,一个TCP包封装为IP报文这里可能也会需要一次拷贝,毕竟这里走到协议栈的下一层了。
作者回复: 总结的很牛
2020-01-15646 - cool什么是粘包问题?怎么解决
作者回复: TCP是流协议,根本不存在所谓粘包一说。应用层协议在设计的时候,是需要充分考虑到数据解析和还原的问题,如果设计不好,导致数据无法还原,那是应用层协议设计不佳,并不是说TCP天然有粘包问题。
2020-04-2629 - 尝试着照着老师贴出来的代码写了一个,可以跑起来 https://github.com/yingcheng-zhou/socket-reading-and-writing
作者回复: 👍
2019-08-13822 - WhatAKitty不涉及协议栈层面,应该是4次: 用户缓冲区 -> 内核缓冲区 -> 网卡 -> 对端网卡 -> 内核缓冲区 -> 用户缓冲区 老师这里提及复制几次,主要是为了引出零拷贝吧。直接由用户缓冲区复制到网卡DMA区域。减少了中间经由内核缓冲区中转的过程。
作者回复: DMA区域都知道,🐂。
2020-04-12517 - itschenxiang关于write函数的返回值那里还是不太懂,当它的返回值(数值大小)小于期望写入的字节数,那它的值代表什么呢???
作者回复: 表示缓冲区就那么大,装不下你要的那么大的字节流,就返回了目前能装下的部分,剩下的部分应用程序要自己接着往里装。
2019-09-03411 - 何赫赫while (remaining) { int n_written = send(sockfd, cp, remaining, 0); fprintf(stdout, "send into buffer %ld \n", n_written); if (n_written <= 0) { error(1, errno, "send failed"); return; } remaining -= n_written; cp += n_written; } 老师你好,send函数不是会等所有的数据都放入缓冲区后才返回吗,那返回的n_written不是应该等于remaining呀,为什么还需要while循环
作者回复: 在非阻塞I/O的情况下,send函数是"能写多少写多少",所以n_written就不等于remaining了,而send函数为了同时对阻塞I/O和非阻塞I/O起作用,就用while循环了。
2020-03-0710 - 郑祖煌增大一些是可以提高系统的效率,一定程度上减少了write/send调用,减少了用户空间和内核之间的切换。但是并不能增大吞吐量,毕竟内核的缓冲区并不能跟用户空间的缓冲区保持同步增大。把内核缓冲区总是满满的会增加粘包的频率和概率。
作者回复: 👍
2020-06-127 - tongmin_tsai老师,如果客户端和服务端要求是一次短链接,并且是一次性发完所有数据,那如果客户端的缓冲区大,服务端的缓冲区小,那么服务端如何能知道客户端这次数据完全发送完毕的?比如客户端发送1000字节,客户端的缓存区大小为1200字节,那客户端可以一次性把数据放到缓冲区,服务端这边,缓冲区大小为400字节,那么服务端是否就相当于要从缓冲区读取数据3次了,那么服务端是如何知道客户端数据发送完毕的?
作者回复: 首先,TCP的报文会被封装成一个一个TCP包,每个包都有一个sequence序列号,每个包里包含了一定的字节,当这个包被接收端接收(放到接收缓冲区中),接收端发送一个ACK,这个ACK和sequence对应,这样服务端就可以知道哪些包被接收,哪些包没有被接收。 按照你的例子,我们以400为包大小,发送了三个ACK,就可以认为1200字节发送结束。 服务端是不需要知道数据是否发送完毕的,因为TCP是一个流式的,没有办法知道客户端下个时刻还会不会发送数据,服务端只要告诉客户端我收到了1200字节就可以了。
2019-09-3046 - 学怪网络编程中为什么要循环读取数据呢?
作者回复: 因为数据像流水一样,不会结束,所以叫做stream流。
2019-12-0625