您的位置:首页 > 运维架构 > Linux

【Linux编程】用select处理普通数据和带外数据

2015-12-06 21:07 447 查看
        传输层的TCP协议有带外数据的概念,带外数据又称为紧急数据,它比普通数据有更高的优先级,一般会立即发送,而不会排队等待。

        在TCP协议头部结构中有URG标志位和16位的紧急指针,若URG标志位被设置,表示紧急指针有效,此时紧急指针将指向紧急数据的下一个字节。

        带外数据只有一个字节大小,因为服务器将读取到的带外数据存入一个特殊的缓冲区,这个缓冲区只有一个字节的大小,并且带外数据会将TCP字节流截断,可以用带MSG_OOB标志的recv系统调用来读取带外数据,

        在网络编程中,select能监听可读,可写和异常事件,但能监听的异常事件只有一种,那就是socket上接收到带外数据。socket上接收到普通数据和带外数据都将使select返回,但前者处于可读状态,后者处于异常状态。

        用select处理普通数据和带外数据的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>

int main(int argc, char *argv[]){

if(argc!=3) printf("格式:%s <IP地址> <端口号>\n", argv[0]), exit(-1);

int servfd = socket(AF_INET, SOCK_STREAM, 0);
if(servfd==-1) printf("socket 失败!:%m\n"), exit(-1);
printf("socket 成功!\n");

/*封装serv端ip地址和端口号*/
const char *ip = argv[1];
int port = atoi(argv[2]);
struct sockaddr_in serv;
serv.sin_family = AF_INET;
serv.sin_port = htons(port);
inet_pton(AF_INET, ip, &serv.sin_addr);

int r = bind(servfd, (struct sockaddr*)*serv, sizeof(serv));
if(r==-1) printf("bind 失败!:%m\n"), exit(-1);
printf("bind 成功!\n");

r = listen(servfd, 5);
if(r==-1) printf("listen 失败!:%m\n"), exit(-1);
printf("listen 成功!\n");

/*用于封装连接到的cli端ip地址和端口号*/
struct sockaddr_in cli;
socklen_t cli_len = sizeof(cli);

int clifd = accept(servfd, (struct sockaddr*)&cli, &cli_len);
if(clifd==-1) printf("accept 失败!:%m\n"), exit(-1);
printf("accept 成功!\n");

/*定义可读描述符集合和异常描述符集合,以及用户接收缓冲区*/
fd_set readfd, exceptfd;
char buf[1024];

while(1){
/*每次循环前清空readfd和exceptfd,然后在这两个描述符集合中重新设置clifd描述符*/
FD_ZERO(&readfd);
FD_ZERO(&exceptfd);
FD_SET(clifd, &readfd);
FD_SET(clifd, &exceptfd);
memset(buf, 0, sizeof(buf));

r = select(clifd+1, &readfd, NULL, &exceptfd, NULL);
if(r==-1) printf("select 失败!:%m\n"), exit(-1);

if(FD_ISSET(clifd, &readfd)){
r = recv(clifd, buf, sizeof(buf), 0);
if(r==-1) printf("recv普通数据出错!:%m\n"), exit(-1);
printf("收到%d字节普通数据:%s\n", r, buf);
}

/*接收带外数据需要用MSG_OOB标志*/
if(FD_ISSET(clifd, &exceptfd)){
r = recv(clifd, buf, sizeof(buf), MSG_OOB);
if(r==-1) printf("recv带外数据出错!:%m\n"), exit(-1);
printf("收到%d字节带外数据:%s\n", r, buf);
}
}

close(clifd);
close(servfd);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息