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

13.4 Linux_网络编程_套接字属性

概述

什么是选项的级别:

socket中可以设置的属性种类很多,比如socke的选项、传输层TCP/UDP的选项、数据链路层的选项。这些选项在不同的层级,这就是选项的级别。常用级别及含义如下:

级别含义
SOL_SOCKET作用于套接字本身
IPPROTO_IP作用于IPv4协议
IPPROTO_TCP作用于流式套接字
IPPROTO_UDP作用于数据报套接字

SOL_SOCKET级别的常用选项:

IPPROTO_IP级别的常用选项:

相关函数

//获取套接字选项
int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
//设置套接字选项
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);

返回值:成功返回0,失败返回-1 

sockfd:套接字的文件描述符

level:选项的级别

optname:要获取/设置哪一个选项

optval:获取/设置的选项存放在哪

optlen:选项存放的空间的大小

保活连接实验

什么是保活链接:

保活链接指的是在TCP通信中,为了防止长时间不交互而连接被关闭的情况,服务器会向客户端发送一些数据,以确保连接正常。当到达设置的间隔时,会发出第一个保活数据,如果服务器没有收到客户端的回应,之后会继续发出。当到达设置的发送次数依旧没有得到回应,那么服务器就会中断与客户端的连接。

保活连接包括:是否开启、未通讯多久发送第一次保活数据、之后发送的间隔、发送几次未响应中断连接。与之相对应的选项如下:

含义选项级别选项设置值
是否开启保活连接SOL_SOCKETSO_KEEPALIVE1开启0关闭
未通讯多久发送第一次保活数据SOL_TCPTCP_KEEPIDLE

单位s

默认7200s(2h)

之后发送的间隔SOL_TCPTCP_KEEPINTVL

单位s

默认75s

发送几次未响应中断连接SOL_TCPTCP_KEEPCNT默认9次

server.c代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <netinet/tcp.h>

#define BACKLOG 5 				//最大接入客户端数量
int socket_init(char** argv);
void getKeepAlive(int fd);
void setKeepAlive(int fd);
int main(int argc ,char** argv){
 
	int fd;
	//判断参数有效性
	if(argc != 3){
		printf("param err\n");
		printf("%s<ip><port>\n",argv[0]);
		return -1;
	}
	printf("ip = %s\n",argv[1]);
	printf("port = %s\n",argv[2]);
	//初始化socket:TCP,IPv4,本地回环测试
	fd = socket_init(argv);
	//设置保活链接
	getKeepAlive(fd);
	setKeepAlive(fd);
	getKeepAlive(fd);
	//接受客户端链接
	int newFd;
	struct sockaddr_in newAddr;
	socklen_t newAddrlen;
	char buf[100] = {0};
	fd_set readfds,readfdsTmp;
	int i,nfds;
	//1.清空可读可写集合,并将服务器的fd添加进可读集合
	FD_ZERO(&readfds);
	FD_ZERO(&readfdsTmp);
	FD_SET(fd,&readfds);
	nfds = fd + BACKLOG + 1;
	while(1){
		readfdsTmp = readfds;
		//2.以阻塞方式监听全部文件描述符,如果有文件描述符可以写入,则返回
		if(select(nfds,&readfdsTmp,NULL,NULL,NULL) == -1){
			perror("select");
			exit(-1);
		}
		//3.根据监听到的文件描述符进行相应的操作
		if(FD_ISSET(fd,&readfdsTmp)){//监听到了服务器的fd,代表有新的客户端接入
			if((newFd = accept(fd,(struct sockaddr*)&newAddr,&newAddrlen)) < 0){
				perror("accept");
				return -1;
			}
			printf("[%s,%d]connect\n",inet_ntoa(newAddr.sin_addr),ntohs(newAddr.sin_port));
			printf("newFd = %d\n",newFd);
			FD_SET(newFd,&readfds);//将新的socket加入监听列表
		}
		else{
			//printf("Debug:fd = %d\n",fd);
			for(i=fd+1;i<nfds;i++){//监听到了客户端的fd,处理相应客户端信息,这里的难点是客户端fd的范围如何确定
				//printf("Debug:i=%d\n",i);
				if(FD_ISSET(i,&readfdsTmp)){
					if(read(i,buf,sizeof(buf)) <= 0){
						close(i);
						FD_CLR(i,&readfds);
						printf("fd=%d closed\n",i);
					}else{
						if(getpeername(i,(struct sockaddr*)&newAddr,&newAddrlen) == -1){//与客户端链接存在问题
							perror("getpeername");
						}else{
							printf("[%s,%d]data:%s",inet_ntoa(newAddr.sin_addr),ntohs(newAddr.sin_port),buf);
							write(i,"server\n",strlen("server\n"));
							memset(buf,0,sizeof(buf));
						}
					}
				}
			}	
		}
	}
	close(fd);
	return 0;
}

