之前写聊天室的时候,因为构思不够严谨,连续重构了好几次,也算是踩了好多次的坑。这次就给大家讲下我写聊天室的一点点经验和建议。
- 采用的策略:迭代服务器还是并发服务器
迭代服务器
服务器进程是一个一个处理各个客户端发来的连接的,比如一个客户端发来一个连接,那么只要它还没有完成自己的任务,那么它就一直会占用服务器的进程直到处理完毕后服务器关闭掉这个socket。
并发服务器
有很多种模型。服务器对于客户端到来的请求在线程中并发处理。这样可以“同时”处理耗时操作。
我当时因为线程池不知道如何部署,包括我的业务逻辑函数的接口不一致,就采用了迭代服务器
我的服务器大致思路:
我的客户端大致思路:
注意:其实客户端同样是可以用epoll监听stdin和服务器套接字。
(另外需要提到的一点就是只能一个线程读同一个套接字,你发送给服务器文件,如果让服务器开接受文件的线程,这个想法很自然,但是事实上会有点问题:我们在发送给服务器文件的同时,发送了一条消息,服务器的这个线程难道不会接受你发送的消息吗?如果想要通过recv 的MSG_PEEK以获取文件的flag位来表示消息类型而不去接收,这同样会出现问题,一个线程可能一直拿到这个包,另一个线程一直拿不到,而我当时的设计里服务器是没用epoll+线程池的,这会导致我整个服务器阻塞在这!因此recv让一个线程来做,而send包的时候其实可以开多线程因为不存在这种冲突)
-
信号怎么处理
SIGPIPE
服务器 Server 向已经关闭的 Client 继续发送数据(其实是发送两次才会触发),大部分信号都是默认结束整个程序。而服务器是需要永久挂着的,如果触发了SIGPIPE,程序是会停止的,我们得使用SIG_IGN。
客户端 使用信号处理函数通知服务器提前退出,紧接着客户端就可以退出了。
SIGINT,SIGTERM 都可以选择性SIG_IGN -
EPOLLRDHUP是什么?这是EPOLL中可以监听到的事件种类之一,代表对端的关闭。epoll_wait得到的结果集中,你可以通过这个标志位来监测客户端退出,关闭客户端连接。
-
应该设计哪些类?mysql设计哪些表?
好友表,消息表,账号表…并没有什么标准答案,最好想好先逻辑了再去写,不然很容易重构的。
传的PACK包最好带有个mysql中id字段(不过你发送给服务器不一定要填写这个字段),因为我当初没有加id字段(因为总感觉这对客户没有用)结果在服务器上处理业务逻辑每次都得根据账号去查询mysql下的id,再根据id去做更多操作,比如查询另一张含有这个id的表。直接代码量大了很多很多,也让程序的结构显得十分丑陋,可读性也会很差。
-
cjson / 自己写的结构体 /还是在msg中用分割符号得到更多的字段?
我觉得json可能会更好,因为我当初用的是分割符号,也就是在myshell中用来分割每一个小段,比如id:1\nname:adl\nclass:计科1904\n
通过一个自己写的函数去将这个片段根据分隔符’\n’分成三份,从而解析出我们需要的字段内容,然而我写的函数需要在函数外开辟大量的数组空间避免内容过长而导致的溢出,而且使用起来难以精确free。至于真要使用这种方法,分隔符请不要使用\n,因为这个过于常见,推荐使用"\r\n"。而如果使用结构体添加字段,你要知道,你发送的包一般是固定的,但你如果需要发送一些文件的大小,名称,你忽然发现你的结构体的字段不够用,添加结构体字段其实是一种下策(我就加了文件的字段),因为服务器和客户端都会需要因此多维护很多相应的信息。cjson呢你就可以比如想将一个 -
发文件如何让对方知道我发完了呢?
发送含END标志位的包,END包的处理在聊天室中用到挺多的,显示一个好友列表,群列表。。。 -
重复加好友,加群该怎么处理???群主和管理员该怎么同时处理这个请求???
这些重复需求大需要验证该消息已存在的操作。而群主和管理员只能让一个生效。 -
同时填写一个结构体的字段最好写成函数
-
聊天记录和其他类型的消息需要在mysql中存放多久?(消息的状态flag)
-
断点传输应该注意哪些事项?
服务器存客户端的文件的总大小和已发送大小,断开连接后,下一次客户连接,服务器可通知该客户,再可以选择性重发
(sys/sendfile.h sendfile函数大家可以试试)
上传给服务器的文件应该放在哪里,是直接发给对方吗?
目录 等到对端在线PUSH -
时间戳
这个其实挺重要的,这样每次你发送的消息都可显示时间,看上去炫酷多了! -
超时处理
仅仅提供我的想法:之前说服务器可以用一个全局链表来记录客户的在线状态,那么可以添加一个字段登录时间(或者超时时间),服务器当客户连接时记录下客户的登录时间,每循环一轮(或者更长一段就将超时的客户删去)
事实上可以采用时间升序链表的方式,也就是插入的时候将这个节点放在那些超时时间在它之前的节点的后面。那么你在链表节点的插入就有O(n)的复杂度,也是每隔一段时间比较链表上节点超时时间和当前时间,超时则删除客户。如果客户发送了请求,说明客户在线,那么就去更新超时时间。
大致就这个思路,当初没有实现,感兴趣的小伙伴可以试试。
写聊天室的思路 先构思整体逻辑 再登录注册入手 再实现私聊 再群聊(其实很多地方和私聊本质相同)
心态很容易爆炸的,一次次痛苦的重构 ,唉,打不死你的只会让你更强大!