linux网络编程(完整版)

2023-05-16

之间在网上看到很多网络编程都是一个一个demo,今天我把之前学到的汇总起来,希望大家可以进行补充。
我理解的网络通信分为4种
1,udp客户端
2,udp服务端
3,tcp客户端
4,tcp服务端

线程中我使用过两种方式编程,一种是经典函数式编程加上标志位,如下:


while(1)
{
		server_init();
		client_init();
		sock_send();
		select_handler();
}


其中各函数里面放置了大量的标志位,如下:

void client_init(void)
{
	  //确认客户端初始化标志位
	  //
}
void sock_send(void)
{
		//判断客户端标志位,成功则继续进行
		if(client_init_flag)
		{
				//发送操作
				//确认客户端发送标志位
		}
}

void select_handler(void)
{
		if(send_flag)
		{
				//处理数据并接受可以用select
		}
}

这种方式,我觉得在看代码的时候很乱,但是他在大量的通信时还比较友好,可以建立一个结构体数组,每个数组成员代表一个客户端,结构体放置client_init_flag和send_flag。
还有一种方式采用的是状态机编程

创建枚举
typedef enum client_statues_t
{
		init_flag_status,
		send_flag_status,
}client_statues_t;
client_statues_t client_statues;
while(1)
{
		//先接受,后发送
		switch(client_statues)
		{
			 case init_flag_status:
			 		client_init();
			 		break;
			 case send_flag_status:
			 		sock_send();
			 		break; 	
			 。。。
			 	
		}

}

状态机在单片机使用很常见,但是如果多客户端初始化与发送,容易搞混,并且个人觉得增删查改略费劲,有可能是我自己水平有限,所以今天只写了一个关于第一种方式的代码。因为标志位太多了,而且各种判断比较乱,所以以下就没有各种标志位,但真正项目中是要有的并且还需要有打印日志功能(printf函数),整体思路如下:

>>创建udp服务端,创建tcp服务端
>>创建udp客户端,创建tcp客户端
			>>发送数据
						>>接受数据并处理
			

首先是udp服务端,创建tcp服务端,服务端程序比客户端较简单

//返回值为是否成功标志,需要在各行赋值代码中判断,此代码不进行演示
//create_udpServer()、create_tcpServer()参数可以为全局变量的关于服务器端的结构体,结构体里端口号,初始化标志位,等等,此代码不进行演示

struct sockaddr_in udp_sockServer;
void create_udpServer(void)
{
		udp_socket = socket(AF_INET, SOCK_DGRAM,0);
		//INADDR_ANY,
		udp_sockServer.sin_addr.s_addr = htonl(INADDR_ANY);
		udp_sockServer.sin_port = htons(udp_port);//port自己设置
		udp_sockServer.sin_family = AF_INET;
		bind(udp_socket, (struct sockaddr *)&udp_sockServer, sizeof(struct sockaddr_in));

}
struct sockaddr_in tcp_sockServer;
void create_tcpServer(void)
{
		tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
		tcp_sockServer.sin_addr.s_addr = htonl(INADDR_ANY);
		tcp_sockServer.sin_port = htons(tcp_podt);//port自己设置
		tcp_sockServer.sin_family = AF_INET;
		bind(tcp_socket, (struct sockaddr *)&tcp_sockServer, sizeof(struct sockaddr_in));
		listen(tcp_socket, n);//n自己设置
}
void server_init(void)
{
		create_udpServer();
		create_tcpServer();
}

其次是创建tcp和udp的客户端

//返回值为是否成功标志,需要在各行赋值代码中判断,此代码不进行演示
//create_tcpClient()、create_udpClient(v)参数可以为全局变量的关于客户端的结构体,要连接服务器的结构体信息,初始化标志位,等等,此代码不进行演示
struct sockaddr_in  tcpServer;
void create_tcpClient(void)
{
		tcpSock = socket(AF_INET, SOCK_STREAM, 0);
		//客户端需要知道服务端信息
		tcpServer.sin_addr.s_addr = htonl(ip);//ip自己设置
		tcpServer.sin_port = htons(tcpPort);//port自己设置
		tcpServer.sin_family = AF_INET;
 	   /*	设置setsockopt参数 ,具体请看我之间发的一篇blog
		*https://blog.csdn.net/qq_32166779/article/details/88853435
		*/
		
}
/*把connect单独写是因为这个步骤比较特殊,他是阻塞函数,需要有一定的延时,有两种方、法,一种是利用setsockopt:https://blog.csdn.net/qq_32166779/article/details/88853435
一种是利用select检查socket描述符:http://blog.csdn.net/ast_224/article/details/2957294
*/
void tcpClient_connectServer(void)
{
        //这个tcpsock和create_tcpClient函数中是同一个
		connect(tcpSock, (struct sockaddr *)&tcpServer, sizeof(struct sockaddr_in));
}

