linux select 多线程编程,双向通信,线程阻塞

Gary_Cui_1st 2016-07-16 03:31:09
我用select封装了一个socket_server和socket_client
在两个类有方法send,其本质就是一个queue,有send要求,往里面压

当socket线程启动时,在while(true)里负责不断的判断fd_set可读可写状态,当可读,即读出数据,当可写时,即把queue里的数据一次一次的send出去。

-----------------小小分割线---------
其测试使用方法为:
test_server.cpp
Thread thread(socketclient);
thread.start();
socketclient.send();//send很多次,数据很多

test_server测试方法也一样
----------------分割线--------------
无论从哪一方大量发数据,另一方等待数据,都能正常接收
----------------再次分割----------
当同时从双方大量发送数据时,程序莫名其妙的就会在test.cpp里不切到socket线程里去了,我估计socket线程被阻塞了,但是跟着,也跟不到阻塞的地方,在封装的socket里,我打了大量的log,read和write都没有阻塞,select也没有阻塞,实在想不通还有其他什么地方可以阻塞了。

求救。。。
...全文
270 9 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
renwotao2009 2016-07-16
  • 打赏
  • 举报
回复
引用 6 楼 dayuanyuan1989 的回复:
[quote=引用 5 楼 renwotao2009 的回复:] [quote=引用 3 楼 dayuanyuan1989 的回复:] [quote=引用 1 楼 renwotao2009 的回复:] read和write都没有阻塞,这很好理解因为你使用select,只有在读写条件满足时才会调用read或write select有也没有阻塞,看你select函数设置,有超时设置吗,还是一直阻塞直到读或写条件满足 至于你为什么没有切换到你的socket线程,你看看你的队列是否为空无法发送或者队列满了无法接收等情况
不好意思啊,是write阻塞了,刚测出来[/quote]你写一次之后,判断write的返回值n,即成功写入字节数,然后重新判断wfd是否可写[/quote] 我查了2天了,总算搞定了,谢谢啦,看来是我对select的用法产生了误解,select的fd_set可读可写状态只能保证我们一次的可读可写状态,如果在一次判断的可读或者可写状态里多次读或者写,其本质上已经脱离select的范畴了。[/quote]恩,确实是,也帮助我深入理解了
renwotao2009 2016-07-16
  • 打赏
  • 举报
回复
引用 7 楼 dayuanyuan1989 的回复:
[quote=引用 5 楼 renwotao2009 的回复:] [quote=引用 3 楼 dayuanyuan1989 的回复:] [quote=引用 1 楼 renwotao2009 的回复:] read和write都没有阻塞,这很好理解因为你使用select,只有在读写条件满足时才会调用read或write select有也没有阻塞,看你select函数设置,有超时设置吗,还是一直阻塞直到读或写条件满足 至于你为什么没有切换到你的socket线程,你看看你的队列是否为空无法发送或者队列满了无法接收等情况
不好意思啊,是write阻塞了,刚测出来[/quote]你写一次之后,判断write的返回值n,即成功写入字节数,然后重新判断wfd是否可写[/quote] 代码改起来也非常简单,把while改成if就可以了,反正外面是个大的while(true)包着的[/quote]是的
Gary_Cui_1st 2016-07-16
  • 打赏
  • 举报
回复
引用 5 楼 renwotao2009 的回复:
[quote=引用 3 楼 dayuanyuan1989 的回复:] [quote=引用 1 楼 renwotao2009 的回复:] read和write都没有阻塞,这很好理解因为你使用select,只有在读写条件满足时才会调用read或write select有也没有阻塞,看你select函数设置,有超时设置吗,还是一直阻塞直到读或写条件满足 至于你为什么没有切换到你的socket线程,你看看你的队列是否为空无法发送或者队列满了无法接收等情况
不好意思啊,是write阻塞了,刚测出来[/quote]你写一次之后,判断write的返回值n,即成功写入字节数,然后重新判断wfd是否可写[/quote] 代码改起来也非常简单,把while改成if就可以了,反正外面是个大的while(true)包着的
Gary_Cui_1st 2016-07-16
  • 打赏
  • 举报
