Socket双向聊天开发:send发送缓冲区后会自动清空以便复用吗?
Socket即时聊天代码问题解析
现有实现背景
目前编写的收发逻辑设定为:先从stdin读取数据发送到指定socket,再从该socket读取数据输出到stdout,暂未明确这种串行收发逻辑的设计初衷,现有代码大致实现如下:
void read_write(int socket_descriptor_file){ int n = 1; char buffer_in[1024]; while(n>0){ n = fread(&buffer_in, sizeof(buffer_in), 1, stdin); if(n == 0) break; ssize_t sent_status = send(socket_description_file, buffer_in, sizeof(buffer_in), 0); if(sent_status == -1){ printf("nothing sent"); } ssize_t receive_status = recv(socket_descriptor_file, buffer_in, sizeof(buffer_in), 0); if(receive_status == -1){ printf("nothing received "); } fwrite(&buffer_in, sizeof(char), sizeof(buffer_in), stdout); fflush(stdout); } }
核心问题解答
缓冲区复用特性说明
send函数不会自动清空用户态应用缓冲区:它的本质是将缓冲区中的内容拷贝到内核侧的socket发送缓冲区,整个过程不会修改应用层数组buffer_in的存储内容。- 直接复用该缓冲区存储
recv接收的内容本身是可行的:recv拿到对端数据后,会将内核缓冲区的内容直接拷贝到传入的数组地址上,自动覆盖之前存储的发送内容。但现有代码的复用逻辑存在多处适配问题:fread参数完全不适配即时聊天场景:将单块读取大小设为1024(整个缓冲区长度)、读取块数设为1,意味着必须等stdin凑够整整1024字节才会触发有效读取,日常逐行输入聊天内容时根本无法正常读取,循环会直接退出。send返回值判断不严谨:send的返回值是实际成功发送的字节数,网络状态不佳时可能仅发送部分内容,现有代码无论实际发送量多少,都直接进入后续recv流程,会出现数据截断、发送不完整的问题。- 默认情况下
recv是阻塞等待的,如果对端没有发来数据,程序会一直卡在这一步,哪怕本地stdin有新的聊天输入,也根本无法读取发送,完全不符合即时聊天的交互要求。 fwrite输出长度逻辑错误:无论recv实际收到了多少字节,都固定往stdout写1024字节,会把缓冲区里没被新数据覆盖的旧内容一起输出,出现乱码、重复字符的问题。- 未处理对端断开场景:
recv返回0时代表对端已经关闭连接,现有代码没有做分支判断,还会继续往已经失效的socket发数据,触发程序异常。
poll函数的接入方法
即时聊天场景需要同时监听三类独立事件,完全不适合用原来的“读输入→发送→阻塞等接收”的串行流程,这也是必须使用poll的核心原因:
- 需要同时响应的事件源包括:本地stdin是否有新的输入内容、业务socket是否收到对端发来的新数据、socket是否出现连接错误/异常断开。
- 核心接入流程:
- 初始化
pollfd结构体数组,配置每个监听对象的文件描述符、需要关注的事件位:标准输入(fd=0)监听POLLIN事件(代表有可读取的输入);业务socket监听POLLIN、POLLHUP、POLLERR事件(分别代表有可接收数据、连接挂起、连接异常)。 - 循环调用
poll接口等待事件触发,一般将超时参数设为-1,没有事件时挂起等待,不占用CPU资源。 - 事件触发后根据返回的事件位分别处理:如果是stdin触发读事件,就读取输入内容,通过
send发送给对端;如果是socket触发读事件,就读取收到的内容写到stdout;如果是socket触发错误/断连事件,就退出循环关闭连接。
- 初始化
现有代码的其他硬伤
- 存在变量拼写错误:形参定义为
socket_descriptor_file,但调用send时误写为socket_description_file,正常编译无法通过。 - 错误处理逻辑不完善:
send/recv返回-1时,需要先判断具体错误码,如果是被信号打断(EINTR)是可以直接重试的,不能直接判定为发送/接收失败。
内容的提问来源于stack exchange,提问作者a_confused_student
相关产品推荐
相关产品推荐

