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;
}