socket编程:信号驱动式I/O实现

信号驱动式l/0的本质就是:进程预先告知内核当某个描述符发生事件时,内核会向该进程发送SIGIO信 号量通知进程,进程可在信号处理函数中进行处理。
进程可以通过fcntl打开O_ ASYNC标志ioctl打开FIOASYNC标志来通知内核,二者的区别是一些系统不支持fcntl,所以应尽量使用ioctl。
对于socket产生SIGIO的条件:
TCP套接字:

1.监听套接字上有新连接请求完成
2某个断连请求发起
3.某个断连请求完成
4.数据到达套接字
5数据已从套接字发送走(输出缓冲区有空闲空间)
6.发生某个异步错误

UDP套接字:

1.数据报到达套接字
2.套接字上发生异步错误

对于套接字而言: TCP套接字和UDP套接字致使内核产生SIGIO信号的条件有所不同,其中TCP可产生该信号的条件较多,而UDP套接字产生该信号的条件只有两个,因为我们无法得知具体是什么事件导致内核产生该信号。对于UDP套接字产生该信号条件的判断就简单的多,这也是信号驱动式l/0主要用于UDP套接字的原因
套接字使用信号驱动式I/O的步骤:
1、建立SIGIO信号处理函数
2、设置设置套接字属主(使用fcnt的F_ SETOWN命令)
3、开启信号驱动式/O(fcnt打开O_ ASYNC或ioct打T开FIOASYNC)
代码如下:
net_info.h

#ifndef _NET_INFO_H  
#define _NET_INFO_H
 
#define RCV_LEN 1024
#define LISTEN_PORT 9999
 
#endif

udp_client.c

#include <sys/types.h>        
#include <sys/socket.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <arpa/inet.h>  
#include <unistd.h>  
#include <time.h>  
#include <string.h>  
#include <sys/select.h>  
#include <sys/time.h>  
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include "net_info.h"
 
int main()
{
	int skfd;
	skfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (skfd < 0)
	{
		perror("socket error");
		exit(-1);
	}
 
	int ret;
	time_t tm;
	struct sockaddr_in desAddr;
	bzero(&desAddr, sizeof(desAddr));
	desAddr.sin_family = AF_INET;
	desAddr.sin_port = htons(LISTEN_PORT);
	desAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	
	while (1)
	{	
		time(&tm);
		ret = sendto(skfd, ctime(&tm), strlen(ctime(&tm)), 0,
					 (struct sockaddr*)&desAddr, sizeof(desAddr));
		if (ret < 0)
		{
			perror("");
			exit(-1);
		}
		sleep(2);
	}
 
	return 0;
}

udp_server.c

#include <sys/types.h>        
#include <sys/socket.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <arpa/inet.h>  
#include <unistd.h>  
#include <time.h>  
#include <string.h>  
#include <sys/select.h>  
#include <sys/time.h>  
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include "net_info.h"
 
static void sigio_handler(int signo);
void sigio_socket_init(int skfd);
 
char recvBuf[RCV_LEN];
int listenfd;
 
int create_socket()
{
	int skfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (skfd < 0)
		return -1;
	
	struct sockaddr_in saddr;
	bzero(&saddr, sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(LISTEN_PORT);
	saddr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	if (bind(skfd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0)
		return -1;
	
	return skfd;
}
 
int main()
{
    int run_cnt = 0;
	listenfd = create_socket();
	if (listenfd < 0)
	{
		perror("prepare error");
		exit(-1);
	}
	
	sigio_socket_init(listenfd);
	while(1)
    {
        printf("run_cnt=%d\n",run_cnt++);
        sleep(1);
    }
		
	return 0;
}
 
 
void sigio_socket_init(int skfd)
{
	int ret = 0;
 
	//1.建立SIGIO信号的处理函数
	signal(SIGIO, sigio_handler);
	
	//2.设置该套接字的属主
	ret = fcntl(skfd, F_SETOWN, getpid());
	if (ret < 0)
	{
		perror("fcntl error");
		exit(-1);
	}
	
	//3.开启该套接字的信号驱动式I/O
	int on = 1;
	ret = ioctl(skfd, FIOASYNC, &on);
	if (ret < 0)
	{
		perror("ioctl error");
		exit(-1);
	}
}
 
static void sigio_handler(int signo)
{
	if (signo != SIGIO)
		return;
    
	printf("recv SIGIO(%d) from kernel\n",signo);
    int i = 0;
    for(i = 0; i < 3; i++){
 
    	int ret = recvfrom(listenfd, recvBuf, sizeof(recvBuf),0, NULL,NULL);//当内核数据没有准备好,进程继续阻塞
    	if (ret < 0)
    	{
    		if (errno == EWOULDBLOCK)
    			return ;
            else
            {
    		    perror("recvfrom error");
    		    exit(-1);
            }
    	}			
 
    	printf("recv = %s\n",recvBuf);
    }
}

执行结果:
在这里插入图片描述
五种网络IO模型-阻塞I/O、非阻塞I/O、I/O多路复用、信号驱动I/O、异步I/O
推荐博客
(1)
https://blog.csdn.net/weixin_30920513/article/details/95158227
(2)
https://www.cnblogs.com/shengguorui/p/11949282.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值