Linux网络编程客户\服务器设计范式
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网络编程客户\服务器设计范式的更多相关文章
- UNIX网络编程——客户/服务器程序设计示范(总结)
(1)当系统负载较轻是,每来一个客户请求现场派生一个子进程为之服务的传统并发服务器程序模型就足够了.这个模型甚至可以与inetd结合使用,也就是inetd处理每个连接的接收.我们的其他意见是就重负荷运 ...
- UNIX网络编程——客户/服务器心搏函数
阅读此博客时,可以参考以前的博客<<UNIX网络编程--socket的keep-alive>>和<<UNIX网络编程--套接字选项(心跳检测.绑定地址复用)> ...
- UNIX网络编程——客户/服务器程序设计示范(八)
TCP预先创建线程服务器程序,主线程统一accept 最后一个使用线程的服务器程序设计示范是在程序启动阶段创建一个线程池之后只让主线程调用accept并把每个客户连接传递给池中某个可用线程. ...
- UNIX网络编程——客户/服务器心搏函数 (转)
下面是关于回送客户和服务器程序开发一些简单的心搏函数.这些函数可以发现对端主机或到对端的通信路径的过早失效. 在给出这些函数之前我们必须提出一些警告.首先,有人会想到使用TCP的保持存 ...
- UNIX网络编程——客户/服务器程序设计示范(七)
TCP预先创建线程服务器程序,每个线程各自accept 前面讨论过预先派生一个子进程池快于为每个客户线程派生一个子进程.在支持线程的系统上,我们有理由预期在服务器启动阶段预先创建一个线程池以取 ...
- UNIX网络编程——客户/服务器程序设计示范(六)
TCP并发服务器程序,每个客户一个线程 前面讲述了,每个客户一个进程的服务器,或为每个客户现场fork一个子进程,或者预先派生一定数目的子进程.如果服务器主机支持线程,我们就可以改用线程以取代子进程. ...
- UNIX网络编程——客户/服务器程序设计示范(五)
TCP预先派生子进程服务器程序,传递描述符 对预先派生子进程服务器程序的最后一个修改版本是只让父进程调用accept,然后把所接受的已连接套接字"传递"给某个子进程.这么做 ...
- UNIX网络编程——客户/服务器程序设计示范(三)
TCP预先派生子进程服务器程序,accept无上锁保护 我们的第一个"增强"型服务器程序使用称为预先派生子进程的技术.使用该技术的服务器不像传统意义的并发服务器那样为每个客户现场派 ...
- UNIX网络编程——客户/服务器程序设计示范(二)
TCP并发服务器程序,每个客户一个子进程 传统上并发服务器调用fork派生一个子进程来处理每个客户.这使得服务器能够同时为多个客户服务,每个进程一个客户.客户数目的唯一限制是操作系统对以其名义 ...
随机推荐
- 【CSS】思考和再学习——关于CSS中浮动和定位对元素宽度/外边距/其他元素所占空间的影响
一.width:auto和width:100%的区别 1.width:100%的作用是占满它的参考元素的宽度.(一般情况下参考元素 == 父级元素,这里写成参考元素而不是父级元素,在下面我会再 ...
- php表单提交 图片、音乐、视频、文字,四种类型共同提交到数据库
这个问题一直困扰了我好几天,终于在今天让我给解决了,难以掩饰的激动. 其实在之前没有接触到这种问题,只是表单提交数据而已,再就是图片,四种类型同时提交还真是没遇到过,做了一个系统,其中有一个功能就是提 ...
- eclipse简介及下载
一.Eclipse 是非常着名的跨平台的自由集成开发环境(IDE).最初主要用来Java语言开发,但是目前亦有人通过插件使其作为其他计算机语言比如C++和Python的开发工具. 二.Eclipse的 ...
- MFC六大关键技术
视频教程地址观看:http://pan.baidu.com/s/1mhKQ6kK 1.MFC六大关键技术sada)MFC程序的初始化过程:从CWinApp类派生一个应用程序类:使用派生类定义的全局变量 ...
- libsvm参数选择
以前接触过libsvm,现在算在实际的应用中学习 LIBSVM 使用的一般步骤是: 1)按照LIBSVM软件包所要求的格式准备数据集: 2)对数据进行简单的缩放操作: 3)首要考虑选用RBF 核函数: ...
- angular.js封装的文件上传指令
今天把最近用到的东西整理一下,直接上代码,需要申请犀牛存储图片,文件 1.html div div img.img-thumbnail.center-block(ng-src="{{ltUp ...
- Linux学习第二步(Java环境安装)
jdk版本:jdk-8u131-linux-x64.rpm 注:以下操作在root用户或具有root权限的用户下操作 一.将 dk-8u131-linux-x64.rpm拷贝到/home目录下 cp ...
- @JsonFormat 日期格式自动格式化
通常日期格式都是以时间戳的形式存放在数据库里,当前端页面通过接口查询时,我们会将一个对象的某些属性查出来返回给页面. 例如,某个类里面有个属性 Timestamp create_time 给这个对象实 ...
- 【2017-05-21】WebForm内置对象:Session、Cookie,登录和状态保持
1.Request -获取请求对象 string s =Request["key"]; 2.Response - 响应请求对象 Response.Redirect(" ...
- Intellij Shortcuts
ctrl+shift+F : search in whole project ctrl+hover : check the field info in brief ctrl+Q : check the ...