1、前言

  网络编程分为客户端和服务端,服务器通常分为迭代服务器和并发服务器。并发服务器可以根据多进程或多线程进行细分,给每个连接创建一个独立的进程或线程,或者预先分配好多个进程或线程等待连接的请求。今天探讨三种设计范式

(1)迭代服务器

(2)并发服务器,为每个客户请求创建一个进程或线程

(3)预先分配子进程或线程,每个子进程或线程调用accept

3、测试用例:

客户端代码:

 #include <sys/wait.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <stdlib.h> #define IP "127.0.0.1"
#define PORT 8888
#define WORKER 4
#define MAXIN 4096
#define MAXLINE 4096 int tcp_connect(const char *host, const char *port)
{
if (host == NULL || port == NULL) {
return -;
}
int sockfd, n;
struct addrinfo hints, *res, *ressave;
bzero(&hints, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((n = getaddrinfo(host, port, &hints, &res)) != ) {
printf("tcp_connect error for %s,%s: %s\n", host, port, strerror(errno));
return -;
}
ressave = res;
do {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < ) {
continue;
}
if (connect(sockfd, res->ai_addr, res->ai_addrlen) == ) {
break;
}
close(sockfd);
} while( (res = res->ai_next) != NULL);
if (res == NULL) {
printf("tcp_connect error for %s,%s: %s", host, port, strerror(errno));
return -;
}
freeaddrinfo(ressave);
return sockfd;
} int main(int argc, char **argv)
{
if (argc != ) {
printf("usage: client <hostname or IPaddr> <port> <#children> <#loops/child> <#bytes/request>\n");
return -;
} int i, j, fd, nchildlen, nloops, nbytes;
pid_t pid;
ssize_t n;
char request[MAXLINE], reply[MAXIN];
nchildlen = atoi(argv[]);
nloops = atoi(argv[]);
nbytes = atoi(argv[]);
snprintf(request, sizeof(request), "%d\n", nbytes);
for (i = ; i < nchildlen; i++) {
if ((pid = fork()) == ) {
for (j = ; j < nloops; j++) {
fd = tcp_connect(argv[], argv[]);
if (fd > ) {
write(fd, request, strlen(request)); if ((n = read(fd, reply, nbytes)) != nbytes) {
printf("read from server is:%s\n", reply);
}
close(fd);
} else {
break;
}
}
printf("child %d done\n", i);
exit();
}
}
/*waits all child process*/
while (wait(NULL) > )
;
if (errno != ECHILD) {
fprintf(stderr, "wait error");
return -;
}
return ;
}

迭代服务器代码如下:

 #include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <string.h>
#include <errno.h> #define IP "127.0.0.1"
#define PORT 8888
#define MAXLINE 4096 int main()
{
int listenfd, connfd;
struct sockaddr_in address, client_addr;
socklen_t client_addrlen = sizeof(client_addr);
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
inet_pton( AF_INET, IP, &address.sin_addr);
address.sin_port = htons(PORT);
listenfd = socket(PF_INET, SOCK_STREAM, );
assert(listenfd >= );
int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
assert(ret != -);
ret = listen(listenfd, );
assert(ret != -); char buffer[MAXLINE];
while () {
printf("begin to accept.\n");
int connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen );
if (connfd != -) {
printf("accept a connection success.ip :%s, port :%d\n", inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
} else {
printf("accept a connection failed,error:%s", strerror(errno));
} int nbytes = read(connfd, buffer, MAXLINE);
printf("read from client is:%s\n", buffer);
write(connfd, buffer, nbytes); close(connfd);
}
return ;
}

