您的位置:首页 > 理论基础 > 计算机网络

【linux高级程序设计】(第十五章)UDP网络编程应用 4

2015-08-06 18:44 746 查看

socket信号驱动

为了使一个套接字能够使用信号驱动I/O,至少需要以下3步操作。

1.安装SIGIO信号

2.套接字的拥有者设定为当前进程。因为SIGIO信号只会送到socket拥有者进程. 通过fcntl的F_SETOWN

3.套接字必须被允许使用异步I/O。 通过fcntl的F_SETFL,设置为O_ASYNC

在UDP通信中,下面情况会产生SIGIO信号



在TCP通信中,下面情况会产生SIGIO信号



例子:

下面的代码好奇怪,说是UDP的,但是发送接收用的是send, recv 而且客户端还跟服务器连接了;说是TCP,但是socket建立的时候用的是SOCK_DGRAM.

而且代码是可以跑通的。

服务器:

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<netinet/tcp.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/time.h>
#include<netdb.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/ioctl.h>
#define MAX_LENTH 1500

//针对SIGIO信号处理
static int nqueue = 0;
void sigio_handler(int signum)
{
if(signum == SIGIO)
nqueue++;
printf("signum = %d, nqueue = %d\n", signum, nqueue); //打印信号值
return;
}
static recv_buf[MAX_LENTH];

int main(int argc, char *argv[])
{
int sockfd, on = 1;
struct sigaction action;
sigset_t newmask, oldmask;
struct sockaddr_in ser_addr;
if(argc != 3)
{
printf("use: %s ip_add port\n", argv[0]);
exit(EXIT_FAILURE);
}

memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;  //使用IPv4
ser_addr.sin_port = htons(atoi(argv[2]));
if(inet_aton(argv[1], (struct in_addr *)&ser_addr.sin_addr.s_addr) == 0)
{
perror(argv[1]);
exit(EXIT_FAILURE);
}
//创建socket
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror("socket");
exit(EXIT_FAILURE);
}
//绑定IP地址
if(bind(sockfd, (struct sockaddr *)&ser_addr, sizeof(ser_addr)) == -1)
{
perror("bind");
exit(EXIT_FAILURE);
}
memset(&action, 0, sizeof(action));
action.sa_handler = sigio_handler;
action.sa_flags = 0;
//安装信号
sigaction(SIGIO, &action, NULL);
//设置socket拥有者
if(fcntl(sockfd, F_SETOWN, getpid()) == -1)
{
perror("fcntl F_SETOWN");
exit(EXIT_FAILURE);
}
//设置socket为信号驱动型
if(ioctl(sockfd, FIOASYNC, &on) == -1)
{
perror("ioctl FIOASYNC");
exit(EXIT_FAILURE);
}
sigemptyset(&oldmask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGIO);
printf("get ready\n");
while(1)
{
int len;
//设置当前阻塞的信号
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
//等待信号
while(nqueue == 0)
sigsuspend(&oldmask);
memset(recv_buf, '\0', MAX_LENTH);
//非阻塞接收数据
len = recv(sockfd, recv_buf, MAX_LENTH, MSG_DONTWAIT);
if(len == -1 && errno == EAGAIN)
nqueue = 0;
//修改进程阻塞的信号
sigprocmask(SIG_SETMASK, &oldmask, NULL);
if(len >= 0)
printf("recv %d byte, msg is %s\n", len, recv_buf);
}

}


客户端

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<netinet/tcp.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/time.h>
#include<netdb.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/ioctl.h>
#define MAX_LENTH 1500

int main(int argc, char *argv[])
{
struct sockaddr_in addr;
int sock_fd, ret;
char snd_buf[MAX_LENTH];
if(argc != 3)  //参数需要服务器的IP和端口
{
printf("use: %s ip_add port\n", argv[0]);
exit(EXIT_FAILURE);
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
if(inet_aton(argv[1], (struct in_addr *)&addr.sin_addr.s_addr) == 0)
{
perror(argv[1]);
exit(EXIT_FAILURE);
}
addr.sin_port = htons(atoi(argv[2]));
//创建socket
if((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror("socket");
exit(EXIT_FAILURE);
}
//向服务器发起连接 ??这不是TCP的么
if(ret = connect(sock_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
perror("connect");
exit(EXIT_FAILURE);
}
while(1)
{
printf("input msg to send:");
memset(snd_buf, '\0', MAX_LENTH);
fgets(snd_buf, MAX_LENTH - 1, stdin);
write(sock_fd, snd_buf, MAX_LENTH - 1);
}
}




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: