网络变成之OOB

1)由主进程 接收OOB,导致并发问题,应该使用各个接收普通数据的子进程来接收OOB(如果其中有多线程,仍需考虑并发问题)

      server代码,

      

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/file.h>

/* 服务器要监听的本地端口*/
#define MYPORT 4000
/* 能够同时接受多少没有accept 的连接*/
#define BACKLOG 10

/* 在sock_fd 上进行监听,new_fd 接受新的连接*/
/* 如果存在多个Client同时连接到server,可以使用map表保存<pid,fd>,来使得URG处理程序使用不同的fd读取OOB */
int new_fd = 0;
int sockfd = 0;

void sig_urg(int signo);
void sig_chld(int signo);

int main()
{
    /* 用于存储以前系统缺省的SIGURL 处理器的变量*/ 
    void * old_sig_urg_handle ;
    /* 自己的地址信息*/
    struct sockaddr_in my_addr;
    /* 连接者的地址信息*/
    struct sockaddr_in their_addr;
    socklen_t sin_size;
    int n ;
    char buff[100] ;
    /* 这里就是我们一直强调的错误检查.如果调用socket() 出错,则返回*/
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
	// - 194 - Linux网络编程
	/* 输出错误提示并退出*/
	perror("socket");
	exit(1);
    }
    /* 主机字节顺序*/
    my_addr.sin_family = AF_INET;
    /* 网络字节顺序,短整型*/
    my_addr.sin_port = htons(MYPORT);
    /* 将运行程序机器的IP 填充入s_addr */
    my_addr.sin_addr.s_addr = INADDR_ANY;
    /* 将此结构的其余空间清零*/
    bzero(&(my_addr.sin_zero), 8);
    /* 这里是我们一直强调的错误检查!! */
    if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
    {
	/* 如果调用bind()失败,则给出错误提示,退出*/
	perror("bind");
	exit(1);
    }
    /* 这里是我们一直强调的错误检查!! */
    if (listen(sockfd, BACKLOG) == -1)
    {
	/* 如果调用listen 失败,则给出错误提示,退出*/
	perror("listen");
	exit(1);
    }

    signal(SIGCHLD, sig_chld);

    while(1)
    {
	// 第6 章berkeley 套接字- 195 -
	/* 这里是主accept()循环*/
	sin_size = sizeof(struct sockaddr_in);
	/* 这里是我们一直强调的错误检查!! */
	if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)
	{
            /* 如果调用accept()出现错误,则给出错误提示,进入下一个循环*/
	    perror("accept");
	    continue;
	}

	/* 服务器给出出现连接的信息*/
	printf("server: got connection from %s\n", inet_ntoa(their_addr.sin_addr));

        printf("server: parent-pid=%d\n", getpid());
        old_sig_urg_handle =(void*) signal(SIGURG, sig_urg);

        /*若getpid()进程的new_fd被关闭,对端无法再发送OOB数据,而普通数据可以发送(只要该fd的副本被打开) */
        /*若在此处使用 父进程来接收OOB,各个子进程来接收普通数据,会引发对new_fd的并发操作,可能导致紧急指针被修改
          导致读取OOB数据错误 */
        fcntl(new_fd, F_SETOWN, getpid()); 

	/* 这里将建立一个子进程来和刚刚建立的套接字进行通讯*/
	if (!fork())
        {
	    /* 这里是子进程*/
            printf("server: child-pid=%d\n", getpid());
            // ============================================================
            /* 设置SIGURG 的处理函数 sig_urg */
            //old_sig_urg_handle =(void*) signal(SIGURG, sig_urg);
            /* 更改connfd 的属主*/
            //fcntl(new_fd, F_SETOWN, getpid());
            // ============================================================

            /* 并发操作sockfd(读取后,缓冲区被清空),可致使读取URG-OOB数据出错,返回EINVAL,因为紧急针被修改了 */
            /* 所以延迟30s 读取普通数据 */
            sleep(30);
	    while(1)
	    {
                /* 若遇到紧急指针,则读取到紧急数据之前的数据 就返回 */
	 	if((n = recv(new_fd, buff, sizeof(buff)-1, 0)) == 0)
		{
	       	     printf("received EOF\n");
		     // break;
                     exit(0);
		}
	        buff[n] = 0 ;
	        printf("Process=%d   Recv %d bytes: %s\n", getpid(), n, buff);
	    }
	}

	/* 关闭new_fd 代表的这个套接字连接*/
	//close(new_fd);
    }
    /* 等待所有的子进程都退出*/
    //if (waitpid(-1,NULL,WNOHANG) > 0);
    //while(waitpid(-1,NULL,WNOHANG) > 0);
	    /* 恢复系统以前对SIGURG 的处理器*/
	    //signal(SIGURG, (void (*)(int))old_sig_urg_handle);
    return 0;
}

