网络编程(五)

news2024/12/25 18:10:47

网络编程(五)

  • 网络服务器超时检测
    • 使用select进行超时检测
    • 套接字属性
      • **getsockopt:获取socket软通道的某项属性值**
      • setsockopt:设置socket软通道的某项属性值**(socket建立之后就可使用)
    • 信号
      • **signal():信号处理函数===set**
      • **sigaction():信号处理函数===set/get**

网络服务器超时检测

在网络通信中,很多操作会使得进程阻塞
TCP套接字中的recv/accept/connect
UDP套接字中的recvfrom
超时检测的必要性:
1、避免进程在没有数据时无限制地阻塞
2、当设定的时间到时,进程从原操作返回继续运行

使用select进行超时检测

如果该服务器使用select实现的,可以使用select的最后一个参数做超时检测

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
//参数1:需要监控的最大文件描述符的个数,总数
//参数2:指针指向读文件描述符集合表
//参数3:指针指向写文件描述符集合表
//参数4:指针指向异常文件描述符集合表
//参数5:① ② ③
//①NULL—>将select变成阻塞函数
//②0—>仅仅检测文件描述符的状态,然后立即返回,不会置位
//③时间值—>表示select的监控超时时间。时间到达之前select监控到有IO通道活跃,select返回通道路数并置位;超时时间到,select没有监控到任何IO通道有数据,那么select返回0
//返回值:成功返回监控到的有数据发生的通道个数;没有活跃则没有返回,则一直阻塞,失败返回-1

*第四个参数struct timeval timeout
在这里插入图片描述
在这里插入图片描述

套接字属性

socket—>有socket层,IP层,tcp/udp层属性

getsockopt:获取socket软通道的某项属性值

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
//参数1:文件描述符–>标识一个网络软通道
//参数2:level指定控制套接字的层次(SOL_SOCKET,IPPROTO_TCP,IPPROTO_IP)
//参数3:要设置的属性项,根据参数2决定属性项
//参数4:参数3对应的属性值的指针
//参数5:指向参数4大小的指针

setsockopt:设置socket软通道的某项属性值**(socket建立之后就可使用)

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
//参数1:文件描述符–>标识一个网络软通道
//参数2:level指定控制套接字的层次(SOL_SOCKET,IPPROTO_TCP,IPPROTO_IP)
//参数3:要设置的属性项,根据参数2决定属性项
//参数4:参数3对应的属性值的指针
//参数5:指向参数4大小
//返回值:成功返回0,失败返回-1,并更新errno

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//超时检测发送方代码
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
int main(int argc, const char *argv[])
{
	//1,创建套接字
	int socketFd = socket(AF_INET, SOCK_DGRAM, 0);
	if(socketFd < 0){
		perror("socket udp client error");
		return -1;
	}
	printf("socket udp client ok!\n");
	//2,设置服务器的IP 地址和端口
	struct sockaddr_in addr;
	bzero(&addr, sizeof(addr));
	//赋值
	addr.sin_family = AF_INET;
	addr.sin_port = htons(8888);//接收方的端口
	addr.sin_addr.s_addr = inet_addr("224.0.2.88");//组播地址
	//允许组播
	int opt = 1;
	setsockopt(socketFd, IPPROTO_IP, IP_MULTICAST_IF, &opt, sizeof(opt));
	//3,通信
	char buf[256] = {0};
	while(1)
	{
		bzero(buf, sizeof(buf));
		printf("推送一条组播消息:");
		fgets(buf, sizeof(buf), stdin);//从标准输入流中获取数据
		if(0 == strncasecmp("quit", buf, 4))
		{
			printf("结束!\n");
			bzero(buf, sizeof(buf));
			sendto(socketFd, buf, strlen(buf), 0, \
			(struct sockaddr *)&addr, sizeof(addr));
			break;
		}
	//发送消息
		sendto(socketFd, buf, strlen(buf), 0, \
		(struct sockaddr *)&addr, sizeof(addr));
		printf("send multicast message ok!\n");
	}
	//关闭套接字
	close(socketFd);
	return 0;
}
//超时检测接收方代码
recvfrom进行超时检测
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
	//1,创建套接字
	int socketFd = socket(AF_INET, SOCK_DGRAM, 0);
	printf("socket server ok!\n");
	//2,绑定接收方的IP 地址和端口
	struct sockaddr_in addr;
	bzero(&addr,sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(8888);//指定的接收方端口,必须和发送方一样
	addr.sin_addr.s_addr = htonl(INADDR_ANY);//任意地址
	bind(socketFd, (struct sockaddr *)&addr, sizeof(addr));
	printf("bind ok!\n");
	//3,加入到多播组中
	struct ip_mreq ipaddr;
	bzero(&ipaddr, sizeof(ipaddr));
	ipaddr.imr_multiaddr.s_addr = inet_addr("224.0.2.88");
	ipaddr.imr_interface.s_addr = htonl(INADDR_ANY);
	setsockopt(socketFd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &ipaddr,
	sizeof(ipaddr));
	char buf[256] = {0};
	//4,接收多播组中的消息
	//定义存储对方地址信息的结构变量
	struct sockaddr_in otherAddr;
	bzero(&otherAddr, sizeof(otherAddr));
	int len = sizeof(otherAddr);
	while(1)
	{
		bzero(buf, sizeof(buf));
		//设置超时检测
		struct timeval timeout;
		bzero(&timeout, sizeof(timeout));
		timeout.tv_sec = 3;
		//设置
		setsockopt(socketFd, SOL_SOCKET, SO_RCVTIMEO,
		&timeout,sizeof(timeout));
		//注意:udp服务器一定要存储发送方的地址信息(没有建立过连接,每一次
		//的发送,都需要知道对方的地址信息!!!)
		int ret = recvfrom(socketFd, buf, sizeof(buf), 0, \
		(struct sockaddr *)&otherAddr, &len);
		if(ret < 0)
		{
			//判断错误码是否为EAGAIN
			if(errno == EAGAIN)
			{
				printf("已超时3秒...");
			}
			else
			{
				perror("recvfrom error");
				break;
			}
		}
		else if(0 == ret){
			printf("组中停止消息的发送~\n");
			break;
		}
		else
		{
			printf("接收的组播消息:%s\n",buf);
		}
	}
	//5,离开多播组
	setsockopt(socketFd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &ipaddr,
	sizeof(ipaddr));
	//6,关闭套接字
	close(socketFd);
	return 0;
}

