socket编程之并发回射服务器
使用到的函数:
// 子进程返回0,父进程返回子进程ID,出错返回-1
pid_t fork(void);
pid_t wait(int *wstatus);
// 最常用的option是WNOHANG,它告知内核在没有已终止子进程时不要阻塞
pid_t waitpid(pid_t pid, int *wstatus, int options);
服务器程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> #define MAXLINE 4096
#define LISTENQ 10 void doEcho(int sockfd) {
char buff[MAXLINE];
while (true) {
memset(buff, , sizeof(buff));
int n = read(sockfd, buff, MAXLINE);
if (n < ) {
perror("read error");
exit();
} else if (n == ) {
printf("client closed\n");
break;
}
fputs(buff, stdout);
write(sockfd, buff, n);
}
} int main(int argc, char **argv) { int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in servaddr, cliaddr; if ( (listenfd = socket(AF_INET, SOCK_STREAM, )) < ) {
perror("socket error");
exit();
} bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(); /* daytime server */ if ( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < ) {
perror("bind error");
exit();
} if ( listen(listenfd, LISTENQ) < ) {
perror("listen error");
exit();
} for ( ; ; ) {
clilen = sizeof(cliaddr);
if ( (connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen)) < ) {
perror("accept error");
exit();
}
/* 子进程 */
if ( (childpid = fork()) == ) {
close(listenfd);
// 回射程序
doEcho(connfd);
exit();
}
/* 父进程 */
close(connfd);
}
}
客户端程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> #define MAXLINE 4096 void str_cli(FILE *fp, int sockfd) {
char sendline[MAXLINE], recvline[MAXLINE];
while (fgets(sendline, MAXLINE, fp) != NULL) {
write(sockfd, sendline, strlen(sendline));
read(sockfd, recvline, MAXLINE);
fputs(recvline, stdout);
memset(sendline, , sizeof(sendline));
memset(recvline, , sizeof(recvline));
}
} int main(int argc, char **argv) { int sockfd[];
struct sockaddr_in servaddr; if (argc != ) {
perror("Usage: a.out <IPaddress>");
exit();
} for (int i = ; i < ; i++) { if ( (sockfd[i] = socket(AF_INET, SOCK_STREAM, )) < ) {
perror("socket error");
exit();
} bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(); /* daytime server */
if (inet_pton(AF_INET, argv[], &servaddr.sin_addr) <= ) {
printf("inet_pton error for %s\n", argv[]);
exit();
} if (connect(sockfd[i], (struct sockaddr*)&servaddr, sizeof(servaddr)) < ) {
perror("connect error");
exit();
}
} str_cli(stdin, sockfd[]); exit();
}
有一个问题需要注意:
服务器子进程终止时,会向父进程发送SIGCHLD信号(默认处理是忽略)。如果父进程不处理该信号,子进程会变成僵尸进程。
父进程增加信号处理函数:
void sig_chld(int signo) {
pid_t pid;
int stat;
while ( (pid = waitpid(-, &stat, WNOHANG)) > ) {
printf("child %d terminated\n", pid);
}
return;
}
这里使用waitpid而不是wait的原因是:
Unix信号默认是不排队的。也就是说,如果一个信号在被阻塞期间产生了一次或多次,那么信号在被解阻塞之后通常只递交一次。实际情况就是,如果同时产生了多个SIGCHLD信号,信号处理函数只会调用一次。waitpid可以避免这个问题。
还有一个问题需要注意:
当SIGCHLD信号递交时,父进程正阻塞于慢系统调用accept,内核会使accept返回一个EINTR错误。如果父进程不处理该错误,会被内核终止(有些系统可以自动重启被中断的系统调用)。
最终的服务器程序如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/errno.h> #define MAXLINE 4096
#define LISTENQ 10 void doEcho(int sockfd) {
char buff[MAXLINE];
while (true) {
memset(buff, , sizeof(buff));
int n = read(sockfd, buff, MAXLINE);
if (n < ) {
perror("read error");
exit();
} else if (n == ) {
printf("client closed\n");
break;
}
fputs(buff, stdout);
write(sockfd, buff, n);
}
} void sig_chld(int signo) {
pid_t pid;
int stat;
while ( (pid = waitpid(-, &stat, WNOHANG)) > ) {
printf("child %d terminated\n", pid);
}
return;
} int main(int argc, char **argv) { int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in servaddr, cliaddr; if ( (listenfd = socket(AF_INET, SOCK_STREAM, )) < ) {
perror("socket error");
exit();
} bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(); /* daytime server */ if ( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < ) {
perror("bind error");
exit();
} if ( listen(listenfd, LISTENQ) < ) {
perror("listen error");
exit();
} signal(SIGCHLD, sig_chld); for ( ; ; ) {
clilen = sizeof(cliaddr);
if ( (connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen)) < ) {
if (errno == EINTR) {
continue;
} else {
perror("accept error");
exit();
}
}
/* 子进程 */
if ( (childpid = fork()) == ) {
close(listenfd);
// 回射程序
doEcho(connfd);
exit();
}
/* 父进程 */
close(connfd);
}
}
你以为这样就完事了?还有下文呢:
参考文章:
linux网络编程之socket(四):使用fork并发处理多个client的请求和对等通信p2p
socket编程之并发回射服务器的更多相关文章
- socket编程之并发回射服务器3
在socket编程之并发回射服务器一文中,服务器采用多进程的方式实现并发,本文采用多线程的方式实现并发. 多线程相关API: // Compile and link with -pthread int ...
- socket编程之并发回射服务器2
承接上文:socket编程之并发回射服务器 为了让服务器进程的终止一经发生,客户端就能检测到,客户端需要能够同时处理两个描述符:套接字和用户输入. 可以使用select达到这一目的: void str ...
- socket编程之时间回射服务器
使用到的函数: // 返回值:读到的字节数,若已到文件尾,返回0:若出错,返回-1 ssize_t read(int fd, void *buf, size_t nbytes); // 返回值:若成功 ...
- 第二十篇:不为客户连接创建子进程的并发回射服务器(poll实现)
前言 在上文中,我使用select函数实现了不为客户连接创建子进程的并发回射服务器( 点此进入 ).但其中有个细节确实有点麻烦,那就是还得设置一个client数组用来标记select监听描述符集中被设 ...
- 不为客户连接创建子进程的并发回射服务器( poll实现 )
前言 在上文中,我使用select函数实现了不为客户连接创建子进程的并发回射服务器( 点此进入 ).但其中有个细节确实有点麻烦,那就是还得设置一个client数组用来标记select监听描述符集中被设 ...
- 第十九篇:不为客户连接创建子进程的并发回射服务器(select实现)
前言 在此前,我已经介绍了一种并发回射服务器实现.它通过调用fork函数为每个客户请求创建一个子进程.同时,我还为此服务器添加了自动消除僵尸子进程的机制.现在请想想,在客户量非常大的情况下,这种为每个 ...
- 不为客户连接创建子进程的并发回射服务器( select实现 )
前言 在此前,我已经介绍了一种并发回射服务器实现( 点此进入 ).它通过调用fork函数为每个客户请求创建一个子进程.同时,我还为此服务器添加了自动消除僵尸子进程的机制.现在请想想,在客户量非常大的情 ...
- 【Unix网络编程】chapter5TCP回射服务器程序
chapter5 5.1 概述 5.2 TCP回射服务器程序:main函数 int main(int argc, char **argv) { int listenfd,connfd; pid_t ...
- 服务器编程入门(11)TCP并发回射服务器实现 - 单线程select实现
问题聚焦: 当客户端阻塞于从标准输入接收数据时,将读取不到别的途径发过来的必要信息,如TCP发过来的FIN标志. 因此,进程需要内核一旦发现进程指定的一个或多个IO条件就绪(即输入已准备好被读取,或者 ...
随机推荐
- Java 方法 的使用
简单的说: 方法就是完成特定功能的代码块– 在很多语言里面都有函数的定义– 函数在Java中被称为方法 • 格式:– 修饰符 返回值类型 方法名(参数类型 参数名1, 参数类型参数名2…) {函数体; ...
- excel中存储的时间的类型是什么
做了一个excel导入数据的功能,其中需要导入时间,默认到天.在开发过程中发现了一个问题, 导入的数据解析到的时间格式是 02-03-19,发现年份前面的两位数丢失了.这当然是导入数据 的解析包的问题 ...
- pgsql的使用
Deepin上面pgsql的启动 service postgresql start 停止 service postgresql stop 查看pgsql的版本 psql --version
- alg-最长回文字符串
class Solution { public: std::string longestPalindrome(const std::string& s) { if (s.empty()) { ...
- python初学(三)
1.以软科中国最好大学排名为分析对象,基于requests库和bs4库编写爬虫程序,对2015年至2019年间的中国大学排名数据进行爬取,并按照排名先后顺序输出不同年份的前10位大学信息,要求对输出结 ...
- Sprint 3 : oxford project API 尝试
本次Sprint我们大家主要在调研和尝试阶段,主要是对photo experience 中的语音接口部分进行相应的调研和分析. 工作进度: 1. 图像界面设计兆阳和敏龙的工作进一步推进,除去之前介绍的 ...
- Key Set HDU - 5363
这个题目套公式 2^(n-1)-1,再来个快速幂基本上就可以AC了 写这个题目的: 公式容易推到错: 容易写成 2^n-1/2...这样写出来结果也不错 但是一直哇 AC: #include< ...
- 代理模式是什么?如何在 C# 中实现代理模式
代理模式 并不是日常开发工作中常常用到的一种设计模式,也是一种不易被理解的一种设计模式.但是它会广泛的应用在系统框架.业务框架中. 定义 它的 定义 就如其它同大部分 设计模式 的定义类似,即不通俗也 ...
- pyinstaller打包
参考 官网:http://www.pyinstaller.org/ pyinstaller参数使用 使用spec文件 安装 Windows依赖pypiwin32,新版的pyinstaller已经包含了 ...
- cmd 文件/文件夹的一切操作
dir // 列出目录下所有文件夹 rd dirname // 删除dirname文件夹(空文件夹) rd /s/q dirname // 删除dirname文件夹(非空)