首页 > 代码库 > udp套接字使用信号驱动式I/O

udp套接字使用信号驱动式I/O

信号驱动式I/O的本质就是:进程预先告知内核当某个描述符发生事件时,内核会向该进程发送SIGIO信号通知进程,进程可在信号处理函数中进行处理


进程可以通过fcntl打开O_ASYNC标志或ioctl打开FIOASYNC标志来通知内核,二者的区别是一些系统不支持fcntl,所以应尽量使用ioctl


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


对于UDP套接字产生SIGIO信号的条件:
1.数据报到达套接字
2.套接字上发生异步错误


对于套接字而言:TCP套接字和UDP套接字致使内核产生SIGIO信号的条件有所不同,其中TCP可产生该信号的条件较多,而UDP套接字产生该信号的条件只有两个,因为我们无法得知具体是什么事件导致内核产生该信号。,对于UDP套接字产生该信号条件的判断就简单的多,这也是信号驱动式I/O主要用于UDP套接字的原因。


套接字使用信号驱动式I/O的步骤:
1.建立SIGIO信号处理函数
2.设置设置套接字属主(使用fcntl的F_SETOWN命令)
3.开启信号驱动式I/O(fcntl打开O_ASYNC 或 ioctl打开FIOASYNC)


实例:
客户每隔1秒钟将系统时间发送到服务器,服务器通过SIGIO信号处理函数中接收数据


net.h

#ifndef MY_NET_H  
#define MY_NET_H  
      
#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 <iostream>
#include <sys/stat.h>
#include <fcntl.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>

using namespace std;

char recvBuf[1025];
int listenfd;

static void sigio_handler(int signo)
{
	if (signo != SIGIO)
		return;
	
	int ret;
	while (1)
	{
		ret = recvfrom(listenfd, recvBuf, sizeof(recvBuf),
				   0, NULL, NULL);
		if (ret < 0)
		{
			if (errno == EWOULDBLOCK)
				break;
				
			perror("recvfrom error");
			exit(-1);
		}			
	
		cout << recvBuf << endl;
	}
}

void init(int skfd)
{
	int ret;

	//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);
	}
}

int prepare()
{
	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(9999);
	saddr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	if (bind(skfd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0)
		return -1;
	
	return skfd;
}

#endif


客户:

#include "net.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(9999);
	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(1);
	}

	return 0;
}

服务器:

#include "net.h"

int main()
{
	listenfd = prepare();
	if (listenfd < 0)
	{
		perror("prepare error");
		exit(-1);
	}
	
	init(listenfd);
	while(1);
		
	return 0;
}





udp套接字使用信号驱动式I/O