当前位置:网站首页>基于udp通信的在线多人聊天室
基于udp通信的在线多人聊天室
2022-07-29 05:25:00 【l_You_K】
1、项目要求
(1)用户可以登录聊天室。
(2)在线用户可以接收到其他用户的上下线通知,和在线消息
(3)服务器可以发送系统消息
2、流程图
3、基本实现思路
(1)登陆操作,较为简陋,没有进行数据库校验,当用户选择登陆操作,客户端将发送包含登录操作码的数据包给服务器,服务器将登录用户信息插入链表,然后再发送给所有在线用户。
客户端:
int logoin(int sockfd, struct sockaddr_in serveraddr, socklen_t serveraddr_lent)
{
msg_t msg;
msg.flag = 1;
puts("请输入你的名字:");
memset(msg.namebuff, sizeof(msg.namebuff), 0);
fgets(msg.namebuff, sizeof(msg.namebuff), stdin);
msg.namebuff[strlen(msg.namebuff) - 1] = '\0';
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, serveraddr_lent);
服务器:
int logoin(int sockfd, struct sockaddr_in cilentaddr, LNode **L, msg_t msg)
{
if (L == NULL)
{
printf("input error\n");
return -1;
}
char buff[512] = {0};
if (*L == NULL)
{
insert_data_head(L, cilentaddr);
}
else
{
LNode *p = *L;
//先接受网络结构体 发给链表中所有人消息 再插入进去
while (p)
{
if (p->cilentaddr.sin_port != cilentaddr.sin_port)
{
snprintf(buff, sizeof(buff), "%s进入聊天室", msg.namebuff);
sendto(sockfd, buff, sizeof(buff), 0, (struct sockaddr *)&(p->cilentaddr), sizeof(p->cilentaddr));
}
p = p->Next;
}
insert_data_head(L, cilentaddr);
}
memset(buff, sizeof(buff), 0);
//先接受网络结构体 发给链表中所有人消息 再插入进去
snprintf(buff, sizeof(buff), "您已登陆成功");
sendto(sockfd, buff, sizeof(buff), 0, (struct sockaddr *)&cilentaddr, sizeof(cilentaddr));
}
(2)群聊操作 当用户进入聊天室 每次从终端键入发送信息,都会打包不同操作码一同发送给服务器,服务器在接收到数据包时便利所有在线的用户链表并发送该用户的群聊消息。
客户端:
while (1)
{
msg.flag = 2;
//printf("input->>");
memset(msg.buff, sizeof(msg.buff), 0);
fgets(msg.buff, sizeof(msg.buff), stdin);
msg.buff[strlen(msg.buff) - 1] = '\0';
if (strncmp(msg.buff, "quit", 5) == 0)
{
msg.flag=-1;
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, serveraddr_lent);
break;
}
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, serveraddr_lent);
}
system("read -p '您已退出聊天室,请按任意键继续....' var");
system("clear");
服务器:
int send_msg(int sockfd, struct sockaddr_in cilentaddr, LNode *L, msg_t msg)
{
LNode *p = L;
char buff[512] = {0};
while (p)
{
if (p->cilentaddr.sin_port == cilentaddr.sin_port)
{
if (msg.flag != -1)
{
snprintf(buff, sizeof(buff), "[you say]>>%s", msg.buff);
sendto(sockfd, buff, sizeof(buff), 0, (struct sockaddr *)&(p->cilentaddr), sizeof(p->cilentaddr));
}else if(msg.flag==-1){
p->flag=-1;
}
}
else
{
if (p->flag!= -1)
{
if(strncmp(msg.buff,"quit",5)==0){
snprintf(buff, sizeof(buff), "[%s]退出聊天室", msg.namebuff);
}else{
snprintf(buff, sizeof(buff), "[%s]say>>%s", msg.namebuff, msg.buff);
}
sendto(sockfd, buff, sizeof(buff), 0, (struct sockaddr *)&(p->cilentaddr), sizeof(p->cilentaddr));
}
}
p = p->Next;
}
}
(3)退出群聊 当用户执行退出群聊操作,会发给服务器一个特殊的数据包,将存储用户信息的结构体设置为不可读的标志,即可实现退出聊天室接收不到消息,再次进入还可接受消息的需求。
客户端在登陆操作函数中已实现。
服务器当接收到特殊操作码会发送消息给其他在线用户。
(4)退出系统 (退出聊天软件)用户在推出软件时发送给服务器一个特殊的包,服务器将保存该用户信息的结构体从链表中删除。防止内存泄漏。
客户端:
void exit_(int sockfd, struct sockaddr_in serveraddr, socklen_t serveraddr_lent)
{
msg_t msg;
msg.flag=0;
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, serveraddr_lent);
puts("欢迎下次使用!");
system("read -p '请按任意键继续....' var");
system("clear");
exit(0);
}
服务器:
int quit_msg(int sockfd, struct sockaddr_in cilentaddr, LNode **L, msg_t msg)
{
//把消息都发过去
//还要把链表删掉
LNode *p = *L;
char buff[512] = {0};
if (p->Next == NULL)
{
free(p);
*L = NULL;
p = *L;
// return 0;
}
while (p)
{
if ((*L)->cilentaddr.sin_port == cilentaddr.sin_port)
{
//删除p
(*L) = (*L)->Next;
free(p);
//重新定向p
p = *L;
break;
}
if (p->Next->cilentaddr.sin_port == cilentaddr.sin_port)
{
// puts("3");
LNode *s = p->Next;
p->Next = s->Next;
free(s);
break;
}
p = p->Next;
}
}
(5)系统消息发送给所有用户,如系统维护,服务器奔溃等等。由于udp同通信有一个阻塞函数recvfrom与发送给所有用户系统消息不可同时处理,这时可以考虑多进程,多线程,io多路复用实现并行处理,为了方便实现,我们采取多进程实现。
4、缺点与不足
此次小练,最大的缺点是没有对客户端断连采取更为健壮的处理,应该再客户端捕获一下客户端因为ctrl+c而结束的信号,在捕获之后发送数据包给服务器,让服务器对客户端退出行为,做出删除保存客户信息结构体的行为。还有就是没有添加数据库的使用,有兴趣的小伙伴可以拿来练手试试。我主要是想练习一下链表的操作加深一下对指针的理解。
欢迎个位看官、前辈指正。
如需源代码可以联系我。
边栏推荐
- 官方教程 Redshift 08 Light
- leetcode---技巧
- 操作系统面试题
- [beauty of software engineering - column notes] 17 | what is the need analysis? How to analyze?
- [leetcode skimming] array 1 - double pointer
- 虹科案例 | PAC:一种整合了softPLC控制逻辑、HMI和其他服务功能的集成控制解决方案
- Leetcode 9. palindromes
- Single chain surface test questions
- SQL Developer图形化窗口创建数据库(表空间和用户)
- LeetCode #26.删除有序数组中的重复项
猜你喜欢
随机推荐
位运算学习笔记
【Leetcode刷题】数组1——双指针
Personal views on time complexity
官方教程 Redshift 05 system参数详细解释
Encapsulation - Super keyword
[beauty of software engineering - column notes] 16 | how to write project documents?
虹科案例 | PAC:一种整合了softPLC控制逻辑、HMI和其他服务功能的集成控制解决方案
Unity初学1——角色移动控制(2d)
[leetcode skimming] array 1 - double pointer
LeetCode #26.删除有序数组中的重复项
Official tutorial redshift 06 opt parameters
Add time series index to two-dimensional table
Official tutorial redshift 08 light
官方教程 Redshift 03 各种GI的参数和常规使用说明
LeetCode #35.搜索插入位置
Unity初学2——瓦片的制作以及世界的交互(2d)
Leetcode 19. delete the penultimate node of the linked list
计算机大厂面试题
Sliding window leetcode 76. minimum covering substring (hard) 76.76. minimumwindow substring (hard)
Ue5 texture system explanation and common problem setting and Solutions