在学习主要代码之前先了解下TCP通信两端使用socket函数建立连接的流程:

1、客户端和服务器均使用socket函数各自创建一个套接字

2、服务器用bind绑定IP地址和端口号

3、服务器用listen开启监听模式

3、服务器用accept阻塞自己,等待客户端的连接请求

4、客户端通过connect发出连接请求

5、服务器收到连接请求后,建立连接,之后双方可用recv(接收)和send(发送)进行通信

通信结束后双方通过close()关闭套接字

 

int socket(int domain,int type,int protocol);

参数:1、指明所用协议族 常用的有AF_INET    AF_INET6  AF_LOCAL  AF_ROUTE

      在TCP连接中一般使用AF_INET表示用ipv4和16位端口号

   2、套接字类型。SOCK_STREAM:TCP   SOCK_DGRAM:UDP   SOCK_RAW:允许对底层访问

   3、协议类别。可以为0让系统自动选择

返回值若小于0说明发生错误,否则返回一个值表示创建套接字,类似每个人的身份证号一样,

之后都用套接字描述符来形容这个值

 

int bind(int sockfd,const struct sockaddr*,socklen_t addrlen)

参数:1、套接字描述符

   2、协议地址,用于指定协议族、端口和IP地址

   3、第二个参数的长度

发生错误返回-1,绑定成功返回0; 该函数用于将某个套接字与协议地址关联起来

 

int listen(int sockfd,int backlog)

参数:1、套接字描述符

   2、接受请求的最大长度

仅被服务器调用,将套接字变为被动连接,将套接字变为监听套接字,成功返回0,失败返回-1

 

int accept(int sockfd, struct sockaddr* addr,socklen_t *addrlen);

参数:1、套接字描述符

   2、用于存放客户端的协议地址信息

   3、第二个参数的长度,注意,这里不要直接用sizeof,会出错。要定义一个socklen_t的变量。

服务器阻塞式等待客户端请求,成功连接后,客户端相关信息会被存储到第二个参数里。

失败返回-1 成功返回客户端的套接字描述符

 

int connect(int sockfd,struct sockaddr *addr,socklen_t *addrlen);

参数:1、套接字描述符

   2、服务器的协议地址

   3、第二个参数的长度

客户端发起连接请求并连接 成功返回0  失败返回-1

 

int send(int sockfd,const void* msg,int len,int flags)

参数:1、发送端套接字描述符

   2、发送数据缓冲区

   3、第二个参数的长度

   4、0个或多个标志的组合体,可直接设置为0 或用“|”连在一起。

    MSG_DONTWAIT:非阻塞操作

    MSG_DONTROUTE:告知网际层协议,目的主机在网络,不需要查找路由表

    MSG_NOSIGNAL:动作不愿被SIGPIPE信号中断

    MSG_OOB:表示接受到需要优先处理的数据

用于通信间发送数据

 

int recv(int sockfd,void buf,int len,unsigned int flags)

参数:1、接受端套接字描述符

   2、接受缓冲区基地址

   3、以字节计算接受换乘区长度

   4、0或多个标志的组合体

    MSG_DONTWAIT:设置非阻塞操作

    MSG_PEEK:接收数据后,在接收队列保留原有数据

    MSG_WAITALL:阻塞操作

    MSG_OOB:表示接受到需要优先处理的数据

用于通信接收数据

 

在前面使用accept()会将服务器阻塞,直到有客户端的连接,这样特别浪费服务器的资源。

接下来介绍使用select()和epoll()实现非阻塞方式监听套接字描述符的变化情况

 

int select(int maxfd,fd_set *read_set,fd *write_set,fd_set *except_set,struct timeval *timeout)

调用该函数可以找到事件发生的个数,2-4参数分别为读事件、写事件、错误异常事件。

该函数会查询所有套接字描述小于maxfd的事件,对于不想监听的事件传NULL即可

最后一个参数表示阻塞时间,若为NULL则阻塞程序,直到有事件到来,否则阻塞timeout时间等待事件到来

函数返回事件的个数

 

对于fd_set结构体

int FD_ZERO(fd_set *fdset);//将fdset置0

