Linux C语言 UDP协议实现的网络聊天室

需求分析

  • 网络协议:UDP
  • 服务器需求:
    1. 需要一个存放用户数据的容器
      • 因为是UDP协议,固采用链表的方式存储
    2. 服务器需要区分用户的操作类型(如:上线,下线,发送消息)
      • 需要解析消息协议区分用户行为
    3. 因为存在多个阻塞IO,需要多进程,多线程,或IO复用实现
      • 本文采用,多线程实现
  • 客户端需求:
    1. 客户端发送的消息,需要带有相应的消息类型
      • 客户端发送的消息需要自定义一个消息头
    2. 能够接收服务器转发消息,并区分出消息发送者

消息协议及其核心函数

//消息结构体
typedef struct msg
{unsigned short type;unsigned char name[32];unsigned char data[128];
} Msg;//登录事件
int Conn_Event(int server, Msg msg, ClistPtr C, struct sockaddr_in caddr);//消息事件
int Data_Event(int server, Msg msg, ClistPtr C, struct sockaddr_in caddr);//退出事件
int Close_Event(int server, Msg msg, ClistPtr C, struct sockaddr_in caddr);

登录事件核心功能实现

//通过遍历链表的方式将用户上线消息发送给所有其他成员
while(p->next != NULL)
{p = p->next;if(sendto(server, &msg, sizeof(msg), 0, (struct sockaddr*)&(p->caddr), sizeof(p->caddr)) < 0){LOG("sendto error");return -1;}}
//当遍历结束后将登录用户信息加入链表
//链表头插
list_insert_head(C, caddr);

数据事件核心功能实现

//遍历链表将用户发送的消息进行群发
while(C->next != NULL)
{C = C->next;if(memcmp(&(C->caddr), &caddr, sizeof(caddr)) != 0){sendto(server, &msg, sizeof(msg), 0, (struct sockaddr*)&(C->caddr), sizeof(C->caddr));}
}

断连事件核心功能实现

if(NULL == C->next)
{printf("最后一位用户%s已下线\n", msg.name);return 0;
}while(p->next != NULL)
{//找到断连用户节点的前一个节点,进行节点删除if(memcmp(&(p->next->caddr), &caddr, sizeof(caddr)) == 0){list_delete(p);}else{p = p->next;if(sendto(server, &msg, sizeof(msg), 0, (struct sockaddr*)&p->caddr, sizeof(p->caddr)) < 0){LOG("sendto error");return -1;}			}
}

服务器函数

#include "udp_sever.h"#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s);//线程参数结构体
typedef struct
{int server;Msg msg;ClistPtr C;struct sockaddr_in caddr;} Arg_t;//线程转发函数
void* forward(void* arg)
{int server = ((Arg_t*)arg)->server;Msg msg = ((Arg_t*)arg)->msg;ClistPtr C = ((Arg_t*)arg)->C;struct sockaddr_in caddr = ((Arg_t*)arg)->caddr;Data_Event(server, msg, C, caddr);pthread_exit(NULL);
}int main(int argc, char const *argv[])
{//创建结构体变量struct sockaddr_in saddr = {0};ClistPtr C = list_create();if(NULL == C){LOG("list_creat error");return -1;}if(argc != 2){printf("please inputs: %s <port>\n", argv[0]);return -1;}int server = 0;if((server = socket(AF_INET, SOCK_DGRAM, 0)) < 0){LOG("server socket error");return -1;}saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){LOG("bind error");return -1;}printf("udp server start success\n");int res = -1;Msg msg;struct sockaddr_in caddr = {0};socklen_t len = sizeof(caddr);pthread_t tid;Arg_t arg;//循环判断接收事件类型while(1){if((res = recvfrom(server, &msg, sizeof(msg), 0, (struct sockaddr*)&caddr, &len)) < 0){LOG("recvfrom error");return -1;}switch(msg.type){//连接事件case EVE_CONN:Conn_Event(server, msg, C, caddr);break;//数据事件case EVE_DATA:arg.server = server;arg.msg = msg;arg.caddr = caddr;arg.C = C;if(pthread_create(&tid, NULL, forward, &arg) < 0){LOG("pthread_create error");return -1;}pthread_detach(tid);break;//断开事件case EVE_CLOSE:Close_Event(server, msg, C, caddr);break;default:LOG("event error");return -1;}}close(server);return 0;
}