并发服务器,为每个客户请求创建一个进程测试代码如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h> #define IP "127.0.0.1"
#define PORT 8888
#define MAXLINE 4096 int main()
{
int count = ;
struct sockaddr_in address, client_addr;
socklen_t client_addrlen = sizeof( client_addr );
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
inet_pton( AF_INET, IP, &address.sin_addr);
address.sin_port = htons(PORT);
int listenfd,connfd;
listenfd = socket(PF_INET, SOCK_STREAM, );
assert(listenfd >= );
int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
assert(ret != -);
ret = listen(listenfd, );
assert(ret != -);
while() {
connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen );
if (connfd == -) {
printf("accept a connection failed,error:%s", strerror(errno));
break;
}
printf("accept a connection success.ip: %s,prot: %d\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port);
pid_t pid = fork();
count = count + ;
/*child process */
if (pid == ) {
printf("Create process %d handle a new connetcion.\n", count);
close(listenfd);
char buffer[MAXLINE];
int nbytes = read(connfd, buffer, MAXLINE);
printf("read from client is:%s\n", buffer);
write(connfd, buffer, nbytes);
exit();
}
if (pid < ) {
printf("fork error");
}
close(connfd);
}
return ;
}

预先分配子进程,每个子进程调用accept测试代码如下:

 #include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h> #define IP "127.0.0.1"
#define PORT 8888
#define WORKER 4
#define MAXLINE 4096 int worker(int listenfd, int i)
{
while () {
printf("I am worker %d, begin to accept connection.\n", i);
struct sockaddr_in client_addr;
socklen_t client_addrlen = sizeof( client_addr );
int connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen );
if (connfd != -) {
printf("worker %d accept a connection success. ip:%s, prot:%d\n", i, inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
} else {
printf("worker %d accept a connection failed,error:%s", i, strerror(errno));
}
char buffer[MAXLINE];
int nbytes = read(connfd, buffer, MAXLINE);
printf("read from client is:%s\n", buffer);
write(connfd, buffer, nbytes);
close(connfd);
}
return ;
} int main()
{
int i = ;
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
inet_pton( AF_INET, IP, &address.sin_addr);
address.sin_port = htons(PORT);
int listenfd = socket(PF_INET, SOCK_STREAM, );
assert(listenfd >= );
int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
assert(ret != -);
ret = listen(listenfd, );
assert(ret != -); for (i = ; i < WORKER; i++) {
printf("Create worker %d\n", i+);
pid_t pid = fork();
/*child process */
if (pid == ) {
worker(listenfd, i);
}
if (pid < ) {
printf("fork error");
}
}
/*wait child process*/
while (wait(NULL) != )
;
if (errno == ECHILD) {
fprintf(stderr, "wait error:%s\n", strerror(errno));
}
return ;
}

Linux网络编程客户\服务器设计范式的更多相关文章

  1. UNIX网络编程——客户/服务器程序设计示范(总结)

    (1)当系统负载较轻是,每来一个客户请求现场派生一个子进程为之服务的传统并发服务器程序模型就足够了.这个模型甚至可以与inetd结合使用,也就是inetd处理每个连接的接收.我们的其他意见是就重负荷运 ...

  2. UNIX网络编程——客户/服务器心搏函数

    阅读此博客时,可以参考以前的博客<<UNIX网络编程--socket的keep-alive>>和<<UNIX网络编程--套接字选项(心跳检测.绑定地址复用)> ...

  3. UNIX网络编程——客户/服务器程序设计示范(八)

        TCP预先创建线程服务器程序,主线程统一accept 最后一个使用线程的服务器程序设计示范是在程序启动阶段创建一个线程池之后只让主线程调用accept并把每个客户连接传递给池中某个可用线程.  ...

  4. UNIX网络编程——客户/服务器心搏函数 (转)

    下面是关于回送客户和服务器程序开发一些简单的心搏函数.这些函数可以发现对端主机或到对端的通信路径的过早失效.         在给出这些函数之前我们必须提出一些警告.首先,有人会想到使用TCP的保持存 ...

  5. UNIX网络编程——客户/服务器程序设计示范(七)

        TCP预先创建线程服务器程序,每个线程各自accept 前面讨论过预先派生一个子进程池快于为每个客户线程派生一个子进程.在支持线程的系统上,我们有理由预期在服务器启动阶段预先创建一个线程池以取 ...

  6. UNIX网络编程——客户/服务器程序设计示范(六)

    TCP并发服务器程序,每个客户一个线程 前面讲述了,每个客户一个进程的服务器,或为每个客户现场fork一个子进程,或者预先派生一定数目的子进程.如果服务器主机支持线程,我们就可以改用线程以取代子进程. ...

  7. UNIX网络编程——客户/服务器程序设计示范(五)

        TCP预先派生子进程服务器程序,传递描述符 对预先派生子进程服务器程序的最后一个修改版本是只让父进程调用accept,然后把所接受的已连接套接字"传递"给某个子进程.这么做 ...

  8. UNIX网络编程——客户/服务器程序设计示范(三)

    TCP预先派生子进程服务器程序,accept无上锁保护 我们的第一个"增强"型服务器程序使用称为预先派生子进程的技术.使用该技术的服务器不像传统意义的并发服务器那样为每个客户现场派 ...

  9. UNIX网络编程——客户/服务器程序设计示范(二)

        TCP并发服务器程序,每个客户一个子进程 传统上并发服务器调用fork派生一个子进程来处理每个客户.这使得服务器能够同时为多个客户服务,每个进程一个客户.客户数目的唯一限制是操作系统对以其名义 ...

随机推荐

  1. js算法集合(二) javascript实现斐波那契数列 (兔子数列)

    js算法集合(二)  斐波那契数列 ★ 上一次我跟大家分享一下做水仙花数的算法的思路,并对其扩展到自幂数的算法,这次,我们来对斐波那契数列进行研究,来加深对循环的理解.     Javascript实 ...

  2. SpringMVC中使用@Value给非String类型注入值

    String类型的@Value注入方式 String类型的直接可以使用 @Value("陈婉清") private String name; 非String类型的@Value注入方 ...

  3. 200 OK (from cache)原因

    Meta标签中的http-equiv用来标记不可缓存或过期时间,但效果一般.而且代理缓存基本不访问HTML文档内容,所以尽量少用meta标签控制缓存. Pragma: no-cache Forces ...

  4. Spring MVC 请求处理流程概览

    SpringMVC工作流程 图一:请求流程概述 图二:请求在每个组件的处理 解释Spring工作流程 1.用户向服务器发送请求,请求被spring前端控制Servelt DispatcherServe ...

  5. 微信小程序大全(下)(最新整理 建议收藏)

  6. java web 数据库开发1

    一个完整的数据库部署架构通常由客户端和服务器端两部分组成.客户端封装数据库请求将其发送给服务器端,服务器端执行完毕将其及结果返回给服务器端. 以mysql为例 介绍java应用程序对数据库的访问 JD ...

  7. NodeJs的包漏洞扫描与漏洞测试攻击

    一个典型的Node应用可能会有几百个,甚至上千个包依赖(大部分的依赖是间接的,即下载一个包,这个包会依赖其他的好多包),所以最终的结果是,应用程序就会像是这个样子的:

  8. 探讨SQL Server并发处理队列数据不阻塞解决方案

    前言 之前对于并发这一块确实接触的比较少,自从遇到现在的老大,每写完一块老大都会过目一下然后给出意见,期间确实收获不少,接下来有几篇会来讲解SQL Server中关于并发这一块的内容,有的是总结,有的 ...

  9. 基于binlog来分析mysql的行记录修改情况(python脚本分析)

          最近写完mysql flashback,突然发现还有有这种使用场景:有些情况下,可能会统计在某个时间段内,MySQL修改了多少数据量?发生了多少事务?主要是哪些表格发生变动?变动的数量是怎 ...

  10. 浅谈JAVA验证码~

    这两天在帮同学做个项目,项目中需要做个验证码,说实话那么多年竟然没注意过这东西,原理很简单,贴出来给大家做个参考. 1.简单介绍 一般稍微有些经验的程序员都不会再自己写原生验证码生成了,因为各种强大的 ...