int FD_CLR(int fd,fd_set *fdset);//在fdset清除fd,fdset作为第二个参数传入select时将不再监听fd的读事件

int FD_SET(int fd,fd_set *fdset);//将fd添加到fdset中去

int FD_ISSET(int fd,fd_set *fdset);//检测fd是否引起fdset变化,即fd发生了事件

 

对于fdset="0111",表示监听fd=0 1 2,当调用select且fd=0 2有事件发生后

fdset="0101"此时用FD_ISSET即可判断fd是否有事件发生

在服务器中可以通过select采用轮询的方式,即可实现非阻塞式等待连接请求,

显然这种方式会随着轮询的套接字增加使得效率下降

因而可以使用epoll系统调用函数,它的效率不会随着监听套接字数目的增长而降低。

 

 

int epoll_create(int size)

参数size表示要监听的套接字的数目,若执行成功返回epoll句柄

这个句柄类似人的身份证号,用于标识不同的epoll句柄

 

int epoll_ctl(int epfd,int op,int fd, struct epoll_event *event);

参数:1、epoll句柄

   2、表示执行的动作,  EPOLL_CTL_ADD:注册新的套接字描述符

             EPOLL_CTL_MOD:修改已有的套接字描述符(一般为修改监听事件)

             EPOLL_CTL_DEL:删除已有的套接字描述符

   3、套接字描述符

   4、要监听的事件:  EPOLLIN:读事件

            EPOLLOUT:写事件

            EPOLLPRI:有紧急数据可读

            EPOLLET:边缘触发

            EPOLLONESHOT: 只监听一次事件,监听完一次后,要重新添加

    多个事件通过” |  “连接

成功返回0 失败返回-1

 

int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);

参数:1、epoll句柄

   2、事件集合

   4、最大可处理事件

   5、超时时间,时间为0立即返回

执行成功返回需要处理的事件数目,返回0表示超时

 

 

样例:

客户端

 1 #include<unistd.h>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<stdlib.h>
 5 #include<sys/socket.h>
 6 #include<arpa/inet.h>
 7 #include<netinet/in.h>
 8 #include<string.h>
 9 using namespace std;
10 int main()
11 {
12     int sockfd;
13     struct sockaddr_in sock_addr;
14     int i,j,op;
15     sockfd=socket(AF_INET,SOCK_STREAM,0);
16     if(sockfd<0)
17     {
18         printf("套接字创建失败\n");
19         return -1;
20     }
21     bzero(&sock_addr,sizeof(sock_addr));
22     sock_addr.sin_family=AF_INET;
23     sock_addr.sin_port=htons(8000);
24     
25     op=connect(sockfd,(struct sockaddr*)&sock_addr,sizeof(sock_addr));
26     if(op<0)
27     {
28         printf("connect error\n");
29         close(sockfd);
30         return -1;
31     }
32 
33     while(1)
34     {
35         char buf[1010]="";
36         
37         fgets(buf,sizeof(buf),stdin);
38         send(sockfd,buf,strlen(buf),0);
39         //printf("send:%s\n",buf);
40         if((op=recv(sockfd,buf,strlen(buf),0))>0)
41         {
42             buf[op]='\0';
43             printf("%s\n",buf);
44         }
45     }
46     close(sockfd);
47     return 0;
48 }

