C++ LINUX网络编程-高并发
P14


这里accept 的第二个参数是传出参数。保存的内容为连接到此服务器上的客户端的IP和端口。
所有的服务器都必须有一个固定的端口,方便客户端来连接,所以服务器要绑定。
而客户端的端口是无关紧要的。你不绑定系统也会隐式绑定,所以客户端不用绑定

recv和send是windows里面的,Windows还需要加载库。#pragram comment.....


p15代码实现
p16函数的封装
p17 三次握手



p21多进程并发服务器分析
原来的服务器的缺陷:accept只调用了一次,只能处理单个连接请求。

读时共享,写时复制
父子进程之间都有int a ,在a被读时,共享同一块内存。写时复制,修改其中一个进程的a,不会影响另一个进程里面的a。

父子进程之间共享文件描述符(socket)和内存映射区。

int accept(int s, struct sockaddr * addr, int * addrlen);
accept()用来接受参数s 的socket 连线. 参数s 的socket 必需先经bind()、listen()函数处理过, 当有连线进来时accept()会返回一个新的socket 处理代码, 往后的数据传送与读取就是经由新的socket处理, 而原来参数s 的socket 能继续使用accept()来接受新的连线要求.
既然父子进程中都有用于监听的socket 和用于通信的socket。而子进程只用来通信,那么就可以关掉用于监听的socket,节省资源

若不进行子进程资源回收,则子进程可能会变成孤儿进程。
p22多进程服务器伪代码
void recycle(int nun){ while(waitpid(-1,NULL,wnohang)>0;) } int main(){ int lfd=sock();//创建套接字 bind()//绑定 listen()//设置监听 //信号回收子进程 struct sigaction act; act.sa_handler=recycle; act.sa_flags=0; sigemptypeset(&act.sas_mask); sigaction(SIGCHLD,&act,NULL); //父进程,一直不停的接受客户端的连接请求。 while(1){ int cfd=accept();//返回的套接字用于通信。 //创建子进程 pid_t pid=fork(); if(pid==0){//子进程 close(lfd);//关闭用于接受连接的套接字。 //然后一直通信 while(1){ int len=read(); if(len==-1){exit(1);} else if(len==0){close(cfd);break;}//客户端关闭了连接 else{ write();} } return 0;//子进程通信完了,退出子进程。 }//子进程完 else{//父进程 close (cfd);//关闭用于通信的套接字 //while( waitpid(-1,NULL,wnohang)!=-1 )回收子进程 这么写不行,因为父进程会一直 //在这里循环,不能去accept了;应该使用信号回收子进程。 } }
p24多线程并发服务器思路


多线程服务器的伪代码
typedef struct sockInfo{ pthread_t id;//线程ID int fd;//套接字 struct sockaddr_in addr; }SockInfo; void *worker(void* arg){ while(1){ //打印客户端IP和端口 write(); read(); } } int main(){ int lfd=sock();//创建套接字 bind()//绑定 listen()//设置监听 SockInfo sock[256]; //父x线程,一直不停的接受客户端的连接请求。 while(1){ sock[i].fd=accept(lfd,&client,&len);//返回的套接字用于通信。 //创建子线程 pthread_create(&sock[i].id, NULL, worker, &sock[i]); }

P33 netstat命令

P34端口复用设置

P35
IO转接技术:select/poll/epoll



P40 select 伪代码
1 int main(){ 2 int lfd=sock();//创建监听的套接字 3 bind(); 4 listen(); 5 //创建文件描述符表 6 fd_st reads,temp; 7 //把reads表内的1024个标志位全部初始化为0 8 fd_zero(&reads); 9 //把监听的文件描述符也添加进读集合 10 fd_set(lfd,&reads); 11 12 int maxfd=lfd; 13 while(1){ 14 //委托内核去检测 15 temp=reads; 16 int ret =select(maxfd+1, &temp,NULL,NULL,NULL); 17 18 if( fd_isset(lfd,&temp)){//若lfd在temp里面的标志位为1,说明有新连接 19 //接受新连接 20 int cfd=accept(); 21 //把cfd加入读集合 22 fd_set(cfd,&reads); 23 maxfd= maxfd<cfd? cfd:maxfd; 24 } 25 26 //客户端发送数据 27 for(int i=lfd+1;i<=maxfd;i++){ 28 if(fd_isset(i,&temp)){ 29 int len=read(); 30 if(len==0){//断开连接了。应该把该套接字从reads列表中删除。 31 fd_clr(i,&reads);//从原始表删除。而不是temp 32 } 33 read(); 34 write(); 35 } 36 } 37 } 38 }






int main(){ int lfd= sock(); bind(); listen(); int epfd=epcreat(); struct epoll_event all[3000];//存储发送变化 的fd的信息 //将监听的lfd挂在epoll树上 struct epoll_event ev; ev.events=EPOLLIN; ev.data.fd=lfd; epoll_ctr(epfd,EPOLL_CTL_ADD,lfd,&ev); while(1){ int ret=epoll_wait(epfd, all, 3000,-1);//返回值为连接的个数 //根据ret便利all数组; for(int i=0;i<ret;i++){ int fd=all[i].data.fd; //有新的连接 if (fd==lfd){ //接受连接请求 int cfd= accept(); //cfd 上树 ev.events=EPOLLIN; ev.data.fd=cfd; epoll_ctl(epfd,epoll_ctl_add,cfd,&ev); } //已经连接的客户端有数据发送过来 else{ //只处理客户端发送过来的数据 if(!all[i].event&EPOLLIN){ continue;} int len=recv(); if(len==0){ close(fd); //把fd从数上删除 epoll_ctl(epfd,epoll_ctl_del,fd,NULL); } send(); } } } }


服务器端UDP代码
1 //f服务器端代码 2 3 int main(){ 4 int fd=socket(AF_INET, SOCK_DGRAM,0); 5 if(fd==-1){ 6 perror("socket error"); 7 exit(1); 8 } 9 //fd绑定本地的IP和端口 10 struct sockaddr_in serv; 11 memset(&serv, 0,sizeof(serv)); 12 serv.sin_family=AF_INETL 13 serv.sin_port=htons(8765); 14 serv.sin_addr.s_addr=htonl(INADDR_ANY); 15 int ret= bind(fd,(struct sockaddr*)&serv,sizeof(serv)); 16 if(ret==-1)...... 17 18 //通信 19 struct sockaddr_in client; 20 socklen_t cli_len=sizeof(client); 21 char buf[1024] ={0}; 22 while(1){ 23 int recvlen=recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr*) &client,&cli_len); 24 printf("recv buf :%s \n",buf ); 25 sendto(fd,buf,strlen(buf)+1,0, (struct sockaddr*) &client,&cli_len) 26 } 27 28 29 30 31 32 33 34 35 }
浙公网安备 33010602011771号