信号

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//信号处理函数:
void handler_func(int signum)
{
;
}
//信号行为结构体
struct sigaction {
void (*sa_handler)(int);//信号处理函数,和signal一样的
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;//标志,可以设置为取消自重启属性
void (*sa_restorer)(void);
};

更改信号默认操作:

signal():信号处理函数===set

signal函数原型:
#include <signal.h>
void (*signal(int signum, void (*handler)(int)))(int);
优化原型:看的更清楚。
//给函数指针类型取别名
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

功能:进行信号安装
参数:
参数1:需要安装的信号名称
参数2:对于该信号的处理函数(该函数是用户自定义的,且返回值类型void ,形参是一个int的函数的地址)
PS:参数2的位置可以传入:忽略(SIG_IGN) 执行缺省操作(SIG_DFL),信号处理函数的地址
返回值:成功安装完毕的信号处理函数的地址。

sigaction():信号处理函数===set/get

sigaction(信号,处理动作结构,处理动作结构)
① sigaction(SIGALRM, NULL, &act) //获取SIGALRM信号对应的默认处理信息结构
① sigaction(SIGALRM, &act, NULL)//设置SIGALRM信号对应的新的处理信息结构

在这里插入图片描述
在这里插入图片描述

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
//参数:秒数
//当定时器指定的时间到了时,它就向进程发送SIGALARM信号,信号的默认操作是结束进程.
//注意:每个进程只能有一个闹钟时间。如果在调用alarm时,以前已为该进程设置过闹钟时间,而且它还没有超时,则该闹钟时间的剩余时间值作为本次alarm函数调用的值返回,以前登记的闹钟时间则被新值代换。如果有以前登记的尚未超过的闹钟时间,而新设的闹钟时间值为0,则取消以前的闹钟时间,其剩余时间值仍作为函数的返回值。

在这里插入图片描述
在这里插入图片描述