struct sockaddr_in  udpServer;
void create_udpClient(void)
{
		udpSock = socket(AF_INET, SOCK_DGRAM, 0);
		udpServer.sin_addr.s_addr = htonl(pUDP->ip);
	    udpServer.sin_port = htons(pUDP->udpPort);
	    udpServer.sin_family = AF_INET;
}

void client_init(void)
{
		create_tcpClient();
		tcpClient_connectServer();
		create_udpClient();
}


udp 服务器接受与发送函数,这个只设置能接受一个客户端发数

//udp先接受客户端的数据,然后发送数据
void udp_server_recviveandsend(void)
{
				/*
		参数udp_socket为 建立udp服务端的
		*/
		recvfrom(udp_socket, rxBuf, rxbuf_len, 0,(struct sockaddr *)&sockClient, &(sizeof(struct sockaddr_in)));
		
		sendto(udp_socket , txBuf, txbuf_len, 0, (struct sockaddr *)&sockClient,sizeof(struct      sockaddr_in));

}
 

//tcp客户端发数与接受

void udp_server_recviveandsend(void)
{
	    /*tcpSock为建立*/
		send(tcpSock , txbuf, len, 0);
		recv(tcpSock , rxbuf, len, 0);
		
}

//tcp服务端是最难的,需要考虑客户端的ip,个数限制,并根据每个客户端进行通信,这里需要设置一个结构体数组,

typedef struct tcp_accept_t
{
	int lifeNum;//用来设置socket存在时间
	int socket;//
	ulong ip;  //保存客户端ip
	ushort port;//保存客户端端口号
	int len;
}tcp_accept_t;
#define num 100//定义最大接受客户端的数量,
tcp_accept_t tcp_accept[num];

tcp接受客户端大体思路是这样想的:
1,利用select先获取accept之前的套接字
2,当accept响应,建立新套接字
3,有了新套接字,利用select获取receive的响应
4,如果新来客户端总数超过num,则放弃最早的客户端

 create_tcpServer();
 struct sockaddr_in sockClient;