服务器(select):

  1 #include<iostream>
  2 #include<sys/socket.h>
  3 #include<arpa/inet.h>
  4 #include<netinet/in.h>
  5 #include<unistd.h>
  6 #include<cstdlib>
  7 #include<cstdio>
  8 #include<string.h>
  9 #include<pthread.h>
 10 #include<sys/select.h>
 11 #include<algorithm>
 12 #include<sys/time.h>
 13 #include<sys/types.h>
 14 using namespace std;
 15 
 16 int main()
 17 {
 18     int sockfd,sfd;
 19     struct sockaddr_in sock_addr;
 20     int i,j,op;
 21     int maxfd,maxi,n;
 22 
 23     fd_set rset,allset;
 24     int client[1024];
 25     char buf[100];
 26 
 27     sockfd=socket(AF_INET,SOCK_STREAM,0);
 28     if(sockfd<0)
 29     {
 30         printf("套接字创建失败\n");
 31         return -1;
 32     }
 33 
 34     bzero(&sock_addr,sizeof(sock_addr));
 35     sock_addr.sin_family=AF_INET;
 36     sock_addr.sin_port=htons(8000);
 37     sock_addr.sin_addr.s_addr=htons(INADDR_ANY);
 38 
 39     op=bind(sockfd,(struct sockaddr*)&sock_addr,sizeof(sock_addr));
 40     if(op<0)
 41     {
 42         printf("套接字绑定失败\n");
 43         close(sockfd);
 44         return -1;
 45     }
 46 
 47     op=listen(sockfd,4);//4表示套接字最大维护连接数目
 48     if(op<0)
 49     {
 50         printf("listen error\n");
 51         close(sockfd);
 52         return -1;
 53     }
 54     int clientfd;
 55     
 56     struct sockaddr_in client_addr;
 57     bzero(&client_addr,sizeof(client_addr));
 58     socklen_t len=sizeof(struct sockaddr);
 59 
 60     maxfd=sockfd;
 61     maxi=-1;
 62     for(i=0;i<1024;i++)
 63         client[i]=-1;
 64     
 65     FD_ZERO(&allset);
 66     FD_SET(sockfd,&allset);//构造监听套接字文件描述符
 67 
 68     while(1)
 69     {
 70         //sleep(5);
 71         rset=allset;
 72         op=select(maxfd+1,&rset,NULL,NULL,NULL);
 73         if(op<0)
 74         {
 75             cout<<"select error\n";
 76             return -1;
 77         }        
 78         
 79         if(FD_ISSET(sockfd,&rset))
 80         {
 81             printf("111111\n");
 82             clientfd=accept(sockfd,(struct sockaddr*)&client_addr,&len);
 83             printf("222222\n");
 84 
 85             if(clientfd<0)
 86             {
 87                 printf("accept error\n");
 88                 return -1;
 89             }
 90         
 91             char clientip[32]="";
 92             inet_ntop(AF_INET,&client_addr.sin_addr,clientip,16);
 93             printf("ip:%s,port:%d\n",clientip,ntohs(client_addr.sin_port));
 94 
 95             for(i=0;i<1024;i++)
 96             {    
 97                 if(client[i]<0)
 98                 {
 99                     client[i]=clientfd;
100                     break;
101                 }
102                 else cout<<clientfd<<endl;
103             }
104 
105             if(i==1024)
106             {
107                 cout<<"to many clients\n";
108                 return -1;
109             }
110 
111             FD_SET(clientfd,&allset);
112             if(clientfd>maxfd)
113                 maxfd=clientfd;
114             
115             maxi=max(maxi,i);
116 
117             if(--op==0)
118                 continue;
119         }
120         //sleep(5);
121         for(i=0;i<=maxi;i++)
122         {
123             sfd=client[i];
124             if(sfd<0)
125                 continue;
126             if(FD_ISSET(sfd,&rset))
127             {
128                 cout<<op<<endl;
129                 if((n=read(sfd,buf,90))==0)
130                 {
131                     cout<<"close"<<sfd<<endl;
132                     close(sfd);
133                     FD_CLR(sfd,&allset);
134                     client[i]=-1;
135                 }
136                 else if(n>0)
137                 {
138                     buf[n]='\0';
139                     cout<<"recv data:"<<buf<<endl;
140                     bzero(&buf,sizeof(buf));
141                     cout<<"send:";
142                     fgets(buf,sizeof(buf),stdin);
143                     send(sfd,buf,strlen(buf),0);
144                 }
145                 if(--op==0)
146                     break;
147             }
148         }
149         //cout<<"oooooooooooooooooooo"<<endl;
150     }
151     cout<<"wryyyyyyyyyyyyyyyyy"<<endl;
152     close(clientfd);
153     close(sockfd);
154     return 0;
155 }

服务器(epoll):

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<iostream>
  5 #include<sys/socket.h>
  6 #include<sys/types.h>
  7 #include<netinet/in.h>
  8 #include<sys/epoll.h>
  9 #include<fcntl.h>
 10 #include<errno.h>
 11 #include<string.h>
 12 #include<arpa/inet.h>
 13 
 14 #define MAXLINE 5
 15 #define LISTENQ 20
 16 using namespace std;
 17 int main(int argc,char **argv)
 18 {
 19     int i,j;
 20     int maxi,listenfd,connfd,sockfd,epfd,nfds,portnumber=8000;
 21     ssize_t n;
 22     char line[5];
 23     socklen_t clilen;
 24     char buf[512];
 25 
 26     struct epoll_event ev,events[20];
 27     epfd=epoll_create(256);
 28 
 29     struct sockaddr_in clientaddr;
 30     struct sockaddr_in serveraddr;
 31 
 32     listenfd=socket(AF_INET,SOCK_STREAM,0);
 33     if(listenfd<0)
 34     {
 35         cout<<"socket error"<<endl;
 36         return -1;
 37     }
 38     
 39     ev.data.fd=listenfd;
 40     ev.events=EPOLLIN|EPOLLET;
 41 
 42     epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
 43     bzero(&serveraddr,sizeof(serveraddr));
 44     serveraddr.sin_family=AF_INET;
 45     serveraddr.sin_port=htons(portnumber);
 46     serveraddr.sin_addr.s_addr=htons(INADDR_ANY);
 47 
 48     int op=bind(listenfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
 49     if(op<0)
 50     {
 51         cout<<"bind error"<<endl;
 52         return -1;
 53     }
 54     op=listen(listenfd,20);
 55     if(op<0)
 56     {
 57         cout<<"listen error"<<endl;
 58         return -1;
 59     }
 60 
 61     maxi=0;
 62     while(1)
 63     {
 64         nfds=epoll_wait(epfd,events,20,500);
 65 
 66         for(i=0;i<nfds;i++)
 67         {
 68             if(events[i].data.fd==listenfd)
 69             {
 70                 clilen=sizeof(sockaddr);
 71                 connfd=accept(listenfd,(struct sockaddr*)&clientaddr,&clilen);
 72                 if(connfd<0)
 73                 {
 74                     cout<<"accept error"<<endl;
 75                     return -1;
 76                 }
 77 
 78                 char *str=inet_ntoa(clientaddr.sin_addr);
 79                 int port_=ntohs(clientaddr.sin_port);
 80                 cout<<"accept a connection from "<<port_<<endl;
 81             
 82                 ev.data.fd=connfd;
 83                 ev.events=EPOLLIN|EPOLLET;
 84                 epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
 85             }
 86             else if(events[i].events&EPOLLIN)
 87             {
 88                 cout<<"EPOLLIN"<<endl;
 89                 sockfd=events[i].data.fd;
 90                 if(sockfd<0)
 91                     continue;
 92                 n=read(sockfd,line,500);
 93                 if(n<0)
 94                 {
 95                     if(errno==ECONNRESET)
 96                     {
 97                         close(sockfd);
 98                         events[i].data.fd=-1;
 99                     }
100                     else cout<<"readline"<<endl;
101                 }
102                 else if(n==0)
103                 {
104                     cout<<"close!"<<endl;
105                     close(sockfd);
106                     events[i].data.fd=-1;
107                     continue;
108                 }
109                 
110                 cout<<"recv:"<<line<<endl;
111                 bzero(&line,sizeof(line));
112                 ev.data.fd=sockfd;
113                 ev.events=EPOLLOUT|EPOLLET;
114         
115                 epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
116             }
117             else if(events[i].events&EPOLLOUT)
118             {
119                 sockfd=events[i].data.fd;
120                 cout<<"send:";
121                 fgets(buf,sizeof(buf),stdin);
122                 buf[strlen(buf)-1]=0;
123                 send(sockfd,buf,strlen(buf),0);
124                 ev.data.fd=sockfd;
125                 ev.events=EPOLLIN|EPOLLET;
126                 epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
127             }
128         }
129     }
130     return 0;
131 }

 

参考书籍——《Linux网络编程》

posted on 2020-11-29 22:16  丶蛋花拉面  阅读(403)  评论(0)    收藏  举报