//alarm定时器接收方超时检测代码
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#include <errno.h>
#include <signal.h>
//信号处理函数
void handler_func(int signum)
{
	printf("已超时...\n");
}
int main(int argc, const char *argv[])
{
	//1,创建套接字
	int socketFd = socket(AF_INET, SOCK_DGRAM, 0);
	printf("socket server ok!\n");
	//2,绑定接收方的IP 地址和端口
	struct sockaddr_in addr;
	bzero(&addr,sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(8888);//指定的接收方端口,必须和发送方一样
	addr.sin_addr.s_addr = htonl(INADDR_ANY);//任意地址
	bind(socketFd, (struct sockaddr *)&addr, sizeof(addr));
	printf("bind ok!\n");
	//3,加入到多播组中
	struct ip_mreq ipaddr;
	bzero(&ipaddr, sizeof(ipaddr));
	ipaddr.imr_multiaddr.s_addr = inet_addr("224.0.2.88");
	ipaddr.imr_interface.s_addr = htonl(INADDR_ANY);
	setsockopt(socketFd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &ipaddr,
	sizeof(ipaddr));
	char buf[256] = {0};
	//4,接收多播组中的消息
	//定义存储对方地址信息的结构变量
	struct sockaddr_in otherAddr;
	bzero(&otherAddr, sizeof(otherAddr));
	int len = sizeof(otherAddr);
	//第一步:先获取SIGALRM信号的旧行为
	struct sigaction act;
	bzero(&act, sizeof(act));
	sigaction(SIGALRM, NULL, &act);
	//第二步:设置处理函数+取消recvfrom的自重启属性
	act.sa_handler = &handler_func;
	//act.sa_flags |= SA_RESTART;//启动函数的自重启属性:eg:recvfrom:自重启属性再次陷入阻塞
	act.sa_flags &= ~SA_RESTART;//取消recvfrom再次陷入阻塞
	//第三步:将设置完毕的新行为设置给SIGALRM信号
	sigaction(SIGALRM, &act, NULL);
	while(1)
	{
		bzero(buf, sizeof(buf));
		//使用sigaction函数捕捉SIGALRM信号,给他设置一个处理函数和
		//取消recv函数自带的自重启属性
		//设置超时检测
		alarm(5);//秒到了,会产生SIGALRM信号
		//注意:udp服务器一定要存储发送方的地址信息(没有建立过连接,每一次
		//的发送,都需要知道对方的地址信息!!!)
		int ret = recvfrom(socketFd, buf, sizeof(buf), 0, \
		(struct sockaddr *)&otherAddr, &len);
		if(ret < 0)
		{
			perror("recvfrom error");//perror:interrupt syatem call-->中断系统调用
		//break;加了break此时会直接跳出while循环,不加则继续进行等待接收(超时检
		测)
		}
		else if(0 == ret){
			printf("组中停止消息的发送~\n");
			break;
		}
		else
		{
			printf("接收的组播消息:%s\n",buf);
		}
	}
	//5,离开多播组
	setsockopt(socketFd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &ipaddr,
	sizeof(ipaddr));
	//6,关闭套接字
	close(socketFd);
	return 0;
}

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

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

相关文章

【Python】搭配 Python 环境(超详细教程)

要想能够进行 Python 开发&#xff0c;就需要搭建好 Python 的环境。 需要安装的环境主要是两个部分&#xff1a; 运行环境&#xff1a;Python 开发环境&#xff1a;PyCharm 一、安装 Python 1、找到官方网站 Welcome to Python.org 在搜索引擎中搜索 python 关键字&…

【WP】猿人学13_入门级cookie

https://match.yuanrenxue.cn/match/13 抓包分析 抓包分析发现加密参数是cookie中有一个yuanrenxue_cookie 当cookie过期的时候&#xff0c;就会重新给match/13发包&#xff0c;这个包返回一段js代码&#xff0c;应该是生成cookie的 <script>document.cookie(y)(u)(a…

Linux环境---在线安装jdk

Linux环境—在线安装jdk 一、使用步骤 1.安装环境 JDK版本&#xff1a;1.8 1.1 建立存放软件的目录 注意&#xff1a;此处本人是将需要按照的软件存放在directory目录下&#xff0c;可根据实际情况调整接收路径。 命令如下&#xff1a; mkdir directory2.安装jdk 2.1 建…

vscode设置代码自动换行显示

☆ 问题描述 vscode设置代码自动换行显示 ★ 解决方案 ✅ 总结

深入理解mysql中的各种超时属性

1. 前言 connectTimeout: 连接超时 loginTimeout: 登录超时 socketTimeout: Socket网络超时&#xff0c;即读超时 queryTimeout: sql执行超时 transactionTimeout:spring事务超时 innodb_lock_wait_timeout:innodb锁等待超时 wait_timeout:非交互式连接关闭前的等待时间 inter…

TMC5160步进电机驱动芯片

TMC5160步进电机驱动芯片 特点和优势静止态自动降电流stealthChop2 & spreadCycle 驱动stealthChop2 & spreadCycle 驱动1.7 stallGuard2 – 机械负载传感1.8 coolStep –负载自适应电流控制1.9 dcStep –负载相关速度控制1.10 编码器接口 TMC5160工作模式模式 1&#…

wireshark 二次开发

一、 Windows 准备 1、源代码下载 Git&#xff1a;https://github.com/wireshark/wireshark 2、 准备Visual C 要编译wireshark&#xff0c;开发电脑上应该安装了Visual Studio并包括了Visual C&#xff0c;请至少安装Visual Studio 2010以减少不必要的麻烦。 visual studio …

Centos Stream 10 测试版下载:未来的RHEL10Rocky Linux 10

