数据结构设计

聊天服务器,有一个服务端和多个客户端。因此,有一个表示聊天服务器的结构chatState,其内维护着每一个客户端的数据,对应结构为client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* This structure represents a connected client. There is very little
* info about it: the socket descriptor and the nick name, if set, otherwise
* the first byte of the nickname is set to 0 if not set.
* The client can set its nickname with /nick <nickname> command. */
struct client {
int fd; // Client socket.
char* nick; // Nickname of the client. 客户端名称,默认为 \0
};

/* This global structure encapsulates the global state of the chat. */
struct chatState {
int serversock; // Listening server socket.
int numclients; // Number of connected clients right now.
int maxclient; // The greatest 'clients' slot populated. 最大的 clients fd 值,用于 select()的第一个参数
struct client* clients[MAX_CLIENTS]; // 以 client's fd 作为 clients 槽位索引
};
数据结构设计

服务端

整体流程

首先,定义一个 chatState 类型的全局聊天服务变量 Chat,并申请对应的内存资源,包含指向 MAX_CLIENTS 个客户端的指针数组(当每有客户端连接到服务器时,调用createClient 创建对应的客户端),并初始化各个成员、创建一个服务端的监听 socket(指向 serversock 成员)。

然后,在无限循环的每次循环开始时,将监听 socket 和已连接 socket 添加到可读事件集合中、并进行监听。

最后,当有监听事件就绪时,根据就绪事件的类别(监听 socket、已连接 socket)进行各自的业务处理(接收客户端连接、修改昵称、广播消息)。

服务端整体流程