TCP带外数据

TCP带外数据特点:
1,该数据有两种接收方式:
1.默认方式:通过单独缓冲区接收(不是接受端接受其他数据的缓冲区),当带外数据到达时,内核会向接收套接字宿主进程发送SIGURG信号
2.设置为“带内数据”:通过接收端调用setsockopt打开SO_IOOBNLINE选项就可以将带外数据放进”带内缓冲区“,但也可以通过sockatmark判断”带内缓冲区“的位置(每个TCP连接最多只有一个带外标记)
2.当TCP套接字接收到带外数据时,select会返回设置异常集(编程时可利用这一点)

采用SIGURG通知接收端接收带外数据:

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/signal.h>
#include <fcntl.h>

#define BUFSIZE 4096
#define LISTENQ 100
int connfd;
void sig_urg(int signo) {
    ssize_t n;
    char buf[BUFSIZE];
    if ((n = recv(connfd,buf,BUFSIZE - 1,MSG_OOB)) != n) {
        printf("recv from peer error: %s\n",strerror(errno));
        exit(1);
    }
    buf[n] = 0;
    printf("receive %ld bytes from peer: %s\n",n,buf);
}
int main(int argc,char** argv) {
    if (argc != 2) {
        printf("please add or check <ip-address or host-name> <service-name or port>\n");
        exit(1);
    }

    struct addrinfo hints;
    bzero(&hints,sizeof(struct addrinfo));
    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    struct addrinfo* results;
    int err;
    if ((err = getaddrinfo(NULL,argv[1],&hints,&results)) != 0) {
        printf("getaddrinfo error: %s\n",gai_strerror(err));
        exit(1);
    }

    struct addrinfo* dummy = results;
    int sockfd;
    for (; dummy != NULL; dummy = dummy->ai_next) {
        if ((sockfd = socket(dummy->ai_family,dummy->ai_socktype,dummy->ai_protocol)) < 0) {
            continue;
        }
        if (bind(sockfd,dummy->ai_addr,dummy->ai_addrlen) == 0) {
            break;
        }
        close(sockfd);
    }
    if (listen(sockfd,LISTENQ) < 0) {
        printf("listen error: %s\n",strerror(errno));
        exit(1);
    }
    char buf[BUFSIZE];
    ssize_t n;
    for (; ;) {
        if ((connfd = accept(sockfd,NULL,NULL)) < 0) {
            if (errno == EINTR) {
                continue;
            }else {
                printf("accept error: %s\n",strerror(errno));
                exit(1);
            }
        }
        if (signal(SIGURG,sig_urg) == SIG_ERR) {
            printf("signal error: %s\n",strerror(errno));
            exit(1);
        }
        if (fcntl(connfd,F_SETOWN,getpid()) < 0) {
            printf("fcntl error: %s\n",strerror(errno));
            exit(1);
        }
        while ((n = read(connfd,buf,BUFSIZE - 1)) > 0) {
            buf[n] = 0;
            printf("read %ld bytes from peer: %s\n",n,buf);
        }

    }
    return 0;
}

采用Select:

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/select.h>
#include <fcntl.h>

#define BUFSIZE 4096
#define LISTENQ 100

int main(int argc,char** argv) {
    if (argc != 2) {
        printf("please add or check <services-name>\n");
        exit(1);
    }

    struct addrinfo hints;
    bzero(&hints,sizeof(struct addrinfo));
    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    struct addrinfo* results;
    int err;
    if ((err = getaddrinfo(NULL,argv[1],&hints,&results)) != 0) {
        printf("getaddrinfo error: %s\n",gai_strerror(err));
        exit(1);
    }

    struct addrinfo* dummy = results;
    int sockfd;
    for (; dummy != NULL; dummy = dummy->ai_next) {
        if ((sockfd = socket(dummy->ai_family,dummy->ai_socktype,dummy->ai_protocol)) < 0) {
            continue;
        }
        if (bind(sockfd,dummy->ai_addr,dummy->ai_addrlen) == 0) {
            break;
        }
        close(sockfd);
    }

    if (listen(sockfd,LISTENQ) < 0) {
        printf("listen error: %s\n",strerror(errno));
        exit(1);
    }
    int connfd;
    fd_set rs;
    fd_set es;
    if ((connfd = accept(sockfd,NULL,NULL)) < 0) {
        printf("accept error: %s\n",strerror(errno));
        exit(1);
    }
    for (; ;) {
        FD_ZERO(&rs);
        FD_ZERO(&es);
        FD_SET(connfd,&rs);
        FD_SET(connfd,&es); 
        if (select(connfd + 1,&rs,NULL,&es,NULL) < 0) {
            if (errno == EINTR) {
                printf("select: %s\n",strerror(errno));
                continue;
            }else {
                printf("select error: %s\n",strerror(errno));
                exit(1);
            }
        }

        if (FD_ISSET(connfd,&rs)) {
            char buf[BUFSIZE];
            ssize_t n;
            if ((n = read(connfd,buf,BUFSIZE - 1)) < 0) {
                printf("read error: %s\n",strerror(errno));
                exit(1);
            }else if (n == 0){
                break;
            }
            buf[n] = 0;
            printf("read %ld normal bytes from peer: %s\n",n,buf);
        }
        if (FD_ISSET(connfd,&es)) {
            char buf[BUFSIZE];
            ssize_t n;
            if ((n = recv(connfd,buf,BUFSIZE - 1,MSG_OOB)) < 0) {
                printf("recv from peer error: %s\n",strerror(errno));
                exit(1);
            }else if (n == 0) {
                break;
            }
            buf[n] = 0;
            printf("recv %ld OOB bytes from peer: %s\n",n,buf);
        }
    }   
    return 0;
}

将”带外数据“放进”带内缓冲区”,并含有带外标记的接收端:

(最后一种要准确提取带外数据很麻烦,而且有可能会丢失,不建议使用)

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#define BUFSIZE 4096
#define LISTENQ 100

int main(int argc,char** argv) {
    if (argc != 2) {
        printf("please add or check <services>\n");
        exit(1);
    }

    struct addrinfo hints;
    bzero(&hints,sizeof(struct addrinfo));
    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    struct addrinfo *results;
    int err;
    if ((err = getaddrinfo(NULL,argv[1],&hints,&results)) != 0) {
        printf("getaddrinfo error: %s\n",gai_strerror(err));
        exit(1);
    }
    struct addrinfo* dummy = results;
    int sockfd;
    for (; dummy != NULL; dummy = dummy->ai_next) {
        if ((sockfd = socket(dummy->ai_family,dummy->ai_socktype,dummy->ai_protocol)) < 0) {
            continue;
        }

        if (bind(sockfd,dummy->ai_addr,dummy->ai_addrlen) == 0) {
            break;
        }
        close(sockfd);
    }

    if (dummy == NULL) {
        freeaddrinfo(results);
        printf("all socket failed\n");
        exit(1);
    }
    freeaddrinfo(results);
    if (listen(sockfd,LISTENQ) < 0) {
        printf("listen error: %s\n",strerror(errno));
        exit(1);
    }
    const int on = 1;
    if (setsockopt(sockfd,SOL_SOCKET,SO_OOBINLINE,&on,sizeof(on)) < 0) {
        printf("setsockopt error: %s\n",strerror(errno));
        exit(1);
    }
    int connfd;
    ssize_t n;
    char buf[BUFSIZE];
    int flag;
    for (; ;) {
        if ((connfd = accept(sockfd,NULL,NULL)) < 0) {
            printf("accept error: %s\n",strerror(errno));
            exit(1);
        }
        while (1) {
            if ((flag = sockatmark(connfd)) < 0) {
                printf("sockmark error: %s\n",strerror(errno));
                exit(1);
            }
            if ((n = read(connfd,buf,BUFSIZE - 1)) < 0) {
                printf("read error: %s\n",strerror(errno));
                exit(1);
            }else if (n == 0) {
                break;
            }
            if (flag) {
                printf("receive %ld OOB bytes from peer %s\n",n,buf);
            }else {
                printf("receive %ld normal bytes from peer %s\n",n,buf);
            }
        }
    }   
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值