先说明一下,本人是个小白,是电子专业的,也是从C转到C++的初始阶段。现在到企业开始写企业级的代码了呢,感兴趣的小伙伴可以看看呦,保证是小白们第一步遇到的问题
!问题:并发服务器架构目前实现的两个版本(其实就是有一点点不一样啦):
1、基本上发挥select函数的特性,将服务端的socket当做文件描述符放入set中,每次循环询问是否为socket,是的话把连接描述符添加进set集合(必须要先询问是否是set集合里面的元素),else就将数据读出来进行处理数据
优点:只要客户端没有断开连接,可以快速处理从客户端发过来的数据,可以处理大量客户端连接的问题,节约了一些资源,
缺点:等到set集合中的连接描述符到达一定个数的时候,轮询set中是否有数据读所花费的时间就不能忽视了,这将导致数据处理的速度降了下来
2、accept到一个连接描述符就开启一个线程去接收数据,当然,也要用到select去看看它后续有没有数据读出来,等待时间随意,但是这样有一些致命的bug,如果有数据过来,一次并没有发完,并且也已经超时了,线程退出,这时候我们收到的数据是不完整的,当数据再来时连接描述符已经改变,接收到的数据和上次的已经连接不上了(现在考虑的是sock_stream),如果是数据包可以指定数据的长度
优点:在大量客户端连接的时候不需要轮询set集合中元素的可读性(我们现在只考虑读事件),提升了读数据的效率
缺点:就上面我说的啦
!问题:在每次使用select函数的时候都要把set里的数据重置,也就是将FD_SET(sockClient, &readSet)
写在while(true)循环里,否则你可能要收到一个惊喜,原因是每次我们都会将set集合中的元素进行改动,如果不重置第一次收到数据是可以的,但是后面会收到一个参数错误的bug,这时候你就要检查一下是否是这个原因呦!
! 问题:我写的代码客户端发送的是两个结构体,一个是消息头,一个是用户数据,这时候你要注意!!!如果我把这两个结构体打一个包发送过去,是140个字节,如果我把它们放到BUF里发送,那么字节数是138,所以在接收数据的时候,一定要知道自己发送了多少个字节!
另外,使用全局变量完成线程间通信,千万要注意,千万要注意,千万要注意,重要的事情说三遍,不要出现死锁现象!按照我的经历来说,如果控制台打印不了其他线程的信息,那么极有可能是死锁造成的原因,为了这个bug我疯了半天的,后来灵机一动才想起来是这个原因造成的,所以提醒一下大家
!在Linux中,接收数据的buf最好清零一下,初始化数组为0有可能没用,数据再打印出来的时候还是乱码,所以调用函数把它清零还是有点必要的,比如bzero(),memset(),等,头文件记不清了,可以去man一下。但是在windows下可以不用这么做,直接初始化就够了,具体为啥我也不太清楚,可能是因为内存大小的原因吧
废话不多说,上代码
server:
client :