当前位置: 首页 > news >正文

Linux下网络通信中的超时设置(C语言 客户端/服务器实现)

1. 网络超时检测

1. ​​超时设置​​:​​为某个操作(如连接、请求、响应等)设定一个最大的等待时间,超出这个时间没有得到预期结果,就认为操作失败或超时,从而触发相应的处理(如报错、重试、放弃等)

1. 在网络通信中,很多操作会使得进程阻塞,比如TCP套接字中的recv/accept/connect函数,UDP套接字中的recvfrom函数等

2. 超时检测的必要性:避免进程在没有数据时无限制的阻塞,当设定的时间到时,进程从原操作返回继续运行

3. 本文介绍两种超时设置的函数用来解决客户端/服务器中的阻塞:setsockopt()sigaction()

2. 方法1:setsockopt

(1)使用方法(简易流程)

 struct timeval {
long    tv_sec;         //单位秒
long    tv_usec;        //单位微妙
};

struct timeval tv = {2};        //设置超时时长2s
setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));      //设置接收超时
while (1) {
ret = recv(connfd, );     
if (ret < 0) {        //recv函数超时返回-1
continue;      //超时设置的关键语句,不会直接结束进程,而是让进程返回继续执行
}
}

(2)代码实现

这里用客户端/服务器的形式来展示超时设置效果

在服务器端server.c的代码中,我们在与客户端进行通信前,进行超时设置

以下是使用setsockopt函数进行超时设置时,服务器端的完整代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <fcntl.h>int main(int argc, char *argv[])
{ //1.创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("socket");return -1;}printf("socket create success\n");//2.绑定本机地址和端口struct sockaddr_in srvaddr;memset(&srvaddr, 0, sizeof(srvaddr));srvaddr.sin_family = AF_INET;srvaddr.sin_port = htons(60621);srvaddr.sin_addr.s_addr = inet_addr(INADDR_ANY);if(0 > bind(sockfd, (struct sockaddr *)&srvaddr, sizeof(srvaddr))){perror("bind");return -1;}printf("bind success\n");//3.设置监听套接字if(0 > listen(sockfd, 1)){perror("listen");return -1;}printf("listen success\n");//4.接收客户端的连接, 并生成通信套接字int connfd = accept(sockfd, NULL, NULL);if(0 > connfd){perror("accept");return -1;}printf("accept success\n");//超时设置struct timeval tv = {3};//设置接收超时setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));//5.与客户端通信:接收客户端的数据并打印int ret;char buf[1024];while(1){ret = recv(connfd, buf, sizeof(buf), 0);if(0 > ret){perror("recv");continue; //超时等待}else if(0 == ret){printf("server close\n");break;}printf("recv: %s\n", buf);}//6.关闭套接字close(sockfd);close(connfd);return 0;
} 

3. 方法2:sigaction

