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. 走进javascript——类型

    ECMAScript语言类型对应于使用ECMAScript语言的ECMAScript程序员直接操作的值.ECMAScript语言类型有以下几种Undefined,Null,Boolean,String ...

  2. Android系统--输入系统(十二)Dispatch线程_总体框架

    Android系统--输入系统(十二)Dispatch线程_总体框架 1. Dispatch线程框架 我们知道Dispatch线程是分发之意,那么便可以引入两个问题:1. 发什么;2. 发给谁.这两个 ...

  3. dotNet Linux 学习记录 - Jexus寄宿WCF服务

    让WCF运行在Linux上(寄宿于服务器程序) WCF介绍请自行 bing 搜索 使用的开发工具为vs2017,系统为 Ubuntu16.04 服务器软件为Jexus ( 详情请看:  Jexus官网 ...

  4. 导入csv文件到数据库

    csv:逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本).纯文本意味着该文件是一个字符 ...

  5. Win下安装MySQL 5.6

    最近身边有人要win下安装mysql 去学习数据库,问我如何安装MySQL,其实win 下安装要比Linux简单的多,直接运行安装包下一步安装即可. 1.首先我们运行mysql-installer-c ...

  6. 【JAVAWEB学习笔记】02_HTML&CSS

    //--------------纯文本版------------------------------------------------- 今日新增单词(更多属性查看手册) form:表单 (要提交的 ...

  7. SSM框架中常用的注解

    @Controller:在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model , ...

  8. 12款Linux系统恢复工具

    电脑死机,硬盘崩溃,花巨大的money搞个急救保护器……这都是计算机的阴暗面.时间一直这样走着,走着,不定哪天背点儿.对于电脑损坏造成的损失,着急抓狂毫无意义.相反,使用恰当的工具反而会最小化损失.你 ...

  9. DDD领域驱动之干货(三)完结篇!

    首先这里发一下结构图,因为是重写的,但是代码都是一样所有如下: 这里我先说一下看了大部分的DDD文章都是采用的WCF做服务,这里呢我用的是webapi做服务,WCF和WEBAPI的区别可以去百度下. ...

  10. Akka(5): ConsistentHashing Router - 可选定Routee的任务分配模式

    上一篇讨论里我们介绍了几种任务分配(Routing)模式.Akka提供的几种现成智能化Routing模式大多数是通过对用户屏蔽具体的运算Routee选择方式来简化Router使用,提高智能程度,所以我 ...