当前位置: 首页 > news >正文

9.11网编项目——UDP网络聊天

服务器端

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <25061head.h>
#define SER_IP "192.168.144.128"
#define SER_PORT 8888// 链表节点结构定义
typedef struct Node
{char usrName[30];           // 用户名struct sockaddr_in cin;     // 用户的地址信息struct Node *next;          // 指针域
}*linklist;// 消息结构定义
struct msgTyp
{char type;char usrName[30];char msgText[50];
};int main(int argc, const char *argv[])
{// 前期配置int sfd = socket(AF_INET, SOCK_DGRAM, 0);if(sfd == -1){ERR_MSG("socket error");return -1;}struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1){ERR_MSG("bind error");close(sfd);return -1;}printf("绑定成功\n");struct sockaddr_in cin;socklen_t addrlen = sizeof(cin);struct msgTyp recv_msg;  // 接收的信息// 创建头节点并初始化linklist head = (linklist)malloc(sizeof(struct Node));if(head == NULL){ERR_MSG("malloc error");close(sfd);return -1;}head->next = NULL;  // 初始化头节点pid_t pid=fork();if(pid>0){// 核心操作while(1){// 清空接收缓冲区memset(&recv_msg, 0, sizeof(recv_msg));// 接收消息ssize_t recv_len = recvfrom(sfd, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr*)&cin, &addrlen);if(recv_len == -1){ERR_MSG("recvfrom error");continue;}switch(recv_msg.type){case 'L':  // login登录printf("%s登录成功\n", recv_msg.usrName);// 创建新用户的节点并初始化linklist temp = (linklist)malloc(sizeof(struct Node));if(temp == NULL){ERR_MSG("malloc error");break;} //存储新用户结点的相关信息strcpy(temp->usrName, recv_msg.usrName);temp->cin = cin;//头插temp->next = head->next;head->next = temp;// 广播登录消息linklist s = head->next;char sbuf[128] = "";snprintf(sbuf, sizeof(sbuf)-1, "%s已上号!", recv_msg.usrName);strcpy(recv_msg.msgText,sbuf);//放入recv.msg中,发给其他人//recv_msg.msgText[sizeof(recv_msg.msgText)-1] = '\0';while(s != NULL){// 跳过发送者自己(用IP和端口区分,后面的广播都是这种方法排除自己)if (s->cin.sin_port == cin.sin_port && s->cin.sin_addr.s_addr == cin.sin_addr.s_addr){s = s->next;continue;}sendto(sfd, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr*)&(s->cin), sizeof(s->cin));s = s->next;}break;case 'C':  // chat聊天if(strcmp(recv_msg.usrName,"系统")!=0){printf("%s:chat成功\n", recv_msg.usrName);}char cbuf[128] = "";snprintf(cbuf, sizeof(cbuf)-1, "%s说:%s", recv_msg.usrName, recv_msg.msgText);strcpy(recv_msg.msgText, cbuf);//recv_msg.msgText[sizeof(recv_msg.msgText)-1] = '\0';s = head->next;//从第一个用户开始while(s != NULL){// 跳过发送者自己if (s->cin.sin_port == cin.sin_port && s->cin.sin_addr.s_addr == cin.sin_addr.s_addr){s = s->next;continue;}sendto(sfd, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr*)&(s->cin), sizeof(s->cin));s = s->next;}break;case 'Q':  // quit退出printf("%s退出聊天室\n", recv_msg.usrName);char qbuf[128] = "";snprintf(qbuf, sizeof(qbuf)-1, "%s退出聊天室", recv_msg.usrName);strcpy(recv_msg.msgText, qbuf);//recv_msg.msgText[sizeof(recv_msg.msgText)-1] = '\0';// 先广播退出消息s = head->next;while(s != NULL){// 跳过发送者自己if (s->cin.sin_port == cin.sin_port && s->cin.sin_addr.s_addr == cin.sin_addr.s_addr){s = s->next;continue;}sendto(sfd, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr*)&(s->cin), sizeof(s->cin));s = s->next;}           // 然后从链表中删除该用户节点linklist prev = head;linklist curr = head->next;int found = 0;//判断找没找到结点while(curr != NULL){if(strcmp(curr->usrName, recv_msg.usrName) == 0){//头删prev->next = curr->next;free(curr);curr=NULL;found = 1;printf("已从链表中移除用户: %s\n", recv_msg.usrName);break;}prev = curr;curr = curr->next;}if(found==0){printf("警告: 未在链表中找到用户 %s\n", recv_msg.usrName);}break;default:printf("发送格式错误\n");}}}//服务器广播系统信息else if(pid==0){struct msgTyp sys_msg;sys_msg.type='C';strcpy(sys_msg.usrName, "系统");while(1){bzero(sys_msg.msgText,50);fgets(sys_msg.msgText,50, stdin);sys_msg.msgText[strlen(sys_msg.msgText)-1] = 0;//核心操作,因为是进程的原因,链表在子进程用不了,不能循环广播//那我直接向主进程发消息,让主进程广播sendto(sfd, &sys_msg, sizeof(sys_msg), 0, (struct sockaddr*)&sin, sizeof(sin));}printf("系统消息发送成功");}close(sfd);return 0;
}