(1)使用方法(简易流程)

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
作用: 检查和改变信号动作
参数:
signum   --- 信号
act         --- 设置信号处理动作
old_act     --- 获取信号处理动作
struct sigaction {
void     (*sa_handler)(int);
void     (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t   sa_mask;
int        sa_flags;
void     (*sa_restorer)(void);
};
返回值:
    成功: 0
失败: -1, 并设置errno


例:设置定时器(timer), 捕捉SIGALRM信号

参考代码如下
void  handler(int signo)     {   return;  }       //一旦进程收到这个信号,执行完信号处理函数之后,下一个函数就会直接返回,不阻塞在那里
struct sigaction  act;
sigaction(SIGALRM, NULL, &act);   //获取信号原来的act
act.sa_handler = handler;
act.sa_flags &= ~SA_RESTART;  //自动重启
sigaction(SIGALRM, &act, NULL);  //设置信号新的act
while (1) {
alarm(5);
if (recv(,,,) < 0) ……
}

(2)代码实现

这里同样用客户端/服务器的形式来展示超时设置效果

在服务器端server.c的代码中,我们在与客户端进行通信前,进行超时设置

以下是使用sigaction函数进行超时设置时,服务器端的完整代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>void handler(int sig)
{printf("timeout...\n");
}
int main(int argc, char *argv[])
{ //1.创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("socket");return -1;}printf("socket create success\n");//2.绑定本机地址和端口struct sockaddr_in srvaddr;memset(&srvaddr, 0, sizeof(srvaddr));srvaddr.sin_family = AF_INET;srvaddr.sin_port = htons(60621);srvaddr.sin_addr.s_addr = inet_addr(INADDR_ANY);if(0 > bind(sockfd, (struct sockaddr *)&srvaddr, sizeof(srvaddr))){perror("bind");return -1;}printf("bind success\n");//3.设置监听套接字if(0 > listen(sockfd, 1)){perror("listen");return -1;}printf("listen success\n");//4.接收客户端的连接, 并生成通信套接字int connfd = accept(sockfd, NULL, NULL);if(0 > connfd){perror("accept");return -1;}printf("accept success\n");//超时设置struct sigaction act;sigaction(SIGALRM, NULL, &act);act.sa_handler = handler;act.sa_flags &= ~SA_RESTART;sigaction(SIGALRM, &act, NULL);//5.与客户端通信:接收客户端的数据并打印int ret;char buf[1024];while(1){alarm(3);  //设置超时时间为3sret = recv(connfd, buf, sizeof(buf), 0)if(0 > ret){perror("recv");continue; //超时等待}else if(0 == ret){printf("server close\n");break;}printf("recv: %s\n", buf);}//6.关闭套接字close(sockfd);close(connfd);return 0;
} 

4. 客户端的搭建

(1)客户端完整代码

这里提供与上方服务器端代码对应的客户端完整代码如下(以上两种超时设置方法对应的客户端代码相同)

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>int main(int argc, char *argv[])
{ //创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(0 > sockfd){perror("socket");return -1;}printf("socket create success\n");//主动连接服务器struct sockaddr_in srvaddr;memset(&srvaddr, 0, sizeof(srvaddr));srvaddr.sin_family = AF_INET;srvaddr.sin_port = htons(60621);srvaddr.sin_addr.s_addr = inet_addr("192.168.2.154");if(0 > connect(sockfd, (struct sockaddr *)&srvaddr, sizeof(srvaddr))){perror("connect");return -1;}//与服务器通信:从键盘输入字符串发送给服务器int ret;char buf[1024];while(1){printf("send: ");fgets(buf, sizeof(buf), stdin);buf[strlen(buf)-1] = '\0';if(0 > send(sockfd, buf, sizeof(buf), 0)){perror("send");break;}//设置客户端退出条件if(strcmp(buf, "exit") == 0)break;}//关闭套接字close(sockfd);return 0;
} 

(2)编译时注意事项

执行前的准备工作:

方法1

1. 我们在第一个终端编译 gcc server.c -o s  //重命名执行文件名

2. 在第二个终端编译 gcc client.c -o c

(为什么要重命名执行文件名?这是因为如果像平常一样进行编译,两个程序编译后生成同一个文件名(a.out),互相覆盖,无法同时执行,所以,通常我们会用 -o 指定不同的输出文件名)

3. 在第一个终端执行 ./s

4. 在第二个终端执行 ./c 

方法2

1. 创建一个makefile文件,在里面写入:

all:gcc server.c -o sgcc client.c -o c
clean:rm s c

2. 接下来与方法1相同,在两个终端上分别执行 ./s 和 ./c 即可

5. 总结

1. 对运行结果分析我们可以得知,在设置的超时时间到时,进程就会从原操作返回继续运行,而不是一直阻塞等待

2. 超时设置的好处:

    (1)设置了超时后,如果一定时间内没有响应,程序可以主动放弃等待,避免永久阻塞​​,保证程序能够继续运行或进行错误处理

    (2)当请求超时时,可以及时给用户反馈,比如提示 “ 请求超时,请重试 ”

    (3)超时后能及时释放连接、线程、内存等资源,让它们可以被其他请求复用,提升系统整体的​​资源利用率和并发能力

3. 在网络编程中,超时设置是不可或缺的核心机制之一,对其他网络编程知识感兴趣的同学,欢迎浏览主页其他相关文章!

感谢观看!如有疑问欢迎提出!

                                                     ----香菜小猫祝这位uu天天开心----

http://www.dtcms.com/a/419546.html

相关文章:

  • 高明建网站服务甘南州城乡建设局网站
  • 深圳建设工程交易网站官网网络销售是什么工作内容
  • 不停服务快速创建一个MySQL从库
  • C# 使用应用RSA和ECC进行数字签名和签名验证
  • 靖江做网站的网站建设可行性研究报告
  • 建设部网站怎么查询相关专业国家开发银行app下载
  • Java Web工程(不使用Spring框架)
  • Java:类和对象(二)
  • pytorch入门学习
  • AMD KFD的BO设计分析系列5-1:kgd_mem 实现详解
  • 大模型-Layer Normalization
  • 内存PE加载器:一种绕过EDR检测的无文件攻击技术
  • 做电商网站报价域名网站排名
  • 网站项目需要什么简洁大方网站建设
  • VideoMimic复现(1):环境搭建(real2sim+simulation)
  • 关于STM32单片机编程中大量使用全局变量而非使用函数调用的一些思考
  • pc端网站开发房地产网
  • nginx中root和alias
  • JMeter 执行流程
  • 网站开发设计与实现云南楚雄网
  • Go 语言中映射(Map)使用场景
  • Go 中实现“面向对象”
  • 富阳做网站广州专业做网站多少钱
  • 威海网站开发公司电话手机软件怎么做出来的
  • 企业系统有哪些南通网站流量优化
  • nginx 的root跟alias的区别
  • 到底什么是智能网联汽车??第三期——汽车总线及车载网络系统
  • 网站做跳转影响排名吗wordpress在线考试插件
  • 网站开发行业推广网站开发合同是否专属管辖
  • 网站建设招聘启事太原城市建设招标网站