回复
引用 5 楼 renwotao2009 的回复:
[quote=引用 3 楼 dayuanyuan1989 的回复:] [quote=引用 1 楼 renwotao2009 的回复:] read和write都没有阻塞,这很好理解因为你使用select,只有在读写条件满足时才会调用read或write select有也没有阻塞,看你select函数设置,有超时设置吗,还是一直阻塞直到读或写条件满足 至于你为什么没有切换到你的socket线程,你看看你的队列是否为空无法发送或者队列满了无法接收等情况
不好意思啊,是write阻塞了,刚测出来[/quote]你写一次之后,判断write的返回值n,即成功写入字节数,然后重新判断wfd是否可写[/quote] 我查了2天了,总算搞定了,谢谢啦,看来是我对select的用法产生了误解,select的fd_set可读可写状态只能保证我们一次的可读可写状态,如果在一次判断的可读或者可写状态里多次读或者写,其本质上已经脱离select的范畴了。
renwotao2009 2016-07-16
  • 打赏
  • 举报
回复
引用 3 楼 dayuanyuan1989 的回复:
[quote=引用 1 楼 renwotao2009 的回复:] read和write都没有阻塞,这很好理解因为你使用select,只有在读写条件满足时才会调用read或write select有也没有阻塞,看你select函数设置,有超时设置吗,还是一直阻塞直到读或写条件满足 至于你为什么没有切换到你的socket线程,你看看你的队列是否为空无法发送或者队列满了无法接收等情况
不好意思啊,是write阻塞了,刚测出来[/quote]你写一次之后,判断write的返回值n,即成功写入字节数,然后重新判断wfd是否可写
Gary_Cui_1st 2016-07-16
  • 打赏
  • 举报
回复
引用 1 楼 renwotao2009 的回复:
read和write都没有阻塞,这很好理解因为你使用select,只有在读写条件满足时才会调用read或write select有也没有阻塞,看你select函数设置,有超时设置吗,还是一直阻塞直到读或写条件满足 至于你为什么没有切换到你的socket线程,你看看你的队列是否为空无法发送或者队列满了无法接收等情况
没有超时处理
Gary_Cui_1st 2016-07-16
  • 打赏
  • 举报
回复
引用 1 楼 renwotao2009 的回复:
read和write都没有阻塞,这很好理解因为你使用select,只有在读写条件满足时才会调用read或write select有也没有阻塞,看你select函数设置,有超时设置吗,还是一直阻塞直到读或写条件满足 至于你为什么没有切换到你的socket线程,你看看你的队列是否为空无法发送或者队列满了无法接收等情况
不好意思啊,是write阻塞了,刚测出来
Gary_Cui_1st 2016-07-16
  • 打赏
  • 举报
回复
找到原因了,是两边select状态都可写,同时写,阻塞在两边同时写的状态。

我还搜了socket的write是否会引起阻塞,就没搜到出结果,没想到真的阻塞在write了,真是奇怪啊,既然select给出的是可写状态,为什么我写不了呢?


if(FD_ISSET(clientSocket[i].socketFd, pWset)) {
m_pAsynMutex->lock();

bool writeChk = false;
while (!m_clientSenderDataQueues[i].empty())
{
writeChk = true;
ByteArray data = m_clientSenderDataQueues[i].front();
printf("++++++start write...\n");
int writelen = write(clientSocket[i].socketFd, data.data(), data.length());
printf("-------end write...\n");
m_clientSenderDataQueues[i].pop();
if( writelen < 0 ) {
writeChk = false;
DEBUGERR("write error!");
close(clientSocket[i].socketFd);
FD_CLR(clientSocket[i].socketFd, pAllset);
restSocketInfo(clientSocket+i);
SocketIoHelper::clearQueue(m_clientSenderDataQueues[i]);
m_transferFileTasks[i].hasTask = false;
break;
}
if( writelen == 0 ) {
DEBUGWARN("writed length is zero!");
}

SocketIoHelper::log(LOG_DEBUG, "data length = %d, writed length = %d", data.length(), writelen);
}

m_pAsynMutex->unlock();

printf("send data queue is empty ? %s\n",m_clientSenderDataQueues[i].empty()?"true":"false");

if(!writeChk) {
continue;
}
}
}

不知道是否是我while循环的关系,一次性写得太多了?我写的处理是,当判断到fd_set可写,我就把所有需要写的数据,一次性全部写进去。



renwotao2009 2016-07-16
  • 打赏
  • 举报
