基于UDP的网络聊天室

news/2024/10/25 9:26:07/

客户端

#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.ppmy.cn/news/1249888.html

相关文章

如何替换docker容器中的redis持久化文件dump.rdb

一、停止redis容器 docker stop <container_id_or_name>二、将新的 dump.rdb 上传到任意目录&#xff0c;如/root目录 三、将新的 dump.rdb 文件复制到容器中的 Redis 数据目录 docker cp /root/dump.rdb <container_id_or_name>:/data/dump.rdb四、重新启动red…

从裸机启动开始运行一个C++程序(十六)

前序文章请看&#xff1a; 从裸机启动开始运行一个C程序&#xff08;十五&#xff09; 从裸机启动开始运行一个C程序&#xff08;十四&#xff09; 从裸机启动开始运行一个C程序&#xff08;十三&#xff09; 从裸机启动开始运行一个C程序&#xff08;十二&#xff09; 从裸机启…

智能化质量控制,三坐标尺寸SPC管理系统引领制造新潮流!

在制造业的浪潮中&#xff0c;提升产品质量一直是企业不懈追求的目标。为了更好地适应市场需求、提高生产效益&#xff0c;我们自豪地介绍全新的三坐标尺寸SPC&#xff08;统计过程控制&#xff09;管理系统&#xff0c;为您的企业带来智能、高效的质量管理体验&#xff0c;助力…

ArkUI框架中的PersistentStorage和Environment深入详解【鸿蒙专栏-13】

文章目录 ArkUI框架中的PersistentStorage和EnvironmentPersistentStorage:保持状态的长存储概述限制条件使用场景初始化和访问属性示例流程图注意事项Environment:设备环境查询使用场景从UI中访问Environment参数应用逻辑使用Environment深入ArkUI框架的持久化存储与设备环境…

动态规划:完全背包问题-二维数组和一维滚动数组解法

题目描述 小明是一位科学家&#xff0c;他需要参加一场重要的国际科学大会&#xff0c;以展示自己的最新研究成果。他需要带一些研究材料&#xff0c;但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等&#xff0c;它们各自占据不同的空间&#xff0…

【上海大学数字逻辑实验报告】二、组合电路(一)

一、 实验目的 熟悉TTL异或门构成逻辑电路的基本方式&#xff1b;熟悉组合电路的分析方法&#xff0c;测试组合逻辑电路的功能&#xff1b;掌握构造半加器和全加器的逻辑测试&#xff1b;学习使用可编程逻辑器件的开发工具 Quartus II设计电路。 二、 实验原理 异或门是数字…

【springboot】idea项目启动端口被占用

问题 idea本地启动springboot项目端口老是被占用 解决 关闭被占用的端口进程 步骤: 1. winR打开程序框 2. 查出被占用端口的进程id netstat -ano | finderstr 端口号 例如 netstat -ano | finderstr 81013.杀死进程 taskkill /pid 进程id -t -f 例如 taskkill /pid 2…

vue2+element-ui npm run build打包后,在服务器打开报错

报错 页面的图标也显示不出来&#xff0c;如下 解决&#xff1a; 在build->utils.js文件里面加上publicPath: ../../&#xff0c;再打包发布一下就可以了 // Extract CSS when that option is specified// (which is the case during production build)if (options.extrac…