客户端

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <25061head.h>
#define SER_IP "192.168.144.128"
#define SER_PORT 8888struct msgTyp
{char type;char usrName[30];char msgText[50];
};int cfd;                  // 客户端socket
struct sockaddr_in sin;   // 服务器的相关配置
char usrName[30] = "";   // 用户名
//虽然是线程,但有多个阻塞函数,所以用多线程
//该线程主要功能是接收消息
void *recv_msg_thread(void *arg)
{struct msgTyp recv_msg;socklen_t addrlen = sizeof(sin);while(1){// 清空接收缓冲区memset(&recv_msg, 0, sizeof(recv_msg));// 接收服务器发送的消息ssize_t recv_len = recvfrom(cfd, &recv_msg,sizeof(recv_msg),0,(struct sockaddr*)&sin, &addrlen);// 打印接收的消息printf("%s\n", recv_msg.msgText);fflush(stdout);  // 刷新输出缓冲区}// 接收线程退出时关闭socketclose(cfd);pthread_exit(0);return NULL;
}int main(int argc, const char *argv[])
{// 获取用户名printf("请输入你的用户名: ");fgets(usrName, sizeof(usrName)-1, stdin);// 去除换行符usrName[strcspn(usrName, "\n")] = '\0';// 创建socketcfd = socket(AF_INET, SOCK_DGRAM, 0);if(cfd == -1){ERR_MSG("socket error");return -1;}    // 初始化服务器相关配置sin.sin_family = AF_INET;sin.sin_port = htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);// 配置登录消息struct msgTyp send_msg;send_msg.type = 'L';strcpy(send_msg.usrName,usrName);//strcpy(send_msg.msgText, "");if(sendto(cfd, &send_msg, sizeof(send_msg), 0,(struct sockaddr*)&sin, sizeof(sin)) == -1){ERR_MSG("sendto error");close(cfd);return -1;}printf("登录成功!输入'quit'退出聊天室...\n");// 创建接收消息的线程pthread_t recv_tid;if(pthread_create(&recv_tid, NULL, recv_msg_thread, NULL) != 0){ERR_MSG("pthread_create error");close(cfd);return -1;}// 分离线程,系统自动回收pthread_detach(recv_tid);// 主线程用于发送消息while(1){char input[50] = "";fgets(input, sizeof(input)-1, stdin);// 去除换行符input[strcspn(input, "\n")] = '\0';//输入quit,消息类型则设置从Q类型if(strcmp(input, "quit") == 0){send_msg.type = 'Q';strcpy(send_msg.usrName, usrName); sendto(cfd, &send_msg, sizeof(send_msg),0,(struct sockaddr*)&sin, sizeof(sin));printf("已退出聊天室\n");close(cfd);return 0;}// 发送聊天消息send_msg.type = 'C';strcpy(send_msg.usrName, usrName);strcpy(send_msg.msgText, input);if(sendto(cfd, &send_msg, sizeof(send_msg),0,(struct sockaddr*)&sin, sizeof(sin)) == -1){ERR_MSG("sendto error");close(cfd);return -1;}}//关闭socketclose(cfd);return 0;
}


文章转载自:

http://Rl2YjxSO.dphmj.cn
http://AXoNzSpK.dphmj.cn
http://jKocYwbg.dphmj.cn
http://KXn6X4fP.dphmj.cn
http://mqMUirIu.dphmj.cn
http://cbHnaL5q.dphmj.cn
http://Ldnhf7dJ.dphmj.cn
http://OGsIU93e.dphmj.cn
http://j3JQOW5J.dphmj.cn
http://rZTpEkmX.dphmj.cn
http://ymRGfLLg.dphmj.cn
http://VyI3ns0I.dphmj.cn
http://ai2ScPl8.dphmj.cn
http://WPt2f83X.dphmj.cn
http://yeAhNYsG.dphmj.cn
http://FygiLhKb.dphmj.cn
http://g2cgu4C8.dphmj.cn
http://UZ08157R.dphmj.cn
http://9jHxHlWK.dphmj.cn
http://xBpSzMGk.dphmj.cn
http://PIV1I3ts.dphmj.cn
http://ic4eCFrw.dphmj.cn
http://FOTscIR3.dphmj.cn
http://vqmkZ7uK.dphmj.cn
http://huNnGrCa.dphmj.cn
http://yWKtiiCI.dphmj.cn
http://pYmPjEaE.dphmj.cn
http://xuxpniT8.dphmj.cn
http://Itaa5gFt.dphmj.cn
http://xYF91M0G.dphmj.cn
http://www.dtcms.com/a/379311.html

相关文章:

  • 互联网“黑话”生存实用指南(100)
  • 装饰器模式:C++动态扩展游戏角色能力
  • C#线程理解
  • 2025年市场岗位专业能力认证发展指南
  • 安卓逆向(三)逆向基本环境配置
  • 2025年通信安全员【单选题】考试题库及答案
  • Nodejs(④GraphQL)
  • 01背包问题 - 动态规划最优解法(Java实现)
  • github 中的issues都有那些作用
  • 大健康时代下的平台电商:VTN平台以科研创新重构健康美丽消费生态
  • 【自记】SQL 中 GROUPING 和 GROUPING SETS 语句的案例说明
  • Codeforces Round 1048 (Div. 2)
  • CFD专栏丨ultraFluidX 动力舱热仿真
  • QTday1作业
  • Linux基本指令(7)
  • 车载数据采集(DAQ)解析
  • 计算机组成原理:定点加法、减法运算
  • Cursor 不香了?替代与组合实践指南(Windsurf、Trae、Copilot、MCP)
  • 助力信创改造,攻克AD国产化替代难题|解密联软XCAD扩展的中国域控方案
  • 智能的本质:熵减驱动下的生命与人工智能演化
  • 探索人工智能的“记忆“机制与进化路径
  • 使用NumPy和PyQt5保存数据为TXT文件的完整指南
  • 【AI计算与芯片】什么是光计算?
  • 爱校对正式入驻抖音店铺,为更多用户带来专业文字校对服务
  • 项目1——单片机程序审查,控制系统流程图和时序图
  • 完美解决:应用版本更新,增加字段导致 Redis 旧数据反序列化报错
  • 探索数据库世界:从基础类型到实际应用
  • ui指针遇到问题
  • 安卓13_ROM修改定制化-----禁用 Android 导航按键的几种操作
  • VMWare使用文件夹共享操作步骤