今天的主题是在 Linux 上实现一个群聊功能,支持群聊,指定人私聊,群主禁言,踢出群聊的功能,实际上要实现这个功能,如果你阅读过我前两天我写一篇 Linux原始系统api实现两个终端实时聊天 ,那么,在以上的基础上其实就是追加一下 两个功能即可,及群主禁言,和将谁踢出群聊的功能,因为群聊的基本功能我们实现了,而且私信的逻辑我们也实现了,ps,文本的代码在此。
先回顾一下上篇文章的内容,我们实现的第一个版本,实际上就是一个大的群聊功能,包含服务端和客户端两部分,具体的交互如下:
// Accept clients
while (1) {
socklen_t clilen = sizeof(cli_addr);
connfd = accept(listenfd, (struct sockaddr*)&cli_addr, &clilen);
// Check if max clients is reached
if ((cli_count + 1) == MAX_CLIENTS) {
printf("Max clients reached. Rejected: ");
print_client_addr(cli_addr);
printf(":%d\\n", cli_addr.sin_port);
close(connfd);
continue;
}
// Client settings
client_t *cli = (client_t *)malloc(sizeof(client_t));
cli->address = cli_addr;
cli->sockfd = connfd;
cli->uid = uid++;
// Add client to the queue and fork thread
queue_add(cli);
pthread_create(&tid, NULL, &handle_client, (void*)cli);
// Reduce CPU usage
sleep(1);
}
void send_message(char *s, int uid) {
pthread_mutex_lock(&clients_mutex);
for (int i = 0; i < MAX_CLIENTS; ++i) {
if (clients[i]) {
if (clients[i]->uid != uid) {
if (write(clients[i]->sockfd, s, strlen(s)) < 0) {
perror("ERROR: write to descriptor failed");
break;
}
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
我们是一个中心化的聊天版本,也就是 A 客户端发送的消息先会到服务器,服务器在进行转发,群聊就是将 A 发送的消息转发给到其他连接到这个服务器的其他所有人,所谓的发起一个私信,即这个发送的消息是不能被转发给到所有其他人,那你就需要和服务器约定消息格式了:
我们通过这样的方式来实现私密消息功能:
/msg <recipient_name> <message>
。/msg
开头时,它会查找指定的接收者,并只将消息发送给那个特定的客户端。这是我们服务端处理私信逻辑的部分
// msg 是接收到的完整消息,sender 是发送者的名字。
void handle_private_message(char *msg, char *sender) {
// 解析消息格式,提取接收者的名字和实际的私密消息
char recipient[32];
char private_msg[LENGTH];
if (sscanf(msg, "/msg %s %[^\\n]", recipient, private_msg) == 2) {
// 查找接收者的客户端结构体
Client *client = find_client_by_name(recipient);
if (client) {
// 发送私密消息给接收者
char buffer[LENGTH + 32];
sprintf(buffer, "[Private]%s: %s\\n", sender, private_msg);
send(client->sockfd, buffer, strlen(buffer), 0);
} else {
// 接收者不存在,发送错误消息给发送者
char buffer[LENGTH];
sprintf(buffer, "User %s does not exist.\\n", recipient);
send(sender_sockfd, buffer, strlen(buffer), 0);
}
}
}
私信的效果是:
A 发送一条给到 B 的私信,只有 B 可以收到,C 是收不到的
只有 B 收到的截图
C 是收不到的
要实现禁言的功能,我们的思考是,如何能够让用户发送的消息不会被其他群聊的人看到,所以,最为直观的实现逻辑就是服务端丢弃被禁言的用户发送过来的消息,因此,我们需要在 client_c 结构中标记下哪个 client 被 mute 了,然后修改一下 send_message 逻辑即可,当发现这个 client 是被 mute 了的,就不转发他的消息了,禁言和踢出用户的整体逻辑图如下:
我们先实现对用户禁言的 部分,解除禁言就不贴了,将to_be_unmute->mute = 0; 就 ok 了
else if(strncmp(s, "/mute ", 8) == 0 && uid == 0){ //uid 为 0 的是群主
char unmute_user[32];
char *message;
char buffer[BUFFER_SIZE + 32];
// 解析消息
message = s + 8; // 跳过 "/unmute "
sscanf(message, "%s", unmute_user);
//unmute client
client_t * to_be_unmute = find_client_by_mute_name(unmute_user);
if (to_be_unmute) {
to_be_unmute->mute = 1;
}
return;
}
// 省略...
// Send message to all clients ,if client is not muted
client_t * client = find_client_by_uid(uid);
if(client->mute == 1){
// 告知 客户端他已经被屏蔽了
char buffer[BUFFER_SIZE];
sprintf(buffer, "Your message has been blocked.\\\\n");
write(client->sockfd, buffer, strlen(buffer));
pthread_mutex_unlock(&clients_mutex);
return;
}
我们看看屏蔽一个用户的效果, A作为第一个用户,加入群聊,是群主,后面 C 加入了,A 发送了屏蔽指令将其屏蔽
然后看看 C 发送消息,发现他被屏蔽了
B 不会看到他发送的消息,ps 这里我们没有屏蔽私信,所以 C 是可以给 B 发送私信的。
2.实现踢出的逻辑
这里的实现方式和实现屏蔽略微不同,而且还稍显简单,直接干掉 server 和 client 的连接即可,实现的方式如下:
else if (strncmp(s, "/kick ", 6) == 0 && uid == 0) {
char kick_user[32];
char *message;
char buffer[BUFFER_SIZE + 32];
// 解析消息
message = s + 6; // 跳过 "/kick "
sscanf(message, "%s", kick_user);
//kick client
client_t * to_be_kick = find_client_by_mute_name(kick_user);
if (to_be_kick) {
//断开与该客户端的 socket 连接,哈哈,比较暴力哈,踢出群聊
close(to_be_kick->sockfd);
remove_client(to_be_kick->uid); // 从客户端列表中移除
}
}
我们来验证下效果,A,B,C 先后加入群聊,A 是群主,A 踢掉 C,然后在发送一条群消息
我们看到 C 这里收不到消息,B 是可以收到的,这说明 C 已经被踢掉了。
今天的内容,基于上一版的群聊+简单的私信的版本的基础上只另外实现了 屏蔽用户 和 踢下线的功能,功能都非常简单,大家不妨思考一下,基于这个版本的的基础上,我们还可以做哪些功能呢?我能想到的:
虽然这是一个极其简单的 demo,但是,如果深入思考,能做的优化是在是太多了,优化本身就是一个不断追求的过程。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。