简介 最近发现Centos最放出了Stream 10 测试版本&#xff0c;应该是基于Fedora 40构建的。未来红帽会基于此版本构建RHEL 10。 内核版本&#xff1a;6.9.0 Python版本&#xff1a;3.12.2 RHEL系发行版对应关系 Fedora (根发行版-软件实时更新-只支持一年) >>某一版本作…

MySQL换路径(文件夹)

#MySQL作为免费数据库很受欢迎&#xff0c;即使公司没有使用&#xff0c;自己也可以用。它是一个服务&#xff0c;在点击CtrlAltDelete选择任务管理器后&#xff0c;它在服务那个归类里。 经常整理计算机磁盘分类的小伙伴&#xff0c;如果你们安装了MySQL&#xff0c;并且想移…

Docker的部署与基本使用

Docker的部署和基本使用 Docker是一个开源的容器化平台&#xff0c;它允许开发者将应用程序及其依赖项打包成独立的、可移植的容器&#xff0c;从而简化了应用程序的部署、管理和扩展过程。这些容器可以在任何支持Docker的平台上运行&#xff0c;确保了应用的一致性和可移植性…

pdf的压缩该怎么做?快速在线压缩pdf的方法

pdf文件是现在很常用的一种文件格式&#xff0c;有很多的文件内容都可以通过这种格式来展示内容&#xff0c;比如一些通知文件、设计图、个人信息等等&#xff0c;文件的内容越多就会越大&#xff0c;在使用的时候经常会受到一定的限制。那么有什么方法能够快速的将pdf文件变小…

Vue3项目炫酷实战,检测密码强度值

在前端项目开发中&#xff0c;确保用户密码的强度是保护账户安全的重要措施。本文将演示如何使用Vue 3实现一个简单的密码强度检测功能。通过实时反馈&#xff0c;帮助用户创建更安全的密码&#xff0c;从而提升整体系统的安全性。无论您是前端开发新手还是经验丰富的开发者&am…

人大金仓数据库大小写敏感查看

V8R3版本检查方法&#xff1a; 执行语句 show case_sensitive; 返回结果 on&#xff1a;表示大小写敏感&#xff1b; 返回结果 off&#xff1a;表示大小写不敏感。 V8R6版本检查方法&#xff1a; 执行语句 show enable_ci; 返回结果 on&#xff1a;表示大小写不敏感&#x…

Django 创建项目及应用

1&#xff0c;安装 Django pip install Django3.1.5 2&#xff0c;创建 Django项目 django-admin startproject myshop 3&#xff0c;创建 Django应用 python manage.py startapp app1 4&#xff0c;启动 Django项目 python .\manage.py runserver 到这里项目及应用创建…

git报错解决方法error: remote origin already exists.

有时想添加远程本地仓库和远程公司仓库&#xff0c;但git remote的时候发现关联的是一样的&#xff0c;你再去关联时会报错&#xff0c;这时候你应该清除你想关联的远程仓库&#xff0c;再次连接就可以了 下面这个错误提示是远程源已经存在 现在你可以这样做 1、查看远程库的信…

2024-前端面试的正确打开方式(GitHub火爆场景题剖析)

写在前面 最近前端面试大家有没有感觉到场景题的压迫感&#xff01;&#xff01;&#xff01; 很显然普通面试八股不会怎么更新&#xff0c;而且就前端来说&#xff0c;面试并不是真正困难的&#xff0c;常规八股显示不出面试者的技术水平。 前端作为一个技术行业&#xff0c…

在vscode运行github的命令

查看当前分支列表 git branch 查看当前分支列表 git branch 如果没有 main 分支&#xff0c;则创建并切换到 main 分支 git checkout -b main 添加和提交所有更改&#xff08;如果有未提交的更改&#xff09; git add . git commit -m “Initial commit” 推送 main 分…

数据结构———链表

链表是经常用到的一种基础数据结构&#xff0c;接下来我们讲讲链表。 链表&#xff1a; 特点&#xff1a; 链表可分为有头/无头链表&#xff0c;循环/无环&#xff0c;双向/单向链表&#xff0c;每个链表节点都包含一个数据和下一个链表节点的地址。 每个链表节点都指向下一…

ESP32开发:2、使用Clion+IDF框架新建ESP32工程

文章目录 背景步骤新建工程编译工程下载代码 参考 背景 使用CLIONIDF框架新建ESP32工程。编译工程&#xff0c;并配置下载。首先需要根据教程1、安装好IDF框架&#xff0c;参考如下&#xff1a; IDF环境搭建 步骤 新建工程 首先找到IDF框架安装路径&#xff0c;我这里的如下…

IMX6ULL-UBOOT外设适配

目录 1.网口移植 2.LCD移植 1.网口移植 100ask 有两个网口,uboot阶段使用网口2,对应的phy是LAN8720A,硬件地址是1,RST引脚对应的是GPIO6_IO6