您的位置:首页 > 理论基础 > 计算机网络

Linux网络编程客户\服务器设计范式

2017-06-25 00:34 387 查看
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 -1;
}
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)) != 0) {
printf("tcp_connect error for %s,%s: %s\n", host, port,  strerror(errno));
return -1;
}
ressave = res;
do {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < 0) {
continue;
}
if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) {
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 -1;
}
freeaddrinfo(ressave);
return sockfd;
}

int main(int argc, char **argv)
{
if (argc != 6) {
printf("usage: client <hostname or IPaddr> <port> <#children> <#loops/child> <#bytes/request>\n");
return -1;
}

int i, j, fd, nchildlen, nloops, nbytes;
pid_t pid;
ssize_t n;
char request[MAXLINE], reply[MAXIN];
nchildlen = atoi(argv[3]);
nloops = atoi(argv[4]);
nbytes = atoi(argv[5]);
snprintf(request, sizeof(request), "%d\n", nbytes);
for (i = 0; i < nchildlen; i++) {
if ((pid = fork()) == 0) {
for (j = 0; j < nloops; j++) {
fd = tcp_connect(argv[1], argv[2]);
if (fd > 0) {
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(0);
}
}
/*waits all child process*/
while (wait(NULL) > 0)
;
if (errno != ECHILD) {
fprintf(stderr, "wait error");
return -1;
}
return 0;
}


迭代服务器代码如下:

#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, 0);
assert(listenfd >= 0);
int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
ret = listen(listenfd, 5);
assert(ret != -1);

char buffer[MAXLINE];
while (1) {
printf("begin to accept.\n");
int connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen );
if (connfd != -1) {
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 0;
}


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

#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 = 0;
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, 0);
assert(listenfd >= 0);
int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
ret = listen(listenfd, 5);
assert(ret != -1);
while(1) {
connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen );
if (connfd == -1) {
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 + 1;
/*child  process */
if (pid == 0) {
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(0);
}
if (pid < 0) {
printf("fork error");
}
close(connfd);
}
return 0;
}


预先分配子进程,每个子进程调用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 (1) {
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 != -1) {
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 0;
}

int main()
{
int i = 0;
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, 0);
assert(listenfd >= 0);
int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
ret = listen(listenfd, 5);
assert(ret != -1);

for (i = 0; i < WORKER; i++) {
printf("Create worker %d\n", i+1);
pid_t pid = fork();
/*child  process */
if (pid == 0) {
worker(listenfd, i);
}
if (pid < 0) {
printf("fork error");
}
}
/*wait child process*/
while (wait(NULL) != 0)
;
if (errno == ECHILD) {
fprintf(stderr, "wait error:%s\n", strerror(errno));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: