最新消息:XAMPP默认安装之后是很不安全的,我们只需要点击左方菜单的 "安全"选项,按照向导操作即可完成安全设置。

嵌入式linux网络编程04-信号驱动原理是什么?

XAMPP案例 admin 485浏览 0评论
每日心灵鸡汤:
重要的是维持一种认真保护自己的姿态。如果一味地只是遭受攻击不反抗,我们就只能止步不前。慢性的无力感是会腐蚀人的。–fq
es0008一、信号驱动。

1、信号驱动原理是什么?

就是使用了系统编程中信号的机制,首先让程序安装SIGIO的信号处理函数,通过监听文件描述符是否产生了SIGIO信号,我们就知道文件描述符有没有数据到达。如果有数据到达(小明这个客人来了),则系统就会产生了SIGIO信号(门铃响了),我们只需要在信号处理函数读取数据即可。

 

模型:

 

void fun(int sig)

{

//读取sockfd的数据即可。

}

 

signal(SIGIO,fun);  -> 只要将来收到SIGIO这个信号,就执行fun这个函数

sockfd   -> 只要有数据到达sockfd,就会产生一个SIGIO的信号

 

2、 信号驱动特点以及步骤。

特点:适用于UDP协议,不适用于TCP协议。

 

步骤一:由于不知道数据什么时候会到达,所以需要提前捕捉SIGIO信号。  -> signal()

步骤二:设置套接字的属主,其实就是告诉这个套接字对应的进程ID是谁。-> fcntl()

步骤三:给套接字添加信号触发模式。                               -> fcntl()

 

1)如何设置属主?  -> fcntl()  -> man 2 fcntl

 

#include <unistd.h>

#include <fcntl.h>

 

int fcntl(int fd, int cmd, … /* arg */ );

fd: 文件描述符

cmd:  F_GETFL (void)  -> 获取文件属性

F_SETFL (int)   -> 设置文件属性

F_SETOWN (int)  -> 设置属性     -> 最后一个参数要填,填进程ID号,一般都是getpid()。

 

返回值:

成功:文件描述符所有者

失败:-1

 

例如:fcntl(sockfd,F_SETOWN,getpid());  -> 让sockfd与进程ID绑定在一起。

 

2)如何添加信号触发模式?   -> fcntl()  -> man 2 fcntl

 

int state;

state = fcntl(sockfd,F_GETFL);

state |= O_ASYNC;

fcntl(sockfd,F_SETFL,state);

 

例题1:使用信号驱动IO模型写一个UDP协议服务器,实现监听多个客户端给我发送过来的消息。

 

#include “head.h”

 

int sockfd;

struct sockaddr_in cliaddr;

socklen_t len = sizeof(cliaddr);

 

void func(int sig)

{

//将sockfd中的数据读取出来。

char buf[100];

bzero(&cliaddr,sizeof(cliaddr));

bzero(buf,sizeof(buf));

recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&cliaddr,&len);

printf(“from cli:%s”,buf);

}

 

int main(int argc,char *argv[]) //  ./server 50001

{

//1. 创建UDP套接字

sockfd = socket(AF_INET,SOCK_DGRAM,0);

//2. 绑定IP地址

struct sockaddr_in srvaddr;

bzero(&srvaddr,sizeof(srvaddr));

srvaddr.sin_family = AF_INET;

srvaddr.sin_port = htons(atoi(argv[1]));

srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);

bind(sockfd,(struct sockaddr *)&srvaddr,len);

//3. 捕捉信号。

signal(SIGIO,func);

//4. 设置属主

fcntl(sockfd,F_SETOWN,getpid());

//5. 添加信号触发模式。

int state;

state = fcntl(sockfd,F_GETFL);

state |= O_ASYNC;

fcntl(sockfd,F_SETFL,state);

//6. 坐等各位给我写信就可以。

while(1)

pause();

return 0;

}

 

二、超时接收。

1、为什么会有超时接收?

一般地,我们都习惯使用阻塞IO,就是有数据就读取,没有数据就会一直阻塞等待。

