Linux:多进程、多线程服务器的实现解析(有图有代码有真相!!!)
来源:互联网 发布:知乎童谣事件文章 编辑:程序博客网 时间:2024/06/06 11:44
一、问题引入
阻塞型的网络编程接口
几乎所有的程序员第一次接触到的网络编程都是从 listen()、send()、recv()等接口开始的。使用这些接口可以很方便的构建服务器 /客户机的模型。
我们假设希望建立一个简单的服务器程序,实现向单个客户机提供类似于“一问一答”的内容服务。
我们注意到,大部分的 socket接口都是阻塞型的。所谓阻塞型接口是指系统调用(一般是 IO接口)不返回调用结果并让当前线程一直阻塞,只有当该系统
调用获得结果或者超时出错时才返回。实际上,除非特别指定,几乎所有的 IO接口 (包括 socket 接口 )都是阻塞型的。这给网络编程带来了一个很大的问题,如在调用 send()的同时,线程将被
阻塞,在此期间,线程将无法执行任何运算或响应任何的网络请求。这给多客户机、多业务逻辑的网络编程带来了挑战。这时,很多程序员可能会选择多线程的方式来解决这个问题。
二、多进程多线程
应对多客户机的网络应用,最简单的解决方式是在服务器端使用多线程(或多进程)。多线程(或多进程)的目的是让每个连接都拥有独立的线程(或进
程),这样任何一个连接的阻塞都不会影响其他的连接。具体使用多进程还是多线程,并没有⼀一个特定的模式。传统意义上,进程的开
销要远远大于线程,所以,如果需要同时为较多的客户机提供服务,则不推荐使用多进程;如果单个服务执行体需要消耗较多的 CPU 资源,譬如需要进行
大规模或长时间的数据运算或文件访问,则进程较为安全。通常,使用pthread_create () 创建新线程,fork() 创建新进程。
我们假设对上述的服务器 / 客户机模型,提出更高的要求,即让服务器同时为多个客户机提供一问一答的服务。于是有了如上的模型。
在上述的线程 / 时间图例中,主线程持续等待客户端的连接请求,如果有连接,则创建新线程,并在新线程中提供为前例同样的问答服务。
很多初学者可能不明白为何一个 socket 可以 accept 多次。实际上,socket的设计者可能特意为多客户机的情况留下了伏笔,让 accept() 能够返回一个新
的 socket。下面是 accept 接口的原型:输入参数 sockfd 是从 socket(),bind() 和 listen() 中沿用下来的 socket 句柄
值。执行完 bind() 和 listen() 后,操作系统已经开始在指定的端口处监听所有的连接请求,如果有请求,则将该连接请求加入请求队列。调用 accept() 接口
正是从 socket s 的请求队列抽取第一个连接信息,创建⼀一个与 socked同类的新的 socket 返回句柄。新的 socket 句柄即是后续 read() 和 recv() 的输入参
数。如果请求队列当前没有请求,则 accept() 将进⼊入阻塞状态直到有请求进入队列。
1、多进程服务器、客户端实现简单通信
fork_server.c:#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<netinet/in.h>static int startup(const char *_ip,int _port){ int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"); return 3; } struct sockaddr_in local; local.sin_family=AF_INET; local.sin_port=htons(_port); local.sin_addr.s_addr=inet_addr(_ip); if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind");return 4; } if(listen(sock,10)<0) { perror("listen");return 5; }}static void usage(const char *proc){ printf("usage:[local_ip] [local_port]",proc);}int main(int argc,char *argv[]){if(argc!=3){ usage(argv[0]); printf("usage"); return 1;}int listen_sock=startup(argv[1],atoi(argv[2]));struct sockaddr_in peer;socklen_t len=sizeof(peer);while(1){ int new_sock=accept(listen_sock,(struct sockaddr*)&peer,&len); if(new_sock>0) { pid_t id=fork(); if(id==0) { //child close(listen_sock); char buf[1024]; while(1) { ssize_t s=read(new_sock,buf,sizeof(buf)-1); if(s>0) { buf[s]=0;printf("client say#%s\n",buf); write(new_sock,buf,strlen(buf)); } else if(s==0) { printf("client quick\n"); } else { break; } } close(new_sock); exit(1); } else { //father close(new_sock); if(fork()>0) { exit(0); } } } else { perror("new_sock");return 2; }} return 0;}fork_client.c:#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<netinet/in.h>static void usage(const char *proc){ printf("usage:[server_ip] [server_port]",proc);}int main(int argc,char *argv[]){int sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){ perror("socket"); return 1;}struct sockaddr_in server;server.sin_family=AF_INET;server.sin_port=htons(atoi(argv[2]));server.sin_addr.s_addr=inet_addr(argv[1]);if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0){ perror("connect"); return 2;}char buf[1024];while(1){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)); ssize_t _s=read(sock,buf,sizeof(buf)-1); if(_s>0) { buf[_s]=0; printf("server echo:%s\n",buf); } }}close(sock); return 0;}
2、多线程实现简单服务器(远程登陆:telnet)
thread_server.c#include<stdio.h>#include<pthread.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<netinet/in.h>#include<string.h>void* handleRequest(void* arg){char buf[10240]; int sock=(int)arg; while(1) { ssize_t s=read(sock,buf,sizeof(buf)-1);//successif(s>0){ buf[s]=0; printf("%s\n",buf); const char *msg= "HTTP/1.1 200 OK\r\n\r\n<html><h1>This is title</h1></html>\r\n"; write(sock,msg,strlen(msg)); break;}else if(s==0) {printf("client is quit!\n");break;}else{ perror("read"); break;} } close(sock);}int startup(const char *_ip,int _port){ //create socket int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"); return 2; } int flag=1; setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag)); //bind struct sockaddr_in local; local.sin_family=AF_INET; local.sin_port=htons(_port); local.sin_addr.s_addr=inet_addr(_ip); if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind");return 3; } //listen if(listen(sock,10)<0) { perror("listen");return 4; } return sock;}static void usage(char *proc){ printf("usage:%s [server_ip] [server_port]",proc);}int main(int argc, char *argv[]){if(argc!=3){ usage(argv[0]); return 1;}int listen_sock=startup(argv[1],atoi(argv[2]));struct sockaddr_in peer;socklen_t len=sizeof(peer);while(1){ int new_sock=accept(listen_sock,(struct sockaddr*)&peer,&len); if(new_sock<0) { perror("accept");continue; } printf("client ip:%s,port:%d\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port)); pthread_t id; pthread_create(&id,NULL,handleRequest,(void*)new_sock); pthread_detach(id);} return 0;}
- Linux:多进程、多线程服务器的实现解析(有图有代码有真相!!!)
- Linux高性能服务器编程:进程池和线程池原理及应用(有图有代码有真相!!!)
- 数据结构:神奇的B树实现解析(有图有代码有真相!!!)
- LInux:shell 彩色进度条实现(有图有代码有真相!!!)
- Linux: shell命令 eval (有图有代码有真相!!!)
- Linux: 系统配置 crond 和 crontab(有图有代码有真相!!!)
- Linux:I/O多路转接之select(有图有代码有真相!!!)
- Linux: I/O多路转接之poll(有图有代码有真相!!!)
- Linux: I/O多路转接之epoll(有图有代码有真相!!!)
- 实现多进程多线程服务器
- 多进程服务器和多线程服务器的实现
- Linux:dup/dup2 文件描述符重定向函数(有图有代码有真相!!!)
- Linux: shell 中命令代换 $() 和 ``(有图有代码有真相!!!)
- 多进程、多线程并发服务器代码
- 开发手机地图(有图有真相有代码)
- TCP server的实现,和多线程,多进程服务器
- linux服务器多线程还是多进程的选择及区别
- TCP的定时器系列 — 超时重传定时器(有图有代码有真相!!!)
- 面向依存关系语法分析的词向量裁剪
- android 放大图片动画
- eclipse下用Maven搭建web项目
- Andorid Preference -- dependency 和 disableDependentsState属性 小结
- JdbcBaseRevpiew系列之JdbcTransaction(三)---事务
- Linux:多进程、多线程服务器的实现解析(有图有代码有真相!!!)
- Ubuntu14.04 sublime text 3 支持中文
- Centos7安装配置Apache+PHP+Mysql+phpmyadmin
- 通过Amazon EC2建立自己的PPTP VPN服务器
- Java虚拟机的性能监控与调优
- 设备注册函数device_register
- 5.字符个数统计
- Nodejs 实用工具集笔记
- Python灰帽子2多线程扫描端口代码