客户端函数

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "udp_sever.h"#define LOG(s) printf("[%s] {%s:%d} %s\n", __DATE__, __FILE__, __LINE__, s);//消息结构体
Msg msg;
//文件描述符变量
int sock = 0;
//远端地址变量
struct sockaddr_in remote = {0};void handler(int sig);int main(int argc, char const *argv[])
{if(argc != 4){printf("please inputs: %s <user name> <host port> <server ip> <server port>\n", argv[0]);return -1;}//本机地址变量struct sockaddr_in addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(atoi(argv[2]));addr.sin_addr.s_addr = htonl(INADDR_ANY);//创建select监听队列和副本fd_set reads = {0};fd_set temps = {0};//创建socketsock = socket(AF_INET, SOCK_DGRAM, 0);if( sock == -1 ){LOG("socket error");return -1;}signal(SIGINT, handler);//指定一个远端地址remote.sin_family = AF_INET;remote.sin_addr.s_addr = inet_addr(argv[3]);remote.sin_port = htons(atoi(argv[4]));//登录msg.type = EVE_CONN;stpcpy(msg.name, argv[1]);if(sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr*)&remote, sizeof(remote)) < 0){LOG("connect failed");return -1;}printf("connect success\n");FD_ZERO(&reads);FD_SET(sock, &reads);FD_SET(0, &reads);Msg rmsg;while(1){msg.type = EVE_DATA;temps = reads;//轮询监测收发事件int num = select(sock+1, &temps, NULL, NULL, NULL);if(num > 0){if(FD_ISSET(0, &temps)){bzero(msg.data, sizeof(msg.data));fgets(msg.data, sizeof(msg.data), stdin);msg.data[strlen(msg.data)-1] = 0;if(sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr*)&remote, sizeof(remote)) < 0){LOG("sendto error");return -1;}}else{int res = -1;if((res = recvfrom(sock, &(rmsg), sizeof(rmsg), 0, NULL, NULL)) < 0){LOG("recvfrom error");return -1;}else if(0 == res){printf("server close \n");return -1;}printf("[%s]: %s\n", rmsg.name, rmsg.data);}	}    }close(sock);return 0;
}void handler(int sig)
{msg.type = EVE_CLOSE;sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr*)&remote, sizeof(remote));exit(-1);
}

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

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

相关文章

Navicat连接postgresql时出现‘datlastsysoid does not exist‘报错

当使用 Navicat 连接 PostgreSQL 数据库时出现 ‘datlastsysoid does not exist’ 的错误报错&#xff0c;这可能是由于 Navicat 版本与 PostgreSQL 版本不兼容所致。 这是因为在较新的 PostgreSQL 版本中移除了 ‘datlastsysoid’ 列&#xff0c;但可能较旧版本的 Navicat 尚…

Go实现LogCollect:海量日志收集系统【上篇——LogAgent实现】

Go实现LogCollect&#xff1a;海量日志收集系统【上篇——LogAgent实现】 下篇&#xff1a;Go实现LogCollect&#xff1a;海量日志收集系统【下篇——开发LogTransfer】 项目架构图&#xff1a; 0 项目背景与方案选择 背景 当公司发展的越来越大&#xff0c;业务越来越复杂…

Mysql底层数据结构为什么选择B+树

索引底层采用什么数据结构&#xff0c;为什么使用B树而不是其他数据结构&#xff1a; &#xff08;1&#xff09;如果采用二叉树&#xff1a;使用递增字段作为索引时&#xff0c;二叉树会退化成链表&#xff0c;查找效率太低 &#xff08;2&#xff09;如果采用红黑树&#xf…

微信小程序开发:一种新型的移动应用程序开发方式

一、引言 随着移动互联网的快速发展&#xff0c;微信小程序作为一种新型的移动应用开发方式&#xff0c;正在受到越来越多的关注。微信小程序是一种基于微信平台的轻量化应用&#xff0c;开发者可以通过微信提供的开发工具和接口&#xff0c;开发出各种具有特定功能的应用程序…

如何创建一个自己的sphinx文档网站

文章目录 前言一、操作步骤1.安装anaconda2.启动python3.8环境3.安装Sphinx4.创建文件夹5.初始化环境6. 编译7.文件夹搭查看8.搭建nginx查看8. 更换主题9.错误修复10.这里提供两个模板1.Demo_md2.Demo_rst前言 最近看到公司的文档中心,突然想起,为什么不为自己创建一个文档中…

在k8s中用label控制Pod部署到指定的node上

案例-标注k8s-node1是配置了SSD的节点 kubectl label node k8s-node1 disktypessd 查看标记 测试 将pod部署到disktypessd的节点上&#xff08;这里设置了k8s-node1为ssd&#xff09; 部署后查看结果-副本全都运行在了k8s-node1上—符合预期 删除标记 kubectl label node k8…

React常见知识点

1. setCount(10)与setCount(preCount > preCount 10) 的区别&#xff1a; import React, { useState } from react; export default function CounterHook() {const [count, setCount] useState(() > 10);console.log(CounterHook渲染);function handleBtnClick() {//…

顶尖211“小清华”!强过985,不要错过它!

一、学校及专业介绍 西安电子科技大学&#xff08;Xidian University&#xff09;&#xff0c;简称“西电” &#xff0c;位于陕西省西安市&#xff0c;是中央部属高校&#xff0c;直属于教育部&#xff0c;为全国重点大学&#xff0c;位列国家“双一流”“211工程”&#xff…

MySQL的内置函数复合查询内外连接

文章目录 内置函数时间函数字符串函数数学函数其他函数 复合查询多表笛卡尔积自连接在where中使用子查询多列子查询在from中使用子查询 内连接外连接左外连接右外连接 内置函数 时间函数 函数描述current_date()当前日期current_time()当前时间current_timestamp()当前时间戳…

Vagrant命令

文章目录 1.介绍2.下载3. 配置3.1 配置环境变量3.2 在xshell中连接使用 4. 相关命令4.1 Box相关4.2 初始化环境4.4 虚拟机相关 1.介绍 Vagrant 是一个虚拟机管理工具 2.下载 https://www.vagrantup.com/ 3. 配置 3.1 配置环境变量 测试安装是否成功 3.2 在xshell中连接使…

Linux权限维持

1.隐藏踪迹 创建隐藏文件(ls不可见&#xff0c;ls -la可见)&#xff1a; vim .shell.php修改时间戳(文件时间)&#xff1a; touch -r 老文件 shell.elf文件锁定(赋予特殊权限&#xff0c;不允许更改)&#xff1a; chattr i shell.elf无w、who、last等记录ssh&#xff1a; …

c#运算符重载

在C#中&#xff0c;您可以通过运算符重载来为自定义类型定义特定的操作行为。运算符重载允许您重新定义与特定运算符相关的操作&#xff0c;以便适应您自定义的类型。 以下是运算符重载的基本语法&#xff1a; public static <returnType> operator <operator> (…

模块化---common.js

入口文件&#xff1a;app.js // require是同步加载 // 客户端&#xff1a;common.js的模块化&#xff0c;需要browserify编译之后才能使用 // 服务端&#xff1a;运行时同步加载&#xff0c;无问题 let module1 require(./module1.js) let module2 require(./module2.js) co…

码云使用记录

码云使用记录 主要步骤 1、https://gitee.com 注册 2、下载Git 3、配置SSH 4、创建远程仓库 5、切到本地项目目录下将本地项目推到远程 前两步根据提示进行即可&#xff0c;下面从第三步开始讲解 3、配置SSH&#xff08;用于提交代码和更新代码&#xff09; https://gitee.…

CCF-CSP 25次 第三题【计算资源调度器】

计算机软件能力认证考试系统 这种题题面很长要迅速提取有效信息&#xff0c;可以根据后面的数据范围逐个的写&#xff0c;这样更容易理解题目&#xff0c;再就是选对数据结构很关键&#xff0c;再加上一点复杂的逻辑思维&#xff0c;基本可以满分 参考&#xff1a;AcWing 445…

机器人中的数值优化(七)——修正阻尼牛顿法

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

CCKS2023:基于企业数仓和大语言模型构建面向场景的智能应用

8月24日-27日&#xff0c;第十七届全国知识图谱与语义计算大会&#xff08;CCKS 2023&#xff09;在沈阳召开。大会以“知识图谱赋能通用AI”为主题&#xff0c;探讨知识图谱对通用AI技术的支撑能力&#xff0c;探索知识图谱在跨平台、跨领域等AI任务中的作用和应用途径。 作为…

nc前端合计行

nc前端合计行 1.无表体和单表体的合计行加法 只要卡片下 如果是只有表头要合计行就只留ShowTotalLine&#xff1b;如果是只有表体要合计行就只留ShowTotalLineTabcodes 2.多表体的合计行加法 表头卡片下和列表下都要 3.档案的合计行加法 重写一下列表模板

Python时间序列分析苹果股票数据:分解、平稳性检验、滤波器、滑动窗口平滑、移动平均、可视化...

全文链接&#xff1a;https://tecdat.cn/?p33550 时间序列是一系列按时间顺序排列的观测数据。数据序列可以是等间隔的&#xff0c;具有特定频率&#xff0c;也可以是不规则间隔的&#xff0c;比如电话通话记录&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。…

史上最全的计算机发展编年史!!!

今天分享给大家&#xff0c;以供参考。 1614年 苏格兰人约翰纳皮尔&#xff08;John Napier&#xff09;发表了一篇论文&#xff0c;其中提到他发明了一种可以计算四则运算和方根运算的精巧装置。 1623年 威廉契克卡德&#xff08;Wilhelm Schickard&#xff09;制作了一个通过…