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而结束的信号,在捕获之后发送数据包给服务器,让服务器对客户端退出行为,做出删除保存客户信息结构体的行为。还有就是没有添加数据库的使用,有兴趣的小伙伴可以拿来练手试试。我主要是想练习一下链表的操作加深一下对指针的理解。
欢迎个位看官、前辈指正。
如需源代码可以联系我。
文章浏览阅读965次,点赞18次,收藏21次。现在我们有了 2021 年和 2023 年的 NDVI 数据帧,我们需要从 2021 年的值中减去 2023 年的值以捕获 NDVI 的差异。该数据集包括像素级别的植被值,我们将编写一个自定义函数来根据红色和绿色波段的表面反射率计算 NDVI。在我的上一篇文章中,我演示了如何将单个多边形分割/镶嵌为一组大小均匀的六边形。现在我们有了植被损失数据,让我们使用 Kepler.gl 可视化每个六边形的植被损失。将地图保存为 HTML 文件,在浏览器中打开 HTML 以获得更好的视图。现在我们将调用该函数并使用、
文章浏览阅读3.3k次,点赞6次,收藏5次。正态分布,又称高斯分布或钟形曲线,是统计学中最为重要和常用的分布之一。_echarts正态分布图
文章浏览阅读217次。首先要在Mainfest.xml中加入所需要的权限:[html] view plain copyprint?uses-permission android:name="android.permission.SEND_SMS"/> uses-permission android:name="android.permission.READ_SMS"/> _android bundle.get("pdus");
文章浏览阅读2.6k次。0、说明最近在学习 Data Assimilation Research Testbed (DART) 相关内容,其软件是在 Unix/Linux 操作系统下编译和运行的 ,由于我的电脑是 Windows 10 的,DART 推荐可以使用 Windows Subsystem For Linux (WSL) 来创建一个 Windows 下的 Linux 子系统。以下的内容主要介绍如何安装 WSL2,以及 WSL2 的联网。1、如何在 Windows 10 下安装WSL具体的安装流程可以在 microso_wsl2 联网
文章浏览阅读1k次。DB_LINK 介绍在本机数据库orcl上创建了一个prod_link的publicdblink(使用远程主机的scott用户连接),则用sqlplus连接到本机数据库,执行select * from scott.emp@prod_link即可以将远程数据库上的scott用户下的emp表中的数据获取到。也可以在本地建一个同义词来指向scott.emp@prod_link,这样取值就方便多了..._添加 database link重复的数据库链接命
文章浏览阅读3.1k次。ylbtech-云-腾讯云-实时音视频:实时音视频(TRTC)支持跨终端、全平台之间互通,从零开始快速搭建实时音视频通信平台1.返回顶部 1、腾讯实时音视频(Tencent Real-Time Communication,TRTC)拥有QQ十几年来在音视频技术上的积累,致力于帮助企业快速搭建低成本、高品质音视频通讯能力的完整解决方案。..._腾讯实时音视频 分享链接
文章浏览阅读534次,点赞10次,收藏8次。编写一个完整的日历表需要处理许多细节,包括公历和农历之间的转换、节气、闰年等。运行程序后,会输出指定年份的日历表。注意,这个程序只是一个简单的示例,还有很多可以改进和扩展的地方,例如添加节气、节日等。_农历库c语言
文章浏览阅读1w次,点赞28次,收藏27次。FL Studio21.1.1.3750中文破解版是最优秀、最繁荣的数字音频工作站 (DAW) 之一,日新月异。它是一款录音机和编辑器,可让您不惜一切代价制作精美的音乐作品并保存精彩的活动画廊。为方便用户,FL Studio 21提供三种不同的版本——Fruity 版、Producer 版和签名版。所有这些版本都是独一无二的,同样具有竞争力。用户可以根据自己的需要选择其中任何一种。FL Studio21.1.1.3750中文版可以说是一站式综合音乐制作单位,可以让您录制、作曲、混音和编辑音乐。_fl studio 21 注册机
文章浏览阅读1.3k次。冯诺依曼计算机工作原理冯 诺依曼计算机工作原理的核心是 和 程序控制世界上不同型号的计算机,就其工作原理而言,一般都是认为冯 诺依曼提出了什么原理冯 诺依曼原理中,计算机硬件系统由那五大部分组成的 急急急急急急急急急急急急急急急急急急急急急急冯诺依曼结构计算机工作原理的核心冯诺依曼结构和现代计算机结构模型 转载重学计算机组成原理 一 冯 诺依曼体系结构从冯.诺依曼的存储程序工作原理及计算机的组成来..._简述冯诺依曼计算机结构及工作原理
文章浏览阅读559次。这次在随机乱下的基础上加上了一些简单的处理,如进营、炸棋、吃子等功能,在和敌方棋子产生碰撞之后会获取敌方棋子大小的一些信息,目前采用的是事件驱动模型,当下完一步棋界面返回结果后会判断是否触发了相关事件,有事件发生则处理相关事件,没有事件发生则仍然是随机下棋。1.事件驱动模型首先定义一个各种事件的枚举变量,目前的事件有工兵吃子,摸暗棋,进营,明确吃子,炸棋。定义如下:enum MoveE..._军棋引擎
文章浏览阅读85次。1, 模板观念与函数模板简单模板: template< typename T > T Function( T a, T b) {… }类模板: template struct Object{……….}; 函数模板 template< class T> inline T Function( T a, T b){……} 不可以使用不同型别的..._geekband 讲义
文章浏览阅读158次。"^\d+$" //非负整数(正整数 + 0)"^[0-9]*[1-9][0-9]*$" //正整数"^((-\d+)|(0+))$" //非正整数(负整数 + 0)"^-[0-9]*[1-9][0-9]*$" //负整数"^-?\d+$" //整数"^\d+(\.\d+)?$" //非负浮点数(正浮点数 + 0)"^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0..._vb.net 正则表达式 取html中的herf