因为有可能会出现一种情况,就是一直等待,都等不到结果,所以超时接收就是为了解决这个问题。

 

2、如何实现超时控制?  —   方法一:使用多路复用。

 

select(xx,xxx,xx,xx,NULL);    -> 无限等待集合中的数据。

-> 如果集合中的文件描述符有数据到达,则函数就会返回。

-> 如果集合中的文件描述符没有数据到达在,则函数就会一直阻塞。

 

 

每次select之前,都需要重置好时间。

select(xx,xxx,xx,xx,5S);     -> 只会在5S内阻塞等待,5S后就会返回。

-> 5S内有数据到达,则函数返回就绪的文件描述符个数。

-> 5S内没有数据到达,则5S内会阻塞。

-> 5S后,这个函数就会返回0,不会继续监听这个集合。

 

3、如何设置时间?

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

 

时间结构体:

struct timeval {

long    tv_sec;         //秒

long    tv_usec;        //微秒

};

 

例如:设置5S

struct timeval v;

v.tv_sec = 5;

v.tv_usec = 0;

 

select(xx,xx,xx,xx,&v);

 

例题2:写一个TCP协议服务器,使用select函数去监听客户端的消息,如果客户端在5S内没有数据到达,则打印一句”timeout”,如果有数据,就打印客户端所说的话。

 

#include “head.h”

 

void *func(void *arg)

{

int i;

for(i=0;i<1000;i++)

{

printf(“i = %d\n”,i);

sleep(1);

}

}

 

int main(int argc,char *argv[])  // ./rose 50000

{

//0. 创建一个线程,用于计算事件。

pthread_t tid;

pthread_create(&tid,NULL,func,NULL);

//1. 创建TCP套接字

int sockfd;

sockfd = socket(AF_INET,SOCK_STREAM,0);

printf(“sockfd = %d\n”,sockfd);

//2. 绑定IP地址,端口号

struct sockaddr_in srvaddr;

socklen_t len = sizeof(srvaddr);

bzero(&srvaddr,len);

srvaddr.sin_family = AF_INET;  //地址族

srvaddr.sin_port = htons(atoi(argv[1]));  //端口号

srvaddr.sin_addr.s_addr = htonl(INADDR_ANY); //IP地址  /usr/include/linux/in.h

bind(sockfd,(struct sockaddr *)&srvaddr,len);

//3. 设置监听套接字

//调用listen之前:sockfd -> 待连接的套接字

listen(sockfd,5);

//调用listen之后:sockfd -> 监听套接字

//4. 等待客户端的连接

struct sockaddr_in cliaddr;

int connfd;

connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&len);

if(connfd >= 0)

{

printf(“connfd = %d\n”,connfd);

printf(“new connection:%s\n”,inet_ntoa(cliaddr.sin_addr));

}

//5. 使用多路复用读取套接字上数据

fd_set set;

struct timeval v;

int ret;

char buf[100];

while(1)

{

FD_ZERO(&set);

FD_SET(connfd,&set);

v.tv_sec = 5;

v.tv_usec = 0;

ret = select(connfd+1,&set,NULL,NULL,&v);

if(ret == -1)

{

printf(“select error!\n”);

}

if(ret == 0)

{

printf(“timeout!\n”);

}

if(ret > 0)

{

bzero(buf,sizeof(buf));

recv(connfd,buf,sizeof(buf),0);

printf(“from jack:%s”,buf);

if(strncmp(buf,”fenshou”,7) == 0)

{

break;

}

}

}

//5. 阻塞读取套接字上的数据

/*

char buf[100];

while(1)

{

bzero(buf,sizeof(buf));

recv(connfd,buf,sizeof(buf),0);

printf(“from jack:%s”,buf);

if(strncmp(buf,”fenshou”,7) == 0)

{

break;

}

}

*/

//6. 回收TCP套接字的资源

close(sockfd);

close(connfd);

return 0;

}

 

练习3:修改test2/,如果在10S内,没有数据到,则打印timeout。

 

