以线程完成并发的UDP服务端
网络(九)并发的UDP服务端 以线程完成功能
客户端
// todo UDP发送端
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#define LOGIN_SUCCESS 1
#define LOGIN_FAIL 0
//?发送数据
//?@param fd 套接字描述符
//?@param addr 目标地址
//?@param addrlen 地址长度
void send_data(int fd, struct sockaddr_in * addr , socklen_t addrlen);void login(int fd, struct sockaddr_in * addr ,struct sockaddr_in * new_addr , socklen_t new_addrlen);//命令行参数 ip port
int main(int argc, char *argv[] ){
// if(argc!= 3){
// printf("Usage: %s ip port\n", argv[0]);
// exit(EXIT_FAILURE);
// }//!通过socket函数创建套接字//!@param domain 协议族,AF_INET表示IPv4协议族//!@param type 套接字类型,SOCK_DGRAM表示UDP套接字//!@param protocol 协议,一般为0 让系统⾃动识别//!@return 成功返回套接字描述符,失败返回-1int fd = socket(AF_INET, SOCK_DGRAM, 0);if(fd == -1){ //创建套接字失败perror("socket err");exit(EXIT_FAILURE);}//准备接收消息的地址/* struct sockaddr_in {short int sin_family; // 地址族 AF_INETunsigned short int sin_port; // 端口号struct in_addr sin_addr;// IP地址unsigned char sin_zero[8]; // 填充字节 为了对齐sockaddr};*/struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family=AF_INET;addr.sin_port=htons(8083);//htons函数将主机字节序转换为网络字节序//addr.sin_addr.s_addr=inet_addr("192.168.74.1");//inet_addr()将点分十进制IP地址转换为网络字节序IP地址//inet_aton()将点分十进制IP地址转换为网络字节序IP地址//@param ip 字符串形式的IP地址//@param in_addr 结构体变量,用于存储IP地址int ret=inet_aton("172.17.140.183", &addr.sin_addr); // 成功返回⾮0,失败返回0if(ret == 0){perror("inet_aton err");exit(EXIT_FAILURE);}printf("ip == %d\n",addr.sin_addr.s_addr);//inet_ntoa()将网络字节序IP地址转换为点分十进制IP地址//char *ip=inet_ntoa(addr.sin_addr); // 成功返回⾮0,失败返回0//printf("ip == %s\n",ip);//获取 和服务端的新建的子进程通信struct sockaddr_in new_addr;login(fd, &addr, &new_addr, sizeof(new_addr));//与新的子进程通信send_data(fd, &new_addr, sizeof(new_addr));return 0;
}//!发送数据
//!@param fd 套接字描述符
//!@param addr 目标地址
//!@param addrlen 地址长度
void send_data(int fd, struct sockaddr_in * addr , socklen_t addrlen){while (1){int n = 0;//返回发送的字节数char buf[1024] = {0};printf("请输入要发送的消息:");fgets(buf, 1024, stdin);//!发送数据//!@param fd 套接字描述符//!@param buf 发送缓冲区//!@param len 发送缓冲区长度//!@param flags 发送标志 0 表示默认操作//!@param addr 目标地址//!@param addrlen 地址长度//!@return 成功返回发送的字节数,失败返回-1n= sendto(fd, buf, strlen(buf), 0, (struct sockaddr *)addr, addrlen);if(n == -1){perror("sendto err");exit(EXIT_FAILURE);}if(strncmp(buf, "exit",4) == 0){break;}}}void login(int fd, struct sockaddr_in * addr ,struct sockaddr_in * new_addr , socklen_t addrlen){char login_status=LITTLE_ENDIAN;while (1){int n = 0;//返回发送的字节数char buf[1024] = {0};printf("请输入要发送的消息:");fgets(buf, 1024, stdin);n= sendto(fd, buf, strlen(buf), 0, (struct sockaddr *)addr, addrlen);if(n == -1){perror("sendto err");exit(EXIT_FAILURE);}//接收消息服务器的响应n= recvfrom(fd, &login_status, sizeof(login_status), 0, (struct sockaddr *)new_addr, &addrlen);if(n == -1){perror("recvfrom err");exit(EXIT_FAILURE);}if(login_status == LOGIN_SUCCESS){printf("登录成功\n");printf("新的子进程的地址为:%s:%d\n",inet_ntoa(new_addr->sin_addr),ntohs(new_addr->sin_port));break;}else if(login_status == LOGIN_FAIL){printf("登录失败\n");continue;}if(strncmp(buf, "exit",4) == 0){break;}}}
服务端
服务端创建主线程,接收客户端的请求,创建新的子线程,
子线程完成后续交互,子线程中创建新的套接字,返回给客户端,后续交互将在新的套接字中完成.
将子线程分离,线程运行结束将由系统回收资源
// todo UDP服务器端程序
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
#define LOGIN_SUCCESS 1
#define LOGIN_FAIL 0//接收数据
void recv_data(int sockfd);
void *pthread_todo(void *arg);//初始化套接字
int init_socket(char *ip, char* port);int TheLogin(char *ip, char * port);//定义结构体为子线程传递参数
struct thread_arg {char *ip;unsigned char login_status;struct sockaddr_in thread_addr;//客户端的地址
}thread_arg;int main(int argc, char *argv[]){//验证int new_sockfd = TheLogin("172.17.140.183", "8083");//接收数据//由子线程完成//关闭套接字close(new_sockfd);return 0;
}//接收数据
void recv_data(int sockfd) {struct sockaddr_in client_addr;//客户端的地址int client_addr_len = sizeof(client_addr);while(1) {char recv_buf[1024]={0};//接收数据//*@param sockfd 套接字描述符//*@param buf 接收缓冲区//*@param len 接收缓冲区长度//*@param flags 接收标志//*@param src_addr 发送方地址//*@param addrlen 发送方地址长度//*@return 成功返回接收到的字节数,失败返回-1int ret = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *) &client_addr, &client_addr_len);if (ret == -1) {perror("recvfrom err");exit(EXIT_FAILURE);}//打印接收到的信息char *ip_str = inet_ntoa(client_addr.sin_addr);//将网络字节序IP地址转换为点分十进制IP地址int port = ntohs(client_addr.sin_port); //将网络字节序端口号转换为主机字节序端口号printf("接收到来自%s:%d的数据:%s\n", ip_str, port, recv_buf);if(strncmp(recv_buf, "exit", 4) == 0){//退出程序break;}}close(sockfd);return;
}int init_socket(char *ip,char *port){//!通过socket函数创建套接字//!@param domain 协议族,AF_INET表示IPv4协议族//!@param type 套接字类型,SOCK_DGRAM表示UDP套接字//!@param protocol 协议,一般为0 让系统⾃动识别//!@return 成功返回套接字描述符,失败返回-1int fd = socket(AF_INET, SOCK_DGRAM, 0);if(fd == -1){ //创建套接字失败perror("socket err");exit(EXIT_FAILURE);}//准备服务器地址/* struct sockaddr_in {short int sin_family; // 地址族 AF_INETunsigned short int sin_port; // 端口号struct in_addr sin_addr;// IP地址unsigned char sin_zero[8]; // 填充字节 为了对齐sockaddr};*/struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family=AF_INET;addr.sin_port=htons(atoi(port));//htons函数将主机字节序转换为网络字节序//inet_aton()将点分十进制IP地址转换为网络字节序IP地址//*@param ip 字符串形式的IP地址//*@param in_addr 结构体变量,用于存储IP地址int ret=inet_aton(ip, &addr.sin_addr); // 成功返回⾮0,失败返回0if(ret == 0){perror("inet_aton err");exit(EXIT_FAILURE);}//!绑定套接字到服务器地址//!@param sockfd 套接字描述符//!@param addr 服务器地址//!@param addrlen 服务器地址长度//!@return 成功返回0,失败返回-1int ret2 = bind(fd, (struct sockaddr*)&addr, sizeof(addr));if(ret2 == -1){perror("bind err");exit(EXIT_FAILURE);}return fd;
}int TheLogin(char *ip, char *port){unsigned char login_status;int new_sockfd;//初始化套接字int sockfd = init_socket(ip, port);//线程创建pthread_t recv_thread;struct sockaddr_in client_addr;//客户端的地址int client_addr_len = sizeof(client_addr);while(1) {char recv_buf[1024]={0};int ret = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *) &client_addr, &client_addr_len);if (ret == -1) {perror("recvfrom err");exit(EXIT_FAILURE);}//打印接收到的信息char *ip_str = inet_ntoa(client_addr.sin_addr);//将网络字节序IP地址转换为点分十进制IP地址int port = ntohs(client_addr.sin_port); //将网络字节序端口号转换为主机字节序端口号printf("接收到来自%s:%d的数据:%s\n", ip_str, port, recv_buf);//登录验证//判断是否为登录请求login_status = ( strncmp(recv_buf, "login",5)==0 ? LOGIN_SUCCESS: LOGIN_FAIL ) ;if(login_status == LOGIN_SUCCESS){//使用子线程完成后续交互struct thread_arg pack;pack.ip = ip;pack.login_status = login_status;pack.thread_addr = client_addr;//创建子线程pthread_create(&recv_thread, NULL, pthread_todo, &pack);printf("子线程创建成功\n");} else{//回传失败消息sendto(sockfd, &login_status, sizeof(login_status), 0, (struct sockaddr *) &client_addr, client_addr_len);}//将新建的线程设置为分离状态pthread_detach(recv_thread);printf("子线程分离成功\n");}}void *pthread_todo(void *arg){//子线程函数struct thread_arg *pack = (struct thread_arg*)arg;//创建新的套接字文件描述符int new_sockfd = init_socket(pack->ip, "0");printf("子线程创建新的套接字文件描述符:%d\n", new_sockfd);sendto(new_sockfd, &pack->login_status, sizeof(pack->login_status), 0,\(struct sockaddr *) &pack->thread_addr, sizeof(pack->thread_addr));//接收数据recv_data(new_sockfd);pthread_exit(NULL);}
相关文章:
以线程完成并发的UDP服务端
网络(九)并发的UDP服务端 以线程完成功能 客户端 // todo UDP发送端 #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/types.h> #include <stdlib.h> #include <string.h…...
linux c 特殊字符分割
/* * brief: 根据split_symbol分割字符串 * param: str为要分割的字符串,split_symbol是分隔符 * return:返回garray的指针数组,如果返回非空需要自己处理释放 */ GPtrArray_autoptr char_sz_spilt(pchar* str, pchar split_symbol) {if (NUL…...
搭建本地私有知识问答系统:MaxKB + Ollama + Llama3 (wsl网络代理配置、MaxKB-API访问配置)
目录 搭建本地私有知识问答系统:MaxKB、Ollama 和 Llama3 实现指南引言MaxKB+Ollama+Llama 3 Start buildingMaxKB 简介:1.1、docker部署 MaxKB(方法一)1.1.1、启用wls或是开启Hyper使用 WSL 2 的优势1.1.2、安装docker1.1.3、docker部署 MaxKB (Max Knowledge Base)MaxKB …...
谷粒商城实战笔记-65-商品服务-API-品牌管理-表单校验自定义校验器
文章目录 1,el-form品牌logo图片自定义显示2,重新导入和注册element-ui组件3,修改brand-add-or-update.vue控件的表单校验规则firstLetter 校验规则sort 校验规则 1,el-form品牌logo图片自定义显示 为了在品牌列表中自定义显示品…...
学好C++之——命名空间
c开始学习之时,你不可避免会遇到一个新朋友,那就是——namespace(命名空间)。 那么这篇文章就来为你解决这个小麻烦喽~ 目录 1.namespace存在的意义 2.namespace的定义 3.namespace的使用 1.namespace存在的意义 在C中&#…...
pytorch lightning报错all tensors to be on the same device
RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu! 修改指定为gpu trainer pl.Trainer(max_epochstrain_params.iterations, loggertb_logger,acceleratorgpu, devices1)...
Redis中的哨兵(Sentinel)
上篇文章我们讲述了Redis中的主从复制(Redis分布式系统中的主从复制-CSDN博客),本篇文章针对主从复制中的问题引出Redis中的哨兵,希望本篇文章会对你有所帮助。 文章目录 一、引入哨兵机制 二、基本概念 三、主从复制的问题 四、哨…...
产业创新研究杂志产业创新研究杂志社产业创新研究编辑部2024年第12期目录
高质量发展 如何在新一轮产业链变革中平稳应对挑战 王宏利; 1-3《产业创新研究》投稿:cnqikantg126.com 基于ERGM的城市间绿色低碳技术专利转让网络结构及演化研究 吕彦朋;姜军;张宁; 4-6 数字基础设施建设对城市FDI的影响——基于“宽带中国”试点政策…...
网闸(Network Gatekeeper或Security Gateway)
本心、输入输出、结果 文章目录 网闸(Network Gatekeeper或Security Gateway)前言网闸主要功能网闸工作原理网闸使用场景网闸网闸(Network Gatekeeper或Security Gateway) 编辑 | 简简单单 Online zuozuo 地址 | https://blog.csdn.net/qq_15071263 如果觉得本文对你有帮助…...
C#中的字符串
String 在实例方法中string虽然传入的是引用类型 但是修改string 并不是修改原来堆里面的值 而是又重新创建一个堆值 用来然后用方法内的变量指向新的堆值 C# 中的字符串(string 类型)提供了许多有用的方法来处理字符串数据。以下是一些常用的字符…...
docker安装部署elasticsearch7.15.2
docker安装部署elasticsearch7.15.2 1.拉取es镜像 docker pull docker.elastic.co/elasticsearch/elasticsearch:7.15.2如果不想下载或者镜像拉去太慢可以直接下载文章上面的镜像压缩包 使用镜像解压命令 docker load -i elasticsearch-7-15-2.tar如下图所示就表示镜像解压成…...
Symfony 入门指南:快速安装与基础配置
Symfony 入门指南:快速安装与基础配置 Symfony 是一个强大而灵活的 PHP 框架,广泛应用于构建现代 Web 应用程序。本指南将带您一步一步地了解如何快速安装 Symfony,并完成基本配置,以便您能够开始使用这个强大的框架。 目录 引…...
3.3V升压至5V的AH6922芯片:高效能的SOP8封装解决方案
# 3.3V升压至5V的AH6922芯片:高效能的SOP8封装解决方案 在当今快速发展的电子设备领域,对于电源管理的需求日益增长。特别是对于便携式产品和手持设备,一个高效、稳定且体积小巧的升压解决方案变得至关重要。本文将介绍一款专为这些需求设计…...
赋能未来教育,3DCAT助力深圳鹏程技师学院打造5G+XR实训室
随着国家对教育行业的重视,实训室建设已成为推动教育现代化的关键。《教育信息化2.0行动计划》、《职业教育示范性虚拟仿真实训基地建设指南》等政策文件,明确指出了加强虚拟仿真实训教学环境建设的重要性。 在这一大背景下,教育行业对于实训…...
力扣141环形链表问题|快慢指针算法详细推理,判断链表是否有环|龟兔赛跑算法
做题链接 目录 前言: 一、算法推导: 1.假设有环并且一定会相遇,那么一定是在环内相遇,且是快指针追上慢指针。 2.有环就一定会相遇吗?快指针是每次跳两步,有没有可能把慢指针跳过去? 3.那一定…...
React 常见的报错及解决方法
1、Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons(无效的钩子调用。钩子只能在函数组件的内部调用。这可能是由于以下原因之一) 原因&#x…...
更新服务器nginx 1.26.1版本
今天在官网下载了nginx1的1.26.1版本,使用gpt的脚本想直接覆盖安装,脚本如下 #!/bin/bash# 设置变量 NGINX_VERSION"1.26.1" TAR_FILE"nginx-$NGINX_VERSION.tar.gz" SRC_DIR"nginx-$NGINX_VERSION"# 检查是否存在tar包 …...
JAVA代码审计JAVA0基础学习(需要WEB基础知识)DAY2
JAVA 在 SQL执行当中 分为3种写法: JDBC注入分析 Mybatis注入分析 Hibernate注入分析 JDBC 模式不安全JAVA代码示例部分特征 定义了一个 sql 参数 直接让用户填入id的内容 一个最简单的SQL语句就被执行了 使用安全语句却并没有被执行 Mybatis: #…...
SpringBoot整合elasticsearch-java
一、依赖 系统使用的是ElasticSearch8.2.0 <dependency><groupId>co.elastic.clients</groupId><artifactId>elasticsearch-java</artifactId><version>8.1.0</version> </dependency> 二、配置 1、yml文件配置 elastics…...
网络服务与应用
一、 文件传输 FTP 1、FTP采用典型的C/S架构(即服务器端和客户端模型),客户端与服务器端建立TCP连接之后即可实现文件的上传、下载。 2、FTP传输过程 1)、主动模式(POST):入站连接 2&#x…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