while(1)
{
		/*先*/
		int  		socketMax=-1;
		fd_set 	fdSockSet;
		int    		fd_act;
		struct timeval timeout;

		FD_ZERO(&fdSockSet);
		//tcp_sockServer与create_tcpServer()一致
		if(tcp_sockServer>0)
		{
				FD_SET(tcp_sockServer, &fdSockSet);
				if(socketMax < tcp_sockServer) {socketMax = tcp_sockServer};
		}
		for(int i = 0; i<num;i++)
		{
				if(tcp_accept[i].socket>0)
				{
						FD_SET(tcp_accept[i].socket, &fdSockSet);
						if(socketMax <tcp_accept[i].socket) {socketMax = tcp_accept[i].socket};
				}	
		}
if(socketMax > 0){
		timeout.tv_sec = 5;
		timeout.tv_usec = 0;
		fd_act = 0;
		fd_act = select(socketMax+1, &fdSockSet, NULL, NULL, &timeout);
		if(fd_act>0)
		{
				for(int i = 0; i<num;i++)
				{
						if(tcp_accept[i].socket>0)
						{
								FD_SET(tcp_accept[i].socket, &fdSockSet);
								if(FD_ISSET(tcp_accept[i].socket, &fdSockSet)) 
								{
										recv(tcp_accept[i].socket, rxbuf, SOCKET_TCP_RECV, 0);
								}
						}	
				}	

				if(FD_ISSET(tcp_sockServer, &fdSockSet)) 
				{
						newSocket = -1;
						newSocket = accept(tcp_sockServer, (struct sockaddr *)&sockClient, (socklen_t *)&addrlen);	//sockclient为连接的客户端信息,addrlen为sockaddr结构体长度。					if(newSocket>0){
						ip = ntohl(pTCP->sockClient.sin_addr.s_addr);
						port = ntohs(pTCP->sockClient.sin_port); 		
						sLinger.l_onoff = 0;
						ret = setsockopt(newSocket, SOL_SOCKET, SO_LINGER, &sLinger, sizeof(struct linger));
						lifeNumMax++;
								if(lifeNumMax >= 100000000){
									lifeNumMax = 1;
									for(i=0;i<SOCKET_ACCEPT_MAX;i++){
										if(tcpAccept[i].socket > 0){
											tcpAccept[i].lifeNum -= 100000000;
										}
									}		
								}
								/* IP相同,则不需要关闭任何客户端 */
								for (i = 0; i < SOCKET_ACCEPT_MAX; i++) {
									if (tcpAccept[i].socket > 0 && tcpAccept[i].ip == ip) {
										j = i;
										goto accept_new_socket;
									}
								}		
								/* 新来设备,判断是否num不够,如果num够则新增acceptsocket */
								for (i = 0; i < SOCKET_ACCEPT_MAX; i++) {
									if (tcpAccept[i].socket==0) {
										j = i;
										goto accept_new_socket;
									}
								}		
								/* 新来设备,判断是否num不够,如果num不够则新增acceptsocket并放弃最早的客户端  */
								j = 0;	
								lifeNumMin = tcpAccept[0].lifeNum;
								for (i = 1; i < SOCKET_ACCEPT_MAX; i++) {
									if(lifeNumMin > tcpAccept[i].lifeNum){
										lifeNumMin = tcpAccept[i].lifeNum;
										j = i;
									}
								}	
accept_new_socket:
						
								if(tcpAccept[j].socket > 0){
									close(tcpAccept[j].socket);
								}
								tcpAccept[j].socket = newSocket;
								tcpAccept[j].ip = ip;
								tcpAccept[j].port = port;
								tcpAccept[j].status = 0xff;	/* socket正常使用 */
								tcpAccept[j].lifeNum = lifeNumMax;

								  
				}
		}
	

}

其中放弃最早的客户端采用了比较简单的算法
每个客户端在连接时给予一个lifeNum生命值,还有一个不断增加的计数器 lifeNumMax,当客户端增加到最大数量时,比较前几个客户端的生命值,把最小的除去,因为它连接的最久。生命值代码为

		lifeNumMax++;
		if(lifeNumMax >= 100000000){
			lifeNumMax = 1;
			for(i=0;i<SOCKET_ACCEPT_MAX;i++){
				if(tcpAccept[i].socket > 0){
					tcpAccept[i].lifeNum -= 100000000;
				}
			}		
		}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux网络编程(完整版) 的相关文章

  • 一种动态更新flink任务配置的方法

    文章目录 1 原理2 例 xff0c 整数过滤2 1 并行度为12 2 并行度大于12 3 完整代码 参考链接 1 原理 参考Flink Spark 如何实现动态更新作业配置 xff0c 讲得比较详细 xff0c 这篇的文章的参考参考文献也
  • 一种Hudi on Flink动态同步元数据变化的方法

    文章目录 一 背景二 官方Schema Evolution例子三 Flink 43 Hudi实现Schema Evolution四 96 HoodieFlinkStreamer 96 流程浅析及扩展方法4 1 FlinkKafkaConsu
  • 一种处理Hive元数据与文件类型不同时SQL查询失败的方法

    文章目录 一 背景二 分析过程2 1 环境及测试数据2 1 1 环境2 1 2 测试数据 2 2 select语句异常分析2 2 1 异常分析2 2 2 捕获异常位置 2 3 insert overwrite语句异常分析2 3 1 异常分析
  • 一种处理Hive元数据与文件类型不同时SQL查询失败的方法(二)

    文章目录 一 异常触发SQL二 异常处理三 Hive on Spark依赖的Hive jar包部署 继上一篇之后 xff0c 又发现了一种新的报错位置 本篇对这种情况进行处理 xff0c 并验证这种处理方式是否适用于Hive on Spar
  • PX4代码CMakeLists.txt的简单解读

    分析一下PX4代码CMakeLists txt文件 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 6
  • Hive分区表修改(增删)列

    文章目录 一 环境及测试数据二 删除列2 1 测试表test22 2 DDL删除列 xff1f 2 3 代码连接Hive Metastore删除列2 3 1 同时更新表与分区元数据 三 添加列结论参考链接 一 环境及测试数据 环境 xff1
  • python通过kerberos连接impala操作hive

    一 python2版本 1 docker基础镜像 tobyhferguson impyla tester 2 安装相关包及配置 2 1 安装包 参考python3 6 5基于kerberos认证的hive和hdfs连接调用 yum span
  • 程序员成长路线

    回顾过去 xff0c 我们会清晰地看见走过来的路线 xff0c 但面向未来我们又该如何走下去 xff1f 但凡过往 xff0c 皆为序章 xff0c 过去不可变 xff0c 未来才是希望 xff0c 而如何去规划并管理好未来的成长进化之路
  • 消息队列、信号量、事件组、任务通知之间的区别

    消息队列 信号量 事件组 任务通知之间的区别 消息队列 xff08 需要传递消息时使用 xff09 在任务与任务间 中断和任务间传递信息 xff0c 可以数据传输 事件标志组 xff08 多个事件同步 xff0c 不需要传递消息时使用 xf
  • 包含癞子的麻将胡牌算法

    记录一下麻将的通用胡牌算法实现 xff0c 只要满足M x ABC 43 N x DDD 43 EE 即可胡牌 在这里先分析一下最简单的胡牌思路 xff1a 先找出所有可能的将牌 xff0c 若除去两张将牌之外的所有牌都能成扑 xff0c
  • 麻将胡牌算法

    用数字代替麻将中的所有牌 xff1a 一万 九万 1 9 一筒 九筒 11 19 一条 九条 21 29 东 南 西 北 中 发 白 31 37 胡牌的种类 xff1a 对对胡 十三幺和33332 判断胡牌的过程 xff08 以下所说的规则
  • 麻将胡牌万能公式:

    基本胡牌类型 M AAA 43 N ABC 43 DD xff08 AAA 是三个刻字 xff0c ABC 是顺子 xff0c DD 是将牌 xff09 麻将总共14张 xff0c m和n系数 xff0c AAA代表一样的三个 xff0c
  • 麻将

    来自百度百科https baike baidu com item E9 BA BB E5 B0 86 215 fr 61 aladdin 一副完整的麻将牌共152张 包括字牌 花牌 序数牌 百搭牌 流行的是精简版麻将 xff0c 不含百搭和
  • 使用etcdctl 获取 kubernetes etcd内容

    kubernetes排错过程中可能需要连接到etcd库核对数据 通过etcdctl命令可以连接到库 xff0c 查看etcd库中的key value 创建一个etcdctl的别名 证书的位置可以通过查看kubelet 运行参数获取到 etc
  • Jenkins 部署jar包到服务器并启动后的乱码问题

    在java命令中加 参数 Dfile encoding 61 UTF 8 export LANG 61 zh CN UTF 8 是为了解决应用输出的日志中有乱码的问题 export LANG 61 zh CN UTF 8 nohup jav
  • VS CODE 页面没暂存/提交就放弃更改了怎么还原

    如图所示 xff0c 不小心手滑将还没有暂存的页面点了放弃更改 xff0c 过了两天才发现搞错了 xff0c 这时候怎么办呢 xff1f 不用担心 xff0c 找到这个页面 xff0c 直接ctrl 43 z可以还原回来 亲测有效
  • idea通过maven使用docker插件生成镜像并推送到harbor仓库

    windows有2种方式获取docker环境 xff0c 使用VM构建linux环境并安装docker xff0c 另一种是安装dockertoolbox xff0c 再通过bash的方式创建虚拟的docker环境 xff08 本文使用后者
  • linux 将for 循环写在命令行

    for p in kubectl get pods namespace 61 kube system l k8s app 61 kube dns o name do kubectl logs namespace 61 kube system
  • iptables -j MARK --set-xmark 解析

    查看man和命令帮助 xff0c 还有网上搜到的解释不是很清晰 xff0c 所以验证了一下 xff0c 并尽量将其解释的通俗易懂 MARK的作用 MARK标记用于将特定的数据包打上标签 xff0c 供Iptables配合TC做QOS流量限制
  • kubernetes调试pod网络故障方法之一

    使用故障pod的network namespace运行一个容器来调试调试pod网络 有时候我们要排查pod的网络问题 xff0c 但是生产容器内一般不会有调试命令 xff0c 另外容器可能处于CrashLoopBackOff或其它不可用的状

随机推荐

  • iptables icmp-host-prohibited引起的No route to host

    如果数据包匹配到下边这条iptables规则 xff0c 访问端则会报No route to host xff0c 抓包中可能会看到 unreachable admin prohibited j REJECT reject with icm
  • kubernetes自动补全命令

    root 64 master01 apt y install bash completion root 64 master01 source usr share bash completion bash completion root 64
  • ubuntu server 20.04使用kubeadm安装k8s v1.23.5

    这篇文章也不错 xff1a https blog csdn net hefashion0190 article details 122635188 两台服务器配置hosts vim etc hosts 192 168 199 116 mas
  • 使用kubeadm 在Centos7.9 上安装k8s集群

    停止并禁用firewalld systemctl stop firewalld amp amp systemctl disable firewalld 关闭selinux sed i s 34 SELINUX 61 enforcing 34
  • Linux bash命令根据历史记录补全

    比如 xff0c 想找 docker run 开头的历史命令 xff0c 只需要键入 docker run 然后按 进行选择 但是在用服务器的bash时没有这个功能 xff0c 每次按 都会把敲入一半的命令清除掉 ctrl r 也不是很好用
  • Docker优化参数

    cat gt etc docker daemon json lt lt EOF 34 oom score adjust 34 1000 34 log driver 34 34 json file 34 34 log opts 34 34 m
  • Rancher 监控组件 Grafana 数据持久化

    以下配置是基于在 Rancher Cluster Manager 中启用监控 xff0c 而非在 Cluster Explorer 应用程序和市场中启用的新版监控 场景描述 xff1a 首先确保你的pv 回收策略是Retain 可以参照此文
  • MATLAB设置使用语言为中文

    因为每次自己都会忘记如何操作 xff0c 所以记录在自己的博客里 xff0c 避免忘记 在MATLAB主页 预设 帮助 右侧选取语言为简体中文 xff0c 具体操作可以见图片
  • kubernetes cordon原理

    cordon 节点可以使节点不接受调度pod 那么原理是什么呢 其实很简单 xff0c 就是给节点增加了一个污点 kubectl describe node NODE NAME 能看到这个污点配置 xff1a Taints node kub
  • 解决win10 win11 蓝牙音箱可以连接无法播放声音/蓝牙音箱无法选择使用

    这个靠谱 xff0c 记录一下 1 快捷键win 43 r xff0c 输入regedit xff0c 打开注册表 2 进入目录 xff1a 计算机 HKEY LOCAL MACHINE SYSTEM ControlSet001 Contr
  • journalctl 日志保留时间及持久化存储日志

    Systemd Journald基础 默认情况下 xff0c Systemd将系统日志存储在 run log journal目录中 xff0c 正如我们在了解Linux文件系统层次结构中所说的 参考 xff1a 介绍Red Hat Ente
  • Linux之/etc/fstab文件详解及实践

    转 https blog csdn net carefree2005 article details 125632867
  • Jenkins 上传文件到工作目录

    使用插件 xff1a File Parameter Plugin 之前写过一篇关于Jenkins 用户上传文件到工作目录的文章 xff0c 那时候还需要使用sharedlibraries 现在使用这个插件可以非常简单的上传文件 话不多说 x
  • Jenkins 在不同的agent之间传递文件/上传文件

    有时jenkins pipeline需要在不同的agent上执行不同的操作 xff0c 同时需要在不同的agent之间传递文件 方法如下 xff1a stage 39 上传文件 39 agent 39 label A 39 steps sc
  • 解决vscode编写go代码时提示过慢

    vscode的自动代码提示 xff0c 发现太慢了 xff0c 隔3 xff0c 4秒才会出提示 xff0c 所以换为Google推荐的 gopls来代替 下载过程 方案一 打开 VS Code 的setting 搜索 go useLang
  • windows server安装OpenSSH server

    右键powershell 选择以管理员运行 powershell 查看可安装的OpenSSH server版本 xff0c 在PowerShell中执行命令如下所示 Get WindowsCapability Online Name Ope
  • 表白密码:I Love you的42种密码表白方式

    字母表白数字密码 xff1a 9121522521 表白解密 xff1a 从1开始到26 xff0c 分别表示从A到Z xff0c 即 xff1a A xff08 1 xff09 B xff08 2 xff09 C xff08 3 xff0
  • C语言字符串结束标志

    为了测定字符串的实际长度 xff0c C语言规定了一个字符串结束标志 0 作为标志 xff0c 如果一个字符串为9个 xff0c 前面九个都不为空字符 xff0c 那么第十个字符为 0 xff0c 也就是说在遇到字符 0 时 xff0c 表
  • 用体光栅作角度滤波器

    体光栅由于其高光谱灵敏性和角度灵敏度 xff0c 可以设计成光谱滤波器或角度滤波器 根据K Bang等人的工作 xff0c 我们在VirtualLab Fusion中构造了这样的体光栅 xff0c 并分析了它们的角度响应 与传统的采用4 f
  • linux网络编程(完整版)

    之间在网上看到很多网络编程都是一个一个demo xff0c 今天我把之前学到的汇总起来 xff0c 希望大家可以进行补充 我理解的网络通信分为4种 1 xff0c udp客户端 2 xff0c udp服务端 3 xff0c tcp客户端 4