基于UDP的网络聊天室

客户端

#include <myhead.h>
//定义存储信息结构体
typedef struct _MSG
{char code;  //操作码:'L'表示登录'C表示群聊'S'表示系统消息'S'表示退出char name[128];  char txt[256];}msg_t;//定义保存客户端网络信息的链表
typedef struct _ADDR
{struct sockaddr_in cin;struct _ADDR* next;
}addrlist_t;//登录操作的函数
void do_login(int sfd,msg_t msg,addrlist_t*addr,struct sockaddr_in cin)
{//先遍历链表 将新用户加入群聊的消息发送给所有人addrlist_t* tmp = addr;  //记录链表头结点while(tmp->next != NULL){tmp = tmp->next;if(sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&(tmp->cin),sizeof(tmp->cin)) == -1){perror("sendto error");return;}}//将新用户的网络信息结构体头插入链表addrlist_t* pnew = NULL;if(NULL == (pnew = (addrlist_t*)malloc(sizeof(addrlist_t)))){printf("malloc error\n");return;}pnew->cin = cin;pnew->next = addr->next;addr->next = pnew;printf("%s已上线\n",msg.name);return;
}//群聊操作函数
void do_chat(int sfd,msg_t msg,addrlist_t*addr,struct sockaddr_in cin)
{//遍历链表,将群聊消息发给除自己以外其他人addrlist_t* ptmp = addr;while(ptmp->next != NULL){ptmp = ptmp->next;if(memcmp(&cin, &(ptmp->cin), sizeof(cin))){//说明不是自己就发送数据if(sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&(ptmp->cin),sizeof(ptmp->cin)) == -1){perror("sendto error");return;}}}return;
}//退出操作的函数
void do_quit(int sfd,msg_t msg,addrlist_t*addr,struct sockaddr_in cin)
{//遍历链表 是自己就将自己在链表中删除,不是自己就发送退出群聊的数据addrlist_t* ptmp = addr;addrlist_t* del = NULL;while(ptmp->next != NULL){if(memcmp(&(ptmp->next->cin), &cin, sizeof(cin))){//不是自己ptmp = ptmp->next;if((sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&(ptmp->cin),sizeof(ptmp->cin))) == -1){perror("sendto error");return;}}else{del = ptmp->next;ptmp->next = del->next;free(del);del=NULL;}}printf("%s已下线\n",msg.name);return;
}
int main(int argc, const char *argv[])
{if(argc != 3){   //输入ip地址及端口号,进行判断printf("input error\n");printf("usage: %s <IP> <PORT>\n",argv[0]);return -1;}//定义用于接收等待套接字int sfd;if((sfd = socket(AF_INET,SOCK_DGRAM,0)) == -1){perror("socket error");return -1;}printf("socket sfd success\n");//设置端口号快速重用int reuse = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1){perror("setsockopt error");return -1;}printf("设置端口号快速重用_%d_%s_%s_\n",__LINE__,__FILE__,__func__);//绑定(填充服务器信息结构体)struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port   = htons(atoi(argv[2]));sin.sin_addr.s_addr = inet_addr(argv[1]);socklen_t seraddr_len = sizeof(sin);if((bind(sfd, (struct sockaddr*)&sin, seraddr_len)) == -1){perror("bind error");return -1;}printf("bind success\n");//定义客户端网络信息结构体struct sockaddr_in cin;socklen_t cliaddr_len = sizeof(cin);msg_t msg;  //定义接收信息的变量msgpid_t pid;  //进程号pid = fork();  //创建多进程if(pid < 0){perror("fork error");return -1;}else if(pid == 0){   //子进程,用来收发数据//创建保存客户端信息的链表头结点addrlist_t* addr;if(NULL == (addr = (addrlist_t*)malloc(sizeof(addrlist_t)))){printf("malloc error\n");return -1;}memset(addr, 0, sizeof(addr));addr->next = NULL;while(1){  //循环收发数据memset(&msg,0,sizeof(msg));  //每次接收新用户数据清空memset(&cin,0,sizeof(cin)); //接收客户端发送的消息,存放在msg中if((recvfrom(sfd, &msg,sizeof(msg), 0,(struct sockaddr*)&cin, &cliaddr_len)) == -1){perror("recvfrom error\n");return -1;}switch(msg.code){  //判断消息中的操作码,根据操作码执行对应操作case 'L':   //登录操作do_login(sfd,msg,addr,cin);break;case 'C':   //群聊操作do_chat(sfd,msg,addr,cin);break;case 'Q':   //退出操作do_quit(sfd,msg,addr,cin);break;}}}else{//父进程,用来发送系统消息//向子进程发送群聊消息strcpy(msg.name, "系统消息");msg.code = 'C';while(1){memset(msg.txt, 0,sizeof(msg.txt));fgets(msg.txt, 256,stdin);  //终端获取接收消息msg.txt[strlen(msg.txt)-1] = '\0';if((sendto(sfd,&msg,sizeof(msg),0 ,(struct sockaddr*)&sin,seraddr_len)) == -1){perror("sendto error");return -1;}}}close(sfd);return 0;
}

服务器

#include <myhead.h>
typedef struct _MSG
{char code; //操作码:'L'表示登录'C'表示群聊'S'表示系统内存出错'S'表示系统消息char name[128];char txt[256];
}msg_t;   //定义消息结构体类型int main(int argc, const char *argv[])
{if(3 != argc){  //考虑用命令行传参方式输入ip地址及端口号,先进行判断printf("input error!\n");printf("usage:%s <IP> <PORT>\n", argv[0]);return -1;}//定义通信的套接字int sfd = socket(AF_INET, SOCK_DGRAM, 0);if(-1 == sfd){perror("sockfd error");return -1;}//定义服务器地址信息结构体struct sockaddr_in sin;memset(&sin, 0,sizeof(sin));sin.sin_family = AF_INET;sin.sin_port   = htons(atoi(argv[2]));sin.sin_addr.s_addr = inet_addr(argv[1]);socklen_t seraddr_len = sizeof(sin);msg_t msg;memset(&msg,0,sizeof(msg));//输入用户名printf("请输入用户名:");fgets(msg.name,45,stdin);msg.name[strlen(msg.name)-1] = '\0';msg.code = 'L';strcpy(msg.txt,"加入群聊");//给服务器发送登录信息if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,seraddr_len) == -1){perror("sendto error");return -1;}//定义父子进程并创建pid_t pid = 0;pid = fork();if(pid < 0){printf("fork error\n");return -1;}else if(pid == 0){  //子进程,循环接收并打印接收的数据while(1){if(recvfrom(sfd,&msg,sizeof(msg),0,NULL,NULL) == -1){perror("sendto error");return -1;}//打印收到的数据printf("[%s]:%s\n",msg.name, msg.txt);}}else{   //父进程循环接收终端数据并发送给客户端while(1){memset(msg.txt,0,sizeof(msg.txt));fgets(msg.txt,128,stdin);  //终端获取聊天消息msg.txt[strlen(msg.txt)-1] = '\0';if(strcmp(msg.txt, "quit") == 0){msg.code = 'Q';strcpy(msg.txt, "退出群聊");}else{msg.code = 'C';}if(sendto(sfd,&msg,sizeof(msg), 0,(struct sockaddr*)&sin,seraddr_len) == -1){perror("sendto error");return -1;}if(strcmp(msg.txt, "退出群聊") == 0){break;}}//杀死子进程kill(pid,SIGKILL);wait(NULL);  //等待回收子进程资源}close(sfd);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/205604.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

玄学调参实践篇 | 深度学习模型 + 预训练模型 + 大模型LLM

&#x1f60d; 这篇主要简单记录一些调参实践&#xff0c;无聊时会不定期更新~ 文章目录 0、学习率与batch_size判断1、Epoch数判断2、判断模型架构是否有问题3、大模型 - 计算量、模型、和数据大小的关系4、大模型调参相关论文经验总结5、训练时模型的保存 0、学习率与batch_s…

Spring不再支持Java8了

在今天新建模块的时候发现了没有java8的选项了&#xff0c;结果一查发现在11月24日&#xff0c;Spring不再支持8了&#xff0c;这可怎么办呢&#xff1f;我们可以设置来源为阿里云https://start.aliyun.com/ 。 java8没了 设置URL为阿里云的地址

c++——string字符串____迭代器.范围for.修改遍历容量操作

在成为大人的路上喘口气. 目录 &#x1f393;标准库类型string &#x1f393;定义和初始化string对象 &#x1f4bb;string类对象的常见构造 &#x1f4bb;string类对象的不常见构造 &#x1f4bb;读写string对象 &#x1f393; string类对象的修改操作 &#x1f4…

爬虫http代理有什么用处?怎么高效使用HTTP代理?

在进行网络爬虫工作时&#xff0c;我们有时会遇到一些限制&#xff0c;比如访问频率限制、IP被封等问题。这时&#xff0c;使用HTTP代理可以有效地解决这些问题&#xff0c;提高爬虫的工作效率。本文将介绍爬虫HTTP代理的用处以及如何高效地使用HTTP代理。 一、爬虫HTTP代理的用…

【数据结构】单链表---C语言版

【数据结构】单链表---C语言版 一、顺序表的缺陷二、链表的概念和结构1.概念&#xff1a; 三、链表的分类四、链表的实现1.头文件&#xff1a;SList.h2.链表函数&#xff1a;SList.c3.测试函数&#xff1a;test.c 五、链表应用OJ题1.移除链表元素&#xff08;1&#xff09;题目…

京东数据产品推荐-京东数据挖掘-京东平台2023年10月滑雪装备销售数据分析

如今&#xff0c;滑雪正成为新一代年轻人的新兴娱乐方式&#xff0c;借助北京冬奥会带来的发展机遇&#xff0c;我国冰雪经济已逐渐实现从小众竞技运动到大众时尚生活方式的升级。由此也带动滑雪相关生意的增长&#xff0c;从滑雪服靴到周边设备&#xff0c;样样都需要消费者掏…

微信小程序 scrollview 滚动到指定位置

在微信小程序中&#xff0c;实现 ScrollView 滚动到指定位置有多种方法&#xff0c;下面将介绍三种主要的实现方式。 一、使用scroll-top属性实现滚动 通过设置 scroll-view 组件的 scroll-top 属性&#xff0c;我们可以实现滚动到指定位置。以下是具体实现方式&#xff1a; …

基于STM32单片机的智能家居系统设计(论文+源码)

1.系统设计 基于STM32单片机的智能家居系统设计与实现的具体任务&#xff1a; &#xff08;1&#xff09;可以实现风扇、窗帘、空调、灯光的开关控制&#xff1b; &#xff08;2&#xff09;具有语音识别功能&#xff0c;可以通过语音控制家电&#xff1b; &#xff08;3&a…

Win中Redis部署与配置

1.下载msi版本 下载传送门 2.双击next-->next安装安装 3.密码配置以及开机自启 在配置文件中配置相应配置进行配置密码以及端口和ip port 6379指定 Redis 监听端口&#xff0c;默认端口为 6379&#xff0c;作者在自己的一篇博文中解释了为什么选用 6379 作为默认端口&…

计算机网络 一到二章 PPT 复习

啥币老师要隔段时间测试&#xff0c;我只能说坐胡狗吧旁边 第一章 这nm真的会考&#xff0c;我是绷不住的 这nm有五种&#xff0c;我一直以为只有三种 广播帧在后面的学习中经常遇到 虽然老师在上课的过程中并没有太过强调TCP/IP的连接和断开&#xff0c;但我必须强调一下&…

全新付费进群系统源码 完整版教程

首先准备域名和服务器 安装环境&#xff1a;Nginx1.18 MySQL 5.6 php7.2 安装扩展sg11 伪静态thikphp 后台域名/admin账号admin密码123456 代理域名/daili账号admin密码123456 一、环境配置 二、建站上传源代码解压 上传数据库配置数据库信息 三、登入管理后台 后台域名/ad…

基于yolov2深度学习网络的打电话行为检测系统matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1、YOLOv2网络原理 4.2、基于YOLOv2的打电话行为检测 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 .................................…

人工智能-优化算法之动量法

对于嘈杂的梯度&#xff0c;我们在选择学习率需要格外谨慎。 如果衰减速度太快&#xff0c;收敛就会停滞。 相反&#xff0c;如果太宽松&#xff0c;我们可能无法收敛到最优解。 泄漏平均值 小批量随机梯度下降作为加速计算的手段。 它也有很好的副作用&#xff0c;即平均梯度…

Java---抽象类讲解

文章目录 1. 抽象类概述2. 抽象类特点3. 抽象类的成员特点4. 抽象类猫狗应用 1. 抽象类概述 在Java中&#xff0c;一个没有方法体的方法应该定义为抽象方法&#xff1b;而类中如果有抽象方法&#xff0c;该类必须定义为抽象类。 2. 抽象类特点 1. 抽象类和抽象方法必须使用abst…

C语言进阶指南(14)(部分字符串库函数及其模拟实现)

欢迎来到博主的专栏——C语言进阶指南 博主id&#xff1a;reverie_ly 文章目录 1、strlen&#xff08;&#xff09;——字符串长度计算函数自定义strlen函数的实现 2、strcpy——字符串拷贝函数strcpy的模拟实现 3.strcat——字符串追加函数strcat的模拟实现 4、strcmp——字符…

【计算机毕业设计】nodejs+vue音乐播放器系统 微信小程序83g3s

本系统的设计与实现共包含12个表:分别是配置文件信息表&#xff0c;音乐列表评论表信息表&#xff0c;音乐论坛信息表&#xff0c;歌手介绍信息表&#xff0c;音乐资讯信息表&#xff0c;收藏表信息表&#xff0c;token表信息表&#xff0c;用户表信息表&#xff0c;音乐类型信…

selenium使用记录

本文记录python环境下使用selenium的一些步骤 Step1&#xff1a;安装并配置驱动 pip install selenium # 使用pip在对应python中安装selenium包为了让selenium能调用指定的浏览器&#xff0c;需要下载对应浏览器的驱动程序&#xff08;这里以edge为例子&#xff09; #Firefo…

DockerCompose修改某个服务的配置(添加或编辑端口号映射)后如何重启单个服务使其生效

场景 docker-compose入门以及部署SpringBootVueRedisMysql(前后端分离项目)以若依前后端分离版为例&#xff1a; docker-compose入门以及部署SpringBootVueRedisMysql(前后端分离项目)以若依前后端分离版为例_docker-compose部署java mysql redis-CSDN博客 上面讲了docker c…

centos7-docker安装与使用

文章目录 一、docker简介1.1docker应用场景1.2docker的优点1.2.1快速&#xff0c;一致地交付应用程序1.2.2响应式部署和扩展1.2.3在同一硬件上运行更多工作负载 1.2docker的架构 二、docker的安装2.1新系统的环境搭建2.1.1更换yum源 2.2安装docker与卸载2.2.1yum安装docker2.2.…

SpringBoot——Swagger2 接口规范

优质博文&#xff1a;IT-BLOG-CN 如今&#xff0c;REST和微服务已经有了很大的发展势头。但是&#xff0c;REST规范中并没有提供一种规范来编写我们的对外REST接口API文档。每个人都在用自己的方式记录api文档&#xff0c;因此没有一种标准规范能够让我们很容易的理解和使用该…