void sig_urg(int signo)
{
    // - 196 - Linux网络编程
    int n = 0;
    char buff[100] ;
    printf("SIGURG received\n");
    n = recv(new_fd, buff, sizeof(buff)-1, MSG_OOB);
    printf("EINVAL=%d \n", EINVAL);
    if (n <= 0)
        printf("n=%d   errno=%d   %s\n", n, errno, strerror(errno));
    buff [n] = 0 ;
    printf("Process=%d   recv %d OOB byte: %s\n", getpid(), n, buff);
}

//考虑 SO_OOBINLINE 和 使用select() 处理紧急数据呢?

void sig_chld(int signo)
{
    pid_t  pid;
    int    stat;
    while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
        printf("child %d terminated\n", pid);
}

2)使用各个接收普通数据的子进程来接收OOB(如果其中有多线程,仍需考虑并发问题)

      

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/file.h>

/* 服务器要监听的本地端口*/
#define MYPORT 4000
/* 能够同时接受多少没有accept 的连接*/
#define BACKLOG 10

/* 在sock_fd 上进行监听,new_fd 接受新的连接*/
/* 如果存在多个Client同时连接到server,可以使用map表保存<pid,fd>,来使得URG处理程序使用不同的fd读取OOB */
int new_fd = 0;
int sockfd = 0;

void sig_urg(int signo);
void sig_chld(int signo);

int main()
{
    /* 用于存储以前系统缺省的SIGURL 处理器的变量*/ 
    void * old_sig_urg_handle ;
    /* 自己的地址信息*/
    struct sockaddr_in my_addr;
    /* 连接者的地址信息*/
    struct sockaddr_in their_addr;
    socklen_t sin_size;
    int n ;
    char buff[100] ;
    /* 这里就是我们一直强调的错误检查.如果调用socket() 出错,则返回*/
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
	// - 194 - Linux网络编程
	/* 输出错误提示并退出*/
	perror("socket");
	exit(1);
    }
    /* 主机字节顺序*/
    my_addr.sin_family = AF_INET;
    /* 网络字节顺序,短整型*/
    my_addr.sin_port = htons(MYPORT);
    /* 将运行程序机器的IP 填充入s_addr */
    my_addr.sin_addr.s_addr = INADDR_ANY;
    /* 将此结构的其余空间清零*/
    bzero(&(my_addr.sin_zero), 8);
    /* 这里是我们一直强调的错误检查!! */
    if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
    {
	/* 如果调用bind()失败,则给出错误提示,退出*/
	perror("bind");
	exit(1);
    }
    /* 这里是我们一直强调的错误检查!! */
    if (listen(sockfd, BACKLOG) == -1)
    {
	/* 如果调用listen 失败,则给出错误提示,退出*/
	perror("listen");
	exit(1);
    }

    signal(SIGCHLD, sig_chld);

    while(1)
    {
	// 第6 章berkeley 套接字- 195 -
	/* 这里是主accept()循环*/
	sin_size = sizeof(struct sockaddr_in);
	/* 这里是我们一直强调的错误检查!! */
	if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)
	{
            /* 如果调用accept()出现错误,则给出错误提示,进入下一个循环*/
	    perror("accept");
	    continue;
	}

	/* 服务器给出出现连接的信息*/
	printf("server: got connection from %s\n", inet_ntoa(their_addr.sin_addr));

        printf("server: parent-pid=%d\n", getpid());
        //old_sig_urg_handle =(void*) signal(SIGURG, sig_urg);

        /*若getpid()进程的new_fd被关闭,对端无法再发送OOB数据,而普通数据可以发送(只要该fd的副本被打开) */
        /*若在此处使用 父进程来接收OOB,各个子进程来接收普通数据,会引发对new_fd的并发操作,可能导致紧急指针被修改
          导致读取OOB数据错误 */
        //fcntl(new_fd, F_SETOWN, getpid()); 

	/* 这里将建立一个子进程来和刚刚建立的套接字进行通讯*/
	if (!fork())
        {
	    /* 这里是子进程*/
            printf("server: child-pid=%d\n", getpid());
            // ============================================================
            /* 设置SIGURG 的处理函数 sig_urg */
            old_sig_urg_handle =(void*) signal(SIGURG, sig_urg);
            /* 更改connfd 的属主*/
            fcntl(new_fd, F_SETOWN, getpid());
            // ============================================================

            /* 并发操作sockfd(读取后,缓冲区被清空),可致使读取URG-OOB数据出错,返回EINVAL,因为紧急针被修改了 */
            /* 所以延迟30s 读取普通数据 */
            //sleep(30);
	    while(1)
	    {
                /* 若遇到紧急指针,则读取到紧急数据之前的数据 就返回 */
	 	if((n = recv(new_fd, buff, sizeof(buff)-1, 0)) == 0)
		{
	       	     printf("received EOF\n");
		     // break;
                     exit(0);
		}
	        buff[n] = 0 ;
	        printf("Process=%d   Recv %d bytes: %s\n", getpid(), n, buff);
	    }
	}

	/* 关闭new_fd 代表的这个套接字连接*/
	close(new_fd);
    }
    /* 等待所有的子进程都退出*/
    //if (waitpid(-1,NULL,WNOHANG) > 0);
    //while(waitpid(-1,NULL,WNOHANG) > 0);
	    /* 恢复系统以前对SIGURG 的处理器*/
	    //signal(SIGURG, (void (*)(int))old_sig_urg_handle);
    return 0;
}