void setKeepAlive(int fd){
	int optval;
	optval = 1;
	if(setsockopt(fd,SOL_SOCKET,SO_KEEPALIVE,&optval,sizeof(int)) == -1){
		perror("SO_KEEPALIVE");
	}
	optval = 5;
	if(setsockopt(fd,SOL_TCP,TCP_KEEPIDLE,&optval,sizeof(socklen_t))){
		perror("TCP_KEEPIDLE");
	}
	optval = 2;
	if(setsockopt(fd,SOL_TCP,TCP_KEEPINTVL,&optval,sizeof(socklen_t))){
		perror("TCP_KEEPINTVL");
	}
	optval = 3;
	if(setsockopt(fd,SOL_TCP,TCP_KEEPCNT,&optval,sizeof(socklen_t))){
		perror("TCP_KEEPCNT");
	}

}
void getKeepAlive(int fd){
	int optval;
	socklen_t optlen;
	if(getsockopt(fd,SOL_SOCKET,SO_KEEPALIVE,&optval,&optlen) == -1){
		perror("get SO_KEEPALIVE");
	}else{
		printf("是否开启保活链接 = %d\n",optval);
	}
	if(getsockopt(fd,SOL_TCP,TCP_KEEPIDLE,&optval,&optlen) == -1){
		perror("get TCP_KEEPIDLE");
	}else{
		printf("未通讯多久发送第一次保活数据 = %ds\n",optval);
	}
	if(getsockopt(fd,SOL_TCP,TCP_KEEPINTVL,&optval,&optlen) == -1){
		perror("get TCP_KEEPINTVL");
	}else{
		printf("之后发送的间隔 = %d\n",optval);
	}
	if(getsockopt(fd,SOL_TCP,TCP_KEEPCNT,&optval,&optlen) == -1){
		perror("get TCP_KEEPCNT");
	}else{
		printf("发送几次未响应中断连接 = %d\n",optval);
	}
}
int socket_init(char** argv){
	int fd;
	struct sockaddr_in addr;
	//1.创建socket
	if((fd=socket(AF_INET,SOCK_STREAM,0))<0){//IPv4,TCP协议
		perror("socket");
		exit(-1);
	}
	//2.绑定IP、端口号
	addr.sin_family = AF_INET;    				  //IPv4
	addr.sin_port = htons(atoi(argv[2])); 		  //端口号,要转化为大端子节序
	addr.sin_addr.s_addr = inet_addr(argv[1]);    //IP地址:0表示在本网络上的本主机,即:自己
	if(bind(fd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in)) == -1){
		perror("bind");
		exit(-1);
	}
	//3.监听socket
	if(listen(fd,BACKLOG) == -1){ 	//允许最多接入5个客户端
		perror("listen");
		exit(-1);
	}
	return fd;
}

代码执行结果如下:


http://www.kler.cn/news/358747.html

相关文章:

  • 【设计模式系列】装饰器模式
  • 自动驾驶系列—CI在自动驾驶开发中的应用场景与实现详解
  • 说了很久的建筑转大模型,也不过是想在职场完成赎身
  • 深入理解Flutter鸿蒙next版本 中的Widget继承:使用extends获取数据与父类约束
  • SpringBoot框架的车辆管理自动化解决方案
  • 「漏洞复现」英飞达医学影像存档与通信系统 WebUserLogin.asmx 信息泄露漏洞
  • OpenCV的常用与形状形状描述相关函数及用法示例
  • VSCODE c++不能自动补全的问题
  • Qt- JSONXML
  • 高频SQL50题(基础版)三
  • 安全升级:索尔德三款产品获得防爆合格证
  • 知识付费专业知识讲解
  • springboot优质鸭梨的培育管理系统-计算机毕业设计源码92834
  • 音频/视频提取器:Python和moviepy实现
  • 基于正常重建的图像异常检测方法
  • CICD持续集成交付与持续交付
  • 【力扣打卡系列】滑动窗口与双指针(三数之和)
  • 有道在线翻译+4款新星,翻译从此无障碍,你get了吗?
  • FastDFS单节点部署
  • VS Code开发qt项目