对于初学者,网络聊天室是一个比较全面易上手的小项目,整体思路如下,本次是一个群聊实现思路与代码,后续加好友,私聊等功能有空再完善。
当某个客户端需要发送消息时,它将此消息发送给服务器,服务器再将此消息转发给各客户端,各客户端之间是无连接的,即相互之间不能直接通信。因此,在服务器中,有两个子线程,一个是用来监听是否有客户端登录服务器,若有,建立与其连接的套接字,并存入在线客户序列里;另一个是接收转发线程,其阻塞等待是否有客户端发消息过来,若有,取出,并转发给所有在线用户,在这里用到了 select 函数实现多路选择,即对各个客户端的 socket 进行读监听。 在客户端也有两个子线程, 一个用来向服务器发送消息,另一个线程用来接收服务器发来的消息。
发送消息格式:分为系统消息和聊天消息两种。
1)系统消息:70个字节 前30个字节保存消息时间,后40个字节保存要发送的系统消息。
2)聊天消息:1024个字节 前20个字节保存客户姓名,中间30个字节保存消息时间,后面保存要发送的聊天消息。
为了区分发送的是系统消息还是聊天消息,消息的第一个字节用来存放消息类型:N代表系统消息,M代表聊天消息。
服务器端:
一、建立套接字绑定服务器的IP地址和端口号,并进行监听。
1)socket
2)实现socket重用。
int reuse = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *) & reuse, sizeof(int));
reus=1,操作系统会在服务器socket被关闭或服务器进程终止后马上释放该服务器的端口,否则操作系统会保留几分钟该端口。
3)bind 绑定服务器IP和端口号
4)listen 设置监听数量(聊天室支持的在线人数)
二、创建线程循环等待客户端的连接。
1)accept 阻塞等待客户端连接。
2)客户端连接后,返回值得到客户端对应的套接字描述符,保存到在线客户序列(数组)中。
3)向聊天室中所有的客户通知新用户上线(系统消息),需要知道新用户的用户名,所以之前需要先等待接收客户端用户名(客户端连接成功后先发送用户名)。
4)创建线程循环接收客户端的消息并转发给所有其它在线客户端,同时保存聊天记录。(追加模式打开或则创建聊天记录文件)
具体实现:
1、用select函数实现I/O多路转换,检测在线客户序列中的客户对应套接字。
2、检测到有消息可读时,读取消息,转发给所有的其他在线客户端,并保存聊天记录,如果读取到的消息部分为”quit”,从在线客户序列中删除对应客户。
三、等待线程退出,关闭套接字,关闭聊天记录文件。(有bug,因为线程内是死循环,没有结束的条件,也没有捕捉信号)
客户端:
一、创建套接字并且连接服务器。
1)socket
2)connect
3)连接成功后先输入用户名(全局变量保存)并且发送给服务器。
4)服务器会发送欢迎新用户提示(系统消息),表示能正常通信,以用户名为文件名创建或则打开一个保存历史记录的文件,用于保存用户历史记录,打印并保存系统消息。
2、创建两个线程用于发送和接收消息。
1)接收线程:循环接收:对接收的消息进行判断,如果不是自己发送的消息,显示并且保存,如果是自己发送的消息,只保存不显示。最后打印当前时间和输入提示符。(可以用> :