void sig_urg(int signo)
{
    // - 196 - Linux网络编程
    int n = 0;
    char buff[100] ;
    printf("SIGURG received\n");
    n = recv(new_fd, buff, sizeof(buff)-1, MSG_OOB);
    printf("EINVAL=%d \n", EINVAL);
    if (n <= 0)
        printf("n=%d   errno=%d   %s\n", n, errno, strerror(errno));
    buff [n] = 0 ;
    printf("Process=%d   recv %d OOB byte: %s\n", getpid(), n, buff);
}

//考虑 SO_OOBINLINE 和 使用select() 处理紧急数据呢?

void sig_chld(int signo)
{
    pid_t  pid;
    int    stat;
    while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
        printf("child %d terminated\n", pid);
}
3)client代码

      

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include <unistd.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/file.h>
/* 服务器程序监听的端口号*/
#define PORT 4000
/* 我们一次所能够接收的最大字节数*/
#define MAXDATASIZE 100
int main(int argc,char *argv[])
{
	/* 套接字描述符*/
	int sockfd,numbytes;
	char buf[MAXDATASIZE];
	struct hostent *he;
	/* 连接者的主机信息*/
	struct sockaddr_in their_addr;
	/* 检查参数信息*/
	if (argc != 2)
	{
		//第6 章berkeley 套接字- 197 -
		/* 如果没有参数,则给出使用方法后退出*/
		fprintf(stderr,"usage: client hostname\n");
		exit(1);
	}
	/* 取得主机信息*/
    if ((he=gethostbyname(argv[1])) == NULL)
    {
	    /* 如果gethostbyname()发生错误,则显示错误信息并退出*/
		perror("gethostbyname");
		exit(1);
	}
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		/* 如果socket()调用出现错误则显示错误信息并退出*/
		perror("socket");
		exit(1);
	}
	/* 主机字节顺序*/
	their_addr.sin_family = AF_INET;
	/* 网络字节顺序,短整型*/
	their_addr.sin_port = htons(PORT);
	their_addr.sin_addr = *((struct in_addr *)he->h_addr);
	/* 将结构剩下的部分清零*/
	bzero(&(their_addr.sin_zero),8);
	if(connect(sockfd,(struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == -1)
	{
		/* 如果connect()建立连接错误,则显示出错误信息,退出*/
		perror("connect");
		exit(1);
	}
	/* 这里就是我们说的错误检查! */
	if (send(sockfd,"123",3,0) == -1)
	{
		/* 如果错误,则给出错误提示,然后关闭这个新连接,退出*/
		perror("send");
		close(sockfd);
		//- 198 - Linux网络编程
		exit(0);
	}
	printf("Send 3 byte of normal data\n");
	/* 睡眠1 秒*/
	sleep(3);
	if (send(sockfd,"4",1,MSG_OOB)== -1)
	{
		perror("send");
		close(sockfd);
		exit(0);
	}
	printf("Send 1 byte of OOB data\n");
	sleep(3);
	if (send(sockfd,"56",2,0) == -1)
	{
		perror("send");
		close(sockfd);
		exit(0);
	}
	printf("Send 2 bytes of normal data\n");
	sleep(3);
	if (send(sockfd,"7",1,MSG_OOB)== -1)
	{
		perror("send");
		close(sockfd);
		exit(0);
	}
	printf("Send 1 byte of OOB data\n");
	sleep(3);
	if (send(sockfd,"89",2,MSG_OOB)== -1)
	{
		perror("send");
		close(sockfd);
		exit(0);
	}
	printf("Send 2 bytes of normal data\n");
	sleep(1);
	//第6 章berkeley 套接字- 199 -
	close(sockfd);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值