基于tcp的小型服务器(多线程多进程)
2017-06-27 16:41
302 查看
在深入理解计算机系统第11章中,讲到一个echo服务器,基于tcp字节流的小型服务器,利用socket套接字接口,完成了一个简易的服务器。
具体细节可参考网络编程(编写一个小型服务器)–csapp11章。
不过这个小型服务器有一个难以接受的缺点在于:它是单进程的服务器,也就是它只能服务一个客户,我们先不去讨论这个功能强弱,我们知道在现实生活中,一个网站,一个服务器必须要能够服务多个用户,如果只能服务一个用户,那简直不可想象。
之所以只能服务一个用户,是由于设计时,只有一个执行流(也就是单进程),串行化完成任务,说白也就是当一个客户发起请求,该服务器去相应然后服务客户,客户退出才能服务其他人。
因此,想要实现并发性,服务多个用户,那需要实现多进程或者多线程服务器,即一个主线程(主进程)去执行等待客户,另一个去服务客户。
思路很简单,那么问题来了:如何选择多线程还是多进程服务器?这时候,我们就要看看它们利弊了。
线程是CPU调度的基本单位,它是一个执行流的不同分支,运行在进程的地址空间之中,共享进程的资源,它强调共享性,因此无论是创建线程,切换线程,终止线程,退出线程代价要小得多,效率相对来说要高很多,在并发问题中,多线程确实是一个提高效率的好办法。
而对于进程,由于进程是分配资源的基本单位,每个进程都具有自己独立的进程地址空间,创建,切换,终止,退出等等效率不如线程快,那么是不是就用多线程好呢?未必,进程强调独立性,就带来一个好处,稳定性,一个进程如果崩溃不会影响其他进程,而线程就不同了,多个线程运行在同一个进程的地址空间之中,因此一旦有一个线程崩溃了,其他线程也会崩溃,事实上,对于一个公司来说,服务器线程是绝对不能挂的(挂了也要立即重启),因此选择多进程也是可以考虑的。
综上考虑,我们各实现一份多进程,多线程的服务器,其实代码主逻辑有很多类似之处。具体如下:
图示如下:
具体细节可参考网络编程(编写一个小型服务器)–csapp11章。
不过这个小型服务器有一个难以接受的缺点在于:它是单进程的服务器,也就是它只能服务一个客户,我们先不去讨论这个功能强弱,我们知道在现实生活中,一个网站,一个服务器必须要能够服务多个用户,如果只能服务一个用户,那简直不可想象。
之所以只能服务一个用户,是由于设计时,只有一个执行流(也就是单进程),串行化完成任务,说白也就是当一个客户发起请求,该服务器去相应然后服务客户,客户退出才能服务其他人。
因此,想要实现并发性,服务多个用户,那需要实现多进程或者多线程服务器,即一个主线程(主进程)去执行等待客户,另一个去服务客户。
思路很简单,那么问题来了:如何选择多线程还是多进程服务器?这时候,我们就要看看它们利弊了。
线程是CPU调度的基本单位,它是一个执行流的不同分支,运行在进程的地址空间之中,共享进程的资源,它强调共享性,因此无论是创建线程,切换线程,终止线程,退出线程代价要小得多,效率相对来说要高很多,在并发问题中,多线程确实是一个提高效率的好办法。
而对于进程,由于进程是分配资源的基本单位,每个进程都具有自己独立的进程地址空间,创建,切换,终止,退出等等效率不如线程快,那么是不是就用多线程好呢?未必,进程强调独立性,就带来一个好处,稳定性,一个进程如果崩溃不会影响其他进程,而线程就不同了,多个线程运行在同一个进程的地址空间之中,因此一旦有一个线程崩溃了,其他线程也会崩溃,事实上,对于一个公司来说,服务器线程是绝对不能挂的(挂了也要立即重启),因此选择多进程也是可以考虑的。
综上考虑,我们各实现一份多进程,多线程的服务器,其实代码主逻辑有很多类似之处。具体如下:
图示如下:
多进程服务器
tcp_server
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<sys/wait.h> void usage(const char* arg) { printf("correct usage : %s [ip] [port]\n", arg); } int start_up(const char* ip, int port) { int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { fprintf(stderr, "socket failure\n"); exit(-1); } struct sockaddr_in local; local.sin_family = AF_INET; local.sin_port = htons(port); inet_aton(ip, &local.sin_addr); //local.sin_addr.s_addr = inet_addr(ip); int opt = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { fprintf(stderr, "setsockopt failure\n"); exit(-1); } int flag = bind(sock, (struct sockaddr*)&local, sizeof(local)); if (flag == -1) { fprintf(stderr, "bind failure\n"); exit(-1); } flag = listen(sock, 6); if (flag == -1) { fprintf(stderr, "listen failure\n"); exit(-1); } return sock; } int main(int argc, char* argv[]) { // ./tcp_server ip port if (argc != 3) { usage(argv[0]); exit(1); } int listen_sock = start_up(argv[1], atoi(argv[2])); while (1) { struct sockaddr_in client; socklen_t len = sizeof(client); // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); int new_fd = accept(listen_sock, (struct sockaddr*)&client, &len); if (new_fd == -1) { //fprintf(stderr, "accept failure\n"); continue; } pid_t pid = fork(); if (pid < 0) { fprintf(stderr, "fork failure\n"); close(new_fd); } else if (pid == 0)//child { close(listen_sock); if (fork() > 0) { exit(0); } else { printf("%s:%d connects the server\n", inet_ntoa(client.sin_addr), client.sin_port); while (1) { char buf[1024]; ssize_t read_size = read(new_fd, buf, sizeof(buf)-1); if (read_size > 0) { buf[read_size] = '\0'; printf("client : %s\n", buf); write(new_fd, buf, strlen(buf)); } else { printf("client quits...\n"); break; } } close(new_fd); } } else //father { close(new_fd); waitpid(-1, NULL, WNOHANG); } } return 0; }
tcp_client
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<unistd.h> void usage(const char* arg) { printf("correct usage: %s [remote_ip] [remote_port]\n", arg); } int open_clientfd(const char* ip, int port) { int clientfd = socket(AF_INET, SOCK_STREAM, 0); if (clientfd == -1) { fprintf(stderr, "socket failure\n"); exit(-1); } struct sockaddr_in remote; remote.sin_family = AF_INET; remote.sin_port = htons(port); inet_aton(ip, &remote.sin_addr); int flag = connect(clientfd, (struct sockaddr*)&remote, sizeof(remote)); if (flag == -1) { fprintf(stderr, "connect failure\n"); exit(-1); } return clientfd; } int main(int argc, char* argv[]) { if (argc != 3) { usage(argv[0]); exit(1); } int sock_fd = open_clientfd(argv[1], atoi(argv[2])); while (1) { char buf[1024]; printf("please enter # "); fflush(stdout); ssize_t s = read(0, buf, sizeof(buf)-1); if (s > 0) { buf[s-1] = '\0'; write(sock_fd, buf, strlen(buf)); s = read(sock_fd, buf, strlen(buf)); if (s > 0) { printf("server echo # %s\n", buf); } //printf("server # %s\n", buf); } } return 0; }
makefile
.PHONY:all all:tcp_server tcp_client tcp_server:tcp_server.c gcc -o tcp_server tcp_server.c -static tcp_client:tcp_client.c gcc -o tcp_client tcp_client.c .PHONY:clean clean: rm -f tcp_client tcp_server
多线程服务器
tcp_server
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<pthread.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> void usage(const char* arg) { printf("correct usage : %s [ip] [port]\n", arg); } int start_up(const char* ip, int port) { int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { fprintf(stderr, "socket failure\n"); exit(-1); } // int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen); // sockaddr* 相当于 void* // // 利用grep -ER 'struct sockaddr_in {' /usr/include 查找 // // struct sockaddr_in { // __kernel_sa_family_t sin_family; /* Address family */ // __be16 sin_port; /* Port number */ // struct in_addr sin_addr; /* Internet address */ // // /* Pad to size of `struct sockaddr'. */ // unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - // sizeof(unsigned short int) - sizeof(struct in_addr)]; // }; // // struct in_addr { // __be32 s_addr; // }; int opt = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { fprintf(stderr, "setsockopt failure\n"); exit(-1); } struct sockaddr_in local; local.sin_family = AF_INET; local.sin_port = htons(port); //local.sin_addr.s_addr = inet_addr(ip); inet_aton(ip, &local.sin_addr); int flag = bind(sock, (struct sockaddr*)&local, sizeof(local)); if (flag == -1) { fprintf(stderr, "bind failure\n"); exit(-1); } flag = listen(sock, 6); if (flag == -1) { fprintf(stderr, "listen failure\n"); exit(-1); } return sock; } void* thread_run(void* arg) { pthread_detach(pthread_self()); int new_fd = (int)arg; char buf[1024]; memset(buf, 0, sizeof(buf)); while (1) { ssize_t s = read(new_fd, buf, sizeof(buf)-1); if (s > 0) { buf[s] = '\0'; printf("client : %s\n", buf); write(new_fd, buf, strlen(buf)); } else { printf("client quits...\n"); break; } } } int main(int argc, char* argv[]) { // ./tcp_server ip port if (argc != 3) { usage(argv[0]); exit(-1); } int listen_sock = start_up(argv[1], atoi(argv[2])); while (1) { struct sockaddr_in client; socklen_t len = sizeof(client); // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); int new_fd = accept(listen_sock, (struct sockaddr*)&client, &len); if (new_fd == -1) { //fprintf(stderr, "accept failure\n"); continue; } pthread_t tid; pthread_create(&tid, NULL, thread_run, (void*)new_fd); //pthread_detach(tid); } return 0; }
tcp_client
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<unistd.h> void usage(const char* arg) { printf("correct usage: %s [remote_ip] [remote_port]\n", arg); } int main(int argc, char* argv[]) { if (argc != 3) { usage(argv[0]); } int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { fprintf(stderr, "socket failure\n"); exit(-1); } // int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); // // struct sockaddr_in { // __kernel_sa_family_t sin_family; /* Address family */ // __be16 sin_port; /* Port number */ // struct in_addr sin_addr; /* Internet address */ // // /* Pad to size of `struct sockaddr'. */ // unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - // sizeof(unsigned short int) - sizeof(struct in_addr)]; // }; // // struct in_addr { // __be32 s_addr; // }; struct sockaddr_in remote; remote.sin_family = AF_INET; remote.sin_port = htons(atoi(argv[2])); //remote.sin_addr.s_addr = inet_addr(argv[1]); inet_aton(argv[1], &remote.sin_addr); int flag = connect(sock, (struct sockaddr*)&remote, sizeof(remote)); if (flag == -1) { fprintf(stderr, "connect failure\n"); exit(-1); } while (1) { char buf[1024]; printf("please enter # "); fflush(stdout); ssize_t s = read(0, buf, sizeof(buf)-1); if (s > 0) { buf[s-1] = '\0'; write(sock, buf, strlen(buf)); s = read(sock, buf, strlen(buf)); if (s > 0) { printf("server echo # %s\n", buf); } } } return 0; }
makefile
.PHONY:all all:tcp_server tcp_client tcp_server:tcp_server.c gcc -o $@ $^ -static -lpthread tcp_client:tcp_client.c gcc -o $@ $^ .PHONY:clean clean: rm -f tcp_client tcp_server
相关文章推荐
- 基于TCP的多进程和多线程服务器
- 基于socket的Tcp多进程多线程服务器
- Linux网络编程 基于tcp的多线程的服务器
- java--通过socket和多线程进行多个客服端与服务器的简单通讯--基于tcp
- 基于多进程和基于多线程服务器的优缺点及nginx服务器的启动过程
- lesson6 基于多线程的tcp并发服务器
- 多线程、多进程TCP服务器比较
- Unix C语言编写基于多线程的小型并发服务器
- 【Linux网络编程】基于TCP多进程(fork)版本客户端/服务器
- 多进程多线程TCP服务器
- 【网络】实现简单的TCP、UDP服务器、TCP多进程/多线程服务器
- Linux下面基于TCP多线程聊天室(服务器)
- 多线程、多进程TCP服务器、进程池和线程池
- TCP服务器多线程 多进程简单测试
- 基于多线程的TCP服务器项目【开源】
- 服务器之多进程VS多线程
- 《ASCE1885的源码分析》の基于完成端口模型的TCP服务器框架
- tcp和udp多线程的epoll服务器+客户端源代码
- tcp和udp多线程的epoll服务器+客户端源代码
- 基于事件的NIO多线程服务器