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); 


//父进程,一直不停的接受客户端的连接请求。
   while1){
   int cfd=accept();//返回的套接字用于通信。
   //创建子进程
   pid_t pid=fork();

    if(pid==0){//子进程
    close(lfd);//关闭用于接受连接的套接字。
    //然后一直通信
    while1){
    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线程,一直不停的接受客户端的连接请求。
   while1){
    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数组;
       forint 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 }
View Code

 

posted on 2022-03-10 17:28  开源侠  阅读(197)  评论(0)    收藏  举报