回复
read和write都没有阻塞,这很好理解因为你使用select,只有在读写条件满足时才会调用read或write select有也没有阻塞,看你select函数设置,有超时设置吗,还是一直阻塞直到读或写条件满足 至于你为什么没有切换到你的socket线程,你看看你的队列是否为空无法发送或者队列满了无法接收等情况
本书通过55个精彩的实例,全面剖析了在Linux下编写网络应用程序的方法,并阐述了网络协议架构和开发规范。为了适应不同读者的需要,本书从最基本的Linux系统操作到网络技术的基本理念,逐步深入至Linux/UNIX下具体的编程实践,结合大量具体实例和编程经验,为读者展现Linux平台下网络编程的魅力。 全书由13章组成,内容涉及到Lindx系统编程基础、TCP/UDP协议、套接字编程概念及I/O模型、高级编程中需要用到的进程问通信同步、多路复用、多线程编程和一些高级套接字控制方法、IPv6介绍以及网络安全等。本书最后还汇集了很多网络编程的具体实例,读者可以模仿书中的范例来开发自己的应用程序。 本书内容丰富结构清晰,实例典型,文字简洁流畅,边讲边练。不但是Linux应用与开发的从业人员的指导书,而且也可作为大专院校相关专业师生教学与自学的参考书以及社会初、中级培训班教材。 基础篇 第1章 linux平台环境简单回顾 1.1 文件系统及其操作 1.1.1 文件系统结构 1.1.2 文件i/o操作 1.1.3 文件、目录及操作 1.2 标推输入输出 1.2.1 流和buffer 1.2.2 i/o类型 1.3 进程概念及控制 1.3.1 进程的运行和终止 1.3.2 进程间竞争 1.3.3 wait操作 1.4 信号 1.4.1 信号屏蔽字 1.4.2 相关操作 1.5 本章小结 第2章 进程间通信 2.1 管道和fif0 2.1.1 管道的创建和使用 .2.1.2 实例 2.1.3 popen和pclose函数 2.1.4 fifo的创建和使用 2.1.5 用fif0实现多客户服务 2.1.6 系统对管道和fif0的限制 2.2 消息队列 2.2.1 消息队列的数据结构 2.2.2 消息队列的创建 2.2.3 消息队列的操作 2.2.4 实例 2.2.5 消息队列的限制 2.3 信号量 2.3.1 信号量的数据结构 2.3.2 信号量的创建和操作 2.4 共享内存区 2.4.1 共享内存区的数据结构 2.4.2 共享内存区的创建和操作 2.4.3 实例 2.4.4 共享内存区的限制 2.5 本章小结 第3章 传输层协议tcp和udp 3.1 tcp/ip基本框架 3.1.1 网络协议与层次 3.1.2 数据的封装与分用 3.1.3 客户-服务器模型 3.2 用户数据报协议(udp) 3.2.1 udp首部 3.3 传输控制协议(tcp) 3.3.1 顺序传输 3.3.2 保证数据的可靠性与完整性 3.3.3 双向传输 3.3.4 tcp首部 3.4 tcp连接的建立、握手与结束 3.4.1 连接的建立--三方握手 3.4.2 tcp参数 3.4.3 tcp连接的终止 3.5 端口 3.5.1 端口号的分配 3.5.2 套接字对 3.6 缓冲区 3.7 标准internet服务 3.8 本章小结 第4章 tcp套接字简介 4.1 套接字概述 4.2 套接字地址结构 4.3 位顺序调整 4.3.1 字节处理函数 4.3.2 地址转换函数 4.4 建立套接字 4.5 连接 4.5.1 客户端 4.5.2 服务器端 4.6 服务进程创建 4.6.1 函数介绍 4.6.2 范例 4.7 终止连接 4.8 连接地址信息获取 4.9 socket编程client/server应用简单示例 4.9.1 一个简单www浏览器 4.9.2 inetd编程 4.9.3 获取本地ip 4.9.4 dns的使用 4.9.5 从socket中读出一行语句 4.9.6 处理用户登录及权限设置 4.10 本章小结 第5章 tcp套接字编程实例 5.1 tcp客户--服务器实例一 5.1.1 服务器端代码-vcserver.c 5.1.2 客户端代码-vcclient.c 5.1.3 运行结果 5.2 实例之二 5.2.1 服务器端代码-tcpserver.c 5.2.2 客户端代码-tcpclient.c 5.3 本章小结 第6章 udp数据报 6.1 udp通讯机制 6.1.1 基本通讯过程 6.1.2 udp与tcp的比较 6.1.3 连接的udp 6.1.4 udp应用实例 6.2 udp的应用场合 6.3 增加udp的可靠性 6.4 udp编程综合实例 6.5 本章小结 第7章 套接字中的i/o模型 7.1 阻塞式i/o 7.1.1 读阻塞 7.1.2 写阻塞 7.1.3 会接宁建立中的阻塞 7.1.4 实例一 7.1.5 实例二 7.2 非阻塞式i/o 7.2.1 读操作 7.2.2 写操作 7.2.3 建立连接过程 7.2.4 接收连接过程 7.2.5 非阻塞方式的实现 7.3 实例 7.3.1 taik实例 7.3.2 可处理并发服务的echo实例 第8章 套接字属性控制 8.1 获取和设置套接字属性 8.1.1 getsockopt函数和 setsockopt函数 8.1.2 通用套接字属性 8.2 ipv4和ipv6套接字属性 8.3 套接字属性控制 8.3.1 ioctl函数 8.3.2 fcntl函数 8.4 本章小结 提 高 篇 第9章 进程间通讯的同步 9.1 互斥锁 9.1.1 互斥锁的基本原理 9.1.2 互斥锁的基本操作函数 9.1.3 编程实例 9.2 条件变量 9.2.1 条件变量的基本过程 9.2.2 条件变量的操作函数 9.2.3 编程实例 9.3 读写锁 9.3.1 基本原理 9.3.2 读写锁的操作函数 9.4 记录上锁 9.4.1 记录上锁的基本原理 9.4.2 fcntl记录上锁 9.4.3 记录上锁应用举例 9.5 本章小结 第10章 多路复用和信号驱动i/0 10.1 多路复用 10.1.1 多路复用的基本原理 10.1.2 select函数 10.1.3 select应用实例 10.2 poll函数 10.3 多路复用编程实例 10.4 信号驱动i/0 10.5 本章小结 第11章 高级套接字i/0操作 11.1 send和recv函数 11.1.1 send函数 11.1.2 recv函数 11.2 readv和writev函数 11.2.1 readv函数 11.2.2 writev函数 11.3 sendto和recvfrom函数 11.3.1 sendto函数 11.3.2 recvfrom()函数 11.3.3 一个运用sendto()和recvfrom()函数的实例 11.4 recvmsg和sendmsg函数 11.5 辅助数据 11.6 本章小结 第12章 多线程编程及网络应用 12.1 基本概念 12.1.1 引入多线程的原因 12.1.2 线程的基本概念 12.1.3 线程的分类 12.2 线程基础 12.2.1 线程的基本操作函数 12.2.2 简单的多线程编程 12.2.3 修改线程的属性 12.3 线程应用中的同步问题 12.3.1 特定线程数据 12.3.2 互斥锁 12.3.3 条件变量 12.3.4 信号量 12.4 多线程编程的网络应用 12.4.1 函数的多线程安全性 12.4.2 多线程的实际应用 12.5 本章小结 第13章 ip协议及其属性 13.1 ipv4内容 13.1.1 ipv4数据报的格式 13.1.2 ipv4地址 13.1.3 ipv4选项 13.1.4 internet控制报文协议icmp 13.1.5 internet路由选择协议 13.1.6 ipv4的局限性及其缺点 13.2 ipv6内容 13.2.1 ipv6基本头部的格式 13.2.2 ipv4到ipv6的变化 13.2.3 ipv6地址 13.2.4 ipv6的扩展头部 13.2.5 ipv6路由选择 第14章 网络编程的安全性问题 14.1 系统子程序 14.1.1 i/0子程序 14.1.2 进程控制 14.1.3 文件属性 14.1.4 uid和gid的处理 14.2 标准c库 14.2.1 标准i/o 14.2.2 /etc/passwd处理 14.2.3 /etc/group的处理 14.2.4 加密子程序 14.2.5 运行shell 14.3 写安全的c程序 14.4 root程序的设计 14.5 本章小结 实例篇 实例一 ping 实例二 聊天室的实现 实例三 端口扫描程序 实例四 网页更新检查程序 实例五 sniffer的基本实现 实例六 ip包检查程序 实例七 ip欺骗实例 实例八 路由测试程序 实例九 linux防火墙的编写 实例十 守护进程 实例十一 普通文件传输协议(tftp) 附录 附录一 gcc命令选项 1.使用语法 2.选项 附录二 makefile文件的编写方法 1.makefile文件的基本结构 2.makefile文件编写规则 3.makefile变量 4.假象目的 5.函数 6.实用makefile举例 7.一个的功能齐全的makefile 附录三 gdb调试器 1.gdb的基本使用方法 2.gdb命令 3.在gdb下运行程序

65,186

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