4、 如何实现超时接收?–  方法二:设置套接字的属性为超时接收。

1)机制如何?

如果不给套接字设置属性,那么读取套接字上的数据时就会无限等待             -> 一直阻塞。

如果设置超时接收属性给套接字,那么读取套接字数据时,就会有时间的限制。    -> 前一段时间阻塞,时间过了就会返回。

 

2)操作步骤?

阻塞情况:

connfd = accept(sockfd);  -> 一直阻塞。

recv(connfd);             -> 一直阻塞等待数据的到达。

 

connfd = accept(sockfd);  -> 一直阻塞。

设置超时接收属性给connfd

recv(connfd);             -> 在规定时间内,就会阻塞读取,超过了时间,这个recv函数就会返回失败。

 

3)如何设置超时接收属性给套接字?  -> setsockopt()  ->  man 2 setsockopt

 

#include <sys/types.h>          /* See NOTES */

#include <sys/socket.h>

 

int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);

 

sockfd:套接字

level:优先级

SOL_SOCKET:套接字

IPPROTO_IP:IP优先级

IPPRO_TCP:TCP优先级

optname:选项名字

optval:值,使能为1,不使能为0

optlen:值类型大小

 

返回值:

成功:0

失败:-1

 

例子:

struct timeval v;

v.tv_sec = 5;

v.tv_usec = 0;

 

setsockopt(connfd,SOL_SOCKET,SO_RCVTIMEO,&v,sizeof(v));

 

接下来,再去读取connfd的数据,前5S就会阻塞,过了5S就会返回失败了。

 

练习4:写一个TCP服务器,使用recv函数接收客户端的消息,如果服务器在5S内,没有数据到达,就打印timeout。

 

#include “head.h”

 

void *func(void *arg)

{

int i;

for(i=0;i<1000;i++)

{

printf(“i = %d\n”,i);

sleep(1);

}

}

 

int main(int argc,char *argv[])  // ./rose 50000

{

//0. 创建线程

pthread_t tid;

pthread_create(&tid,NULL,func,NULL);

//1. 创建TCP套接字

int sockfd;

sockfd = socket(AF_INET,SOCK_STREAM,0);

printf(“sockfd = %d\n”,sockfd);

//2. 绑定IP地址,端口号

struct sockaddr_in srvaddr;

socklen_t len = sizeof(srvaddr);

bzero(&srvaddr,len);

srvaddr.sin_family = AF_INET;  //地址族

srvaddr.sin_port = htons(atoi(argv[1]));  //端口号

srvaddr.sin_addr.s_addr = htonl(INADDR_ANY); //IP地址  /usr/include/linux/in.h

bind(sockfd,(struct sockaddr *)&srvaddr,len);

//3. 设置监听套接字

//调用listen之前:sockfd -> 待连接的套接字

listen(sockfd,5);

//调用listen之后:sockfd -> 监听套接字

//4. 等待客户端的连接

struct sockaddr_in cliaddr;

int connfd;

connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&len);

if(connfd >= 0)

{

printf(“connfd = %d\n”,connfd);

printf(“new connection:%s\n”,inet_ntoa(cliaddr.sin_addr));

}

//5. 设置超时属性给connfd。

struct timeval v;

v.tv_sec = 5;

v.tv_usec = 0;

setsockopt(connfd,SOL_SOCKET,SO_RCVTIMEO,&v,sizeof(v));

//6. 不断读取connfd的内容。

char buf[100] = {0};

while(1)

{

bzero(buf,sizeof(buf));

if(recv(connfd,buf,sizeof(buf),0) >= 0) //有数据到达

{

printf(“from client:%s”,buf);

}

else{  //返回失败,说明5S内都没有数据到达,即超时。

printf(“timeout!\n”);

}

if(strncmp(buf,”quit”,4) == 0)

{

break;

}

}

close(connfd);

close(sockfd);

return 0;

}

转载请注明:XAMPP中文组官网 » 嵌入式linux网络编程04-信号驱动原理是什么?

您必须 登录 才能发表评论!