网络编程练习:UDP聊天室

devtools/2024/9/24 22:36:37/

 【1】服务器代码

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "head.h"// 服务器-------------------------------------------------------------------------》短信的接收方/*创建一个空的单向链表*/
Node_p Create();/*登录,聊天,退出*/
void login(int fd_s, Msg msg, Node_p p, struct sockaddr_in caddr);
void chat(int fd_s, Msg msg, Node_p p, struct sockaddr_in caddr);
void quit(int fd_s, Msg msg, Node_p p, struct sockaddr_in caddr);int main(int argc, char const *argv[])
{int fd_s;// 1.创建数据报套接字(socket)------------------》有手机fd_s = socket(AF_INET, SOCK_DGRAM, 0);if (fd_s < 0){perror("soc err\n");return -1;}// printf("%d\n", fd_s);// 2.指定网络信息--------------------------------------》有号码struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = INADDR_ANY;int len = sizeof(caddr);// 3.绑定套接字(bind)------------------------------》绑定手机if (bind(fd_s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind err\n");return -1;}// printf("bind ok\n");printf("欢迎使用欢欢聊天室\n");Msg msg;Node_p p = Create(); // 创建空链表pid_t pid;pid = fork(); // 创建新进程if (pid < 0){perror("fork err\n");return -1;}else if (pid == 0){fgets(msg.text, sizeof(msg.text), stdin);if (msg.text[strlen(msg.text) - 1] == '\n')msg.text[strlen(msg.text) - 1] = '\0';msg.type = 'C';strcpy(msg.name, "server");sendto(fd_s, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, len);}else{// 4.接收、发送消息(recvfrom sendto)-------》收短信while (1){int ret;ret = recvfrom(fd_s, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, &len);if (ret < 0){perror("recv err\n");return -1;}// printf("addr: %s  port: %d\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));if (msg.type == 'L'){// printf("addr: %s  port: %d\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));printf("<%s> 已登录\n", msg.name);sprintf(msg.text, "<%s> 已登录", msg.name);login(fd_s, msg, p, caddr);}else if (msg.type == 'C'){printf("[%s]: %s\n", msg.name, msg.text);chat(fd_s, msg, p, caddr);}else if (msg.type == 'Q'){sprintf(msg.text, "<%s> 已下线", msg.name);printf("<%s> 已下线\n", msg.name);quit(fd_s, msg, p, caddr);}}}// 5.关闭套接字(close)----------------------------》接收完毕close(fd_s);return 0;
}/*创空表*/
Node_p Create()
{Node_p p = (Node_p)malloc(sizeof(Node_t)); // 开辟一个节点大小的堆区空间if (NULL == p)                             // 开辟失败{printf("malloc lost");return NULL;}p->next = NULL; // 初始化头节点指针域return p;       // 开辟成功,返回
}/*登录*/
void login(int fd_s, Msg msg, Node_p p, struct sockaddr_in caddr)
{while (p->next != NULL){p = p->next;sendto(fd_s, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));}/*创建新的节点*/Node_p p_new = (Node_p)malloc(sizeof(Node_t));if (p_new == NULL){perror("malloc err\n");return;}/*初始化节点*/p_new->addr = caddr;p_new->next = NULL;/*将新节点连接到链表中*/p->next = p_new;// p = p_new;return;
}/*聊天*/
void chat(int fd_s, Msg msg, Node_p p, struct sockaddr_in caddr)
{while (p->next != NULL){p = p->next;if ((memcmp(&(p->addr), &caddr, sizeof(caddr))) == 0){continue;}sendto(fd_s, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));}
}/*退出*/
void quit(int fd_s, Msg msg, Node_p p, struct sockaddr_in caddr)
{while (p->next != NULL){if ((memcmp(&(p->next->addr), &caddr, sizeof(caddr))) == 0){Node_p p_del = NULL;p_del = p->next;p->next = p_del->next;free(p_del);p_del = NULL;continue;}else{p = p->next;sendto(fd_s, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));}}return;
}

【2】客户端代码 

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include "head.h"
#include <pthread.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>// 客户端---------------------------------------------------------------------------》短信的发送方struct sockaddr_in saddr;
int len = sizeof(saddr);int main(int argc, char const *argv[])
{// 1.创建数据报套接字(socket)------------------》有手机int fd_c;fd_c = socket(AF_INET, SOCK_DGRAM, 0);if (fd_c < 0){perror("soc err\n");return -1;}// printf("%d\n", fd_c);// 2.指定网络信息--------------------------------------》有对方号码saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("192.168.31.187");Msg msg;msg.type = 'L';printf("欢迎使用欢欢聊天室\n");printf("请输入用户名:");fgets(msg.name, N, stdin);if (msg.name[strlen(msg.name) - 1] == '\n')msg.name[strlen(msg.name) - 1] = '\0';sendto(fd_c, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, sizeof(saddr));pid_t pid = fork();if (pid < 0){perror("fork err");exit(-1);}else if (pid == 0){while (1){if (recvfrom(fd_c, &msg, sizeof(msg), 0, NULL, NULL) < 0){perror("recvfrom err");return -1;}if (msg.type == 'L')printf("%s\n", msg.text);else if (msg.type == 'C')printf("[%s]:%s\n", msg.name, msg.text);else if (msg.type == 'Q')printf("%s\n", msg.text);}}else{while (1){fgets(msg.text, sizeof(msg.text), stdin);if (msg.text[strlen(msg.text) - 1] == '\n')msg.text[strlen(msg.text) - 1] = '\0';if (strcmp(msg.text, "quit") == 0){msg.type = 'Q';sendto(fd_c, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, len);kill(pid, SIGKILL);wait(NULL);exit(-1);}else{ msg.type = 'C';}// 发送消息sendto(fd_c, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, len);}}close(fd_c);return 0;
}

今天的分享就到这里结束啦,如果有哪里写的不好的地方,请指正。
如果觉得不错并且对你有帮助的话点个关注支持一下吧!


http://www.ppmy.cn/devtools/116696.html

相关文章

Go语言语法基础

Go语言语法基础 1.Go项目构建及编译2.下划线的用处3.Go语言变量与常量变量命名规范变量声明与初始化常量 4.Go语言基本数据类型整型浮点型复数布尔型指针字符串窥探字符串类型字符串内建函数字符串和数字的转换 1.Go项目构建及编译 一个Go工程中主要包含以下三个目录&#xff…

word-break和word-wrap

1&#xff0c;word-break:break-all 例如div宽200px&#xff0c;它的内容就会到200px自动换行&#xff0c;如果该行末端有个英文单词很长&#xff08;congratulation等&#xff09;&#xff0c;它会把单词截断&#xff0c;变成该行末端为conra(congratulation的前端部分)&#…

SpringCloud Feign 以及 一个标准的微服务的制作

一个标准的微服务制作 以一个咖啡小程序项目的订单模块为例&#xff0c;这个模块必将包括&#xff1a; 各种实体类&#xff08;pojo,dto,vo....&#xff09; 控制器 controller 服务类service ...... 其中控制器中有的接口需要提供给其他微服务&#xff0c;订单模块也需要…

MySQL—触发器详解

基本介绍 触发器是与表有关的数据库对象&#xff0c;在 INSERT、UPDATE、DELETE 操作之前或之后触发并执行触发器中定义的 SQL 语句。 触发器的这种特性可以协助应用在数据库端确保数据的完整性、日志记录、数据校验等操作。 使用别名 NEW 和 OLD 来引用触发器中发生变化的记…

江科大51单片机

文章目录 led灯led点亮led闪烁流水灯 独立按键按键点灯按键消抖按键实现二进制流水灯按键实现流水灯 数码管静态数码管显示动态数码管显示 矩阵键盘定时器/中断串口通信led点阵屏DS1302实时时钟蜂鸣器AT24C02DS18B20LCD1602直流电机驱动AD/DA红外遥控 led灯 创建项目&#xff…

基于代理的分布式身份管理方案

目的是使用分布式的联合计算分发去替换掉区块链中原有的类第三方可信中心的证书机制&#xff0c;更加去中心化。 GS-TBK Group Signatures with Time-bound Keys. CS-TBK 算法 Complete subtree With Time-bound Keys&#xff0c;该算法是用来辅助检测用户的签名是否有效&…

MySQL篇(视图)(持续更新迭代)

目录 一、简介 二、语法 1. 创建 2. 查询 3. 修改 4. 删除 三、演示示例 1. 案例 2. 测试 3. 疑问 四、检查选项 1. 简介 2. 两种 2.1. CASCADED 2.2. LOCAL 五、视图的更新 1. 简介 2. 实例演示 六、视图的作用 1. 简单 2. 安全 3. 数据独立 七、演示案…

基于php的小说阅读系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于phpvueMySQL的小说阅读系…