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. easyui datebox定位到某一个日期, easyui datebox直接定位到具体的日期, easyui datebox MoveTo方法使用

    easyui datebox定位到某一个日期, easyui datebox直接定位到具体的日期, easyui datebox MoveTo方法使用 >>>>>> ...

  2. Python学习之路-Day2-Python基础3

    Python学习之路第三天 学习内容: 1.文件操作 2.字符转编码操作 3.函数介绍 4.递归 5.函数式编程 1.文件操作 打印到屏幕 最简单的输出方法是用print语句,你可以给它传递零个或多个 ...

  3. H5游戏见缝插针开发

    中秋节马上就要来临,公司开发了一个h5小游戏叉月饼,其实就是游戏“见缝插针”的翻版.这个游戏的开发任务落到了我的头上... 一 游戏介绍 游戏场景基本如下所示:         二 所用工具 这次的开 ...

  4. css 3d 基础知识

    css3d 总结 3d transform (3D变形)(rotate skew scale translate) 基础知识 perspective (视距,景深) perspective-origi ...

  5. 用Docker在一台笔记本电脑上搭建一个具有10个节点7种角色的Hadoop集群(上)-快速上手Docker

    如果想在一台电脑上搭建一个多节点的Hadoop集群,传统的方式是使用多个虚拟机.但这种方式占用的资源比较多,一台笔记本能同时运行的虚拟机的数量是很有限的.这个时候我们可以使用Docker.Docker ...

  6. Oracle数据库悲观锁与乐观锁详解

    数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁.什么叫悲观锁呢,悲观锁顾名思义,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住.而乐 ...

  7. 【JAVAWEB学习笔记】09_MySQL多表&JDBC(包含MySQL数据库思维导图)

    今天晨读单词: order:订单constraint:(强制)约束foreign key:外键references:指向orderitem:订单项join:加入resourceBundle:资源捆绑c ...

  8. CopyOnWriteArrayList实现原理及源码分析

    CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可 ...

  9. Struts2 设置global timer

    设置全局的timer需要在web.xml中添加servlet, 并设置load-on-startup 为 1, 然后在servlet的init()中开启timer, 具体代码如下: 1. web.xm ...

  10. 求一个整数数组最大子数组之和,时间复杂度为N

    #include<iostream.h> int main () { ]={-,-,-,-,-,-,-,-,-,-}; ],sum=; ;i<;i++) { ) { sum=b[i] ...