Linux 网络 UDP TCP select模式 http协议

发布时间:2017-1-19 22:59:09 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"Linux 网络 UDP TCP select模式 http协议",主要涉及到Linux 网络 UDP TCP select模式 http协议方面的内容,对于Linux 网络 UDP TCP select模式 http协议感兴趣的同学可以参考一下。

网络: 1.      网络的基本概念 网络编程采用socket模型 网络通信本质也是进程之间的通信,是不同主机之间   识别主机:4字节整数 :ip地址 识别进程:2字节整数 :端口号   IP地址的表示方法:  内部表示:4字节整数                                   外部表示:数点字符串                                                                                                                                    结构体                            1 2 3 4    分段表示,每个段使用.分割                            "192.168.0.26" ip地址转换: #include<netinet/in.h> structsockaddr_in {       int      sin_family       in_port_t sin_port;       struct in_addr sin_addr; };   struct in_addr {      in_addr_t  s_addr; };   总结:      ip地址的表示           字符串表示“192.168.0.26”           整数表示: in_addr_t           结构体表示:struct in_adddr  连接点:endpoint struct  sockaddr_in {      in_port_t    sin_port;      strcut in_addr  sin_addr; };   Ip地址的转换: #include<arpa/inet.h>    inet_addr //把字符串转化为整数(网络字节序)    inet_aton //把字符串转化为structin_addr(网络字节序) inet_network //把结构体转换为字符串(本地字节序)     inet_ntoa //把结构体转换为字符串     例子1:   #include<stdio.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>           void  main()          {                 in_addr_t nip=192<<24|168<<16|0<<8|26;                 char *ip= “192.168.0.26”;                 int  myip;                //把整数转换为字符串inet_ntoa                 struct  in_addr sip={nip};                 printf(“%s\n”,inet_ntoa(sip));                   //把字符串转换为整数                 myip=inet_addr(ip);                 printf(“%u\n”,myip);                       }                     inet_lnaof inet_netof 函数:          从ip地址得到主机标示位,和网络标示位            函数inet_lnaof函数是将套接口地址中的IP地址(网络字节序)转换为没有网络位的主机ID(主机字节序)             in_addr_t inet_lnaof(struct in_addr addr);           in_addr_tinet_netof(struct in_addr addr);           代码例子: #include<stdio.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>   void  main() {      char *ip= “192.168.0.26”;      struct in_addr addr;      in_addr_t  net;      int_addr_t host; struct in_addr tmp; inet_aton(ip,&addr); //转换为网络字节序 host=inet_lnaof(addr); net=inet_netof(addr); tem.s_addr=net; printf(“%s\n”,inet_ntoa(tem)); tem.s_addr=host; printf(“%s\n”,inet_ntoa(tem));   }     TCP/UDP编程      对等模型:AF_INET    SOCK_DGRAM  对应的默认协议 :0 :UDP      c/s模型:AF_INET    SOCK_STREAM               0     :TCP 网络编程:           IOS的7层模型:           物理层           数据链路层            数据链路层(数据物理怎么传输)           网络层                 IP层      (数据的传输方式)           传输层                 传输层     (数据传输的结果)           会话层                 应用层      (数据传递的含义)           表示层           应用层    UDP编程的数据特点 UDP采用对等模型SOCK_DGRAM socket                      socket 绑定ip地址bind            连接目标(可选)connect read/recv/recvfrom          发送数据write/send/sendto 关闭close   recv函数:          ssize_t recv(int s, void * buf,size_tlen ,int flags);        参数3:指定为0时和read函数一样(接收不到数据阻塞等待) recvfrom函数: ssize_t  recvfrom(int s,void *buf,size_t len ,intflags struct sockaddr*from,socklen_t *fromlen);             参数4,5:为发送者的ip地址和地址长度       send函数          sendto函数            int sendto(                   int fd,//socket描述符号                   const void*buf,//发送的数据缓冲                   size_t size,//发送的数据长度                   int flags,//发送方式MSG_NOWAITMSG_OOB                   const structsockaddr *addr,//发送的目标的IP与端口                   socklen_tlen//sockaddr_in的长度               );            返回:                   -1:发送失败                   >=0:发送的数据长度 案例:        A:                                B:        接收用户数据                       发送数据        打印数据与发送者的ip               接收数据        返发一个消息                        打印   代码:         UdpA.c   #include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<string.h> #include<sys/socket.h> #include<netinet/in.h>     void  main() {       int fd;  //socket描述符       struct sockaddr_in  ad;  //本机的ip地址       char buf[100];           //接收数据缓存       struct sockaddr_in   ad_snd; //发送者ip地址       int r;       socklen_t len;//发送者ip长度             fd=socket(AF_INET , SOCK_DGRAM ,0); //0默认代表UDP协议, //17也代表UDP协议           if(fd== -1) printf(“socket:%m\n”),exit(-1);           printf(“建立socket成功!\n”);           ad.sin_family=AF_INET;          ad.sin_port=htons(11111);           inet_aton(“127.0.0.1”, &ad.sin_addr);           r=bind(fd,(structsockaddr*)&ad,sizeof(ad));           if(r==-1) printf(“bind:%m\n”),exit(-1);           printf(“绑定成功!\n”);           while(1) {              len=sizeof(ad_snd);              r=recvfrom(fd,buf,sizeof(buf),0, (struct sockaddr*)&ad_snd,&len);              sendto(fd, “收到信息”,strlen(“收到信息”),0, (structsockaddr*)&ad_snd,sizeof(ad_snd));              if(r>0)              {                   Buf[r]=0;                    printf(“发送者IP:%s,数据:%s\n”, inet_ntoa(ad_snd.sin_addr),buf);               }               if(r==0)               {                    printf(“关闭!\n”);                    close(fd);                    break;               }               if(r==-1)               {                    printf(“网络故障!\n”);                    close(fd);                    break;                } } close(fd);           }   udpB.c   #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<netdb.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>   void  main() {       int fd;       struct sockadddr_in ad;       char buf[102];       int r;       fd=socket(AF_INET,SOCK_DGRAM,0);       if(fd==-1) printf(“socket err:%m\n”),exit(-1);       ad.sin_family=AF_INET;       ad.sin_port=htons(11111);       ad.sin_addr.s_addr=inet_addr(“127.0.0.1”);       while(1)       {            r=read(0,buf,sizeof(buf)-1);            if(r<=0) break;            buf[r]= ‘\0’;            r=sendto(fd,buf,r,0,(structsockaddr*)&ad,sizeof(ad));            if(r==-1)break;            bzero(buf,sizeof(buf));            r=recv(fd,buf,sizeof(buf),0);            buf[r]= ‘\0’;            printf(“来自接收方的数据:%s\n”,buf);                  }      close(fd); }     总结:1.connect+send==sendto 2.    recvfrom的作用不是专门从指定ip接收 而是从任意ip接收数据,返回发送者ip地址 3.    为什么要bind,bind主要目的告诉网络发送目标 4.    是否一定绑定才能发送数据? 否:只要知道你的ip和端口,就能发送数据 5.    为什么发送者没有绑定ip和端口,它也有端口 底层网络驱动,帮我们自动生成ip与端口     TCP编程的数据特点 TCP的编程模型: 案例1:         TCP的服务器(在案例中使用浏览器作为客户程序)       socket建立服务器的文件描述符号缓冲       bind把IP地址与端口设置到文件描述符号中       listen负责根据客户连接的不同IP与端口,负责生成对应的文件描述符号及其信息       accept一旦listen有新的描述符号产生就返回,否则阻塞。     代码: Tcp_sev.c   #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>   void   main() {       int serverfd;       int cfd;       struct sockaddr_in  saddr;       struct sockaddr_in  caddr;           int  r;           socklen_t  len;       //1.socket       serverfd=socket(AF_INET,SOCK_STREAM,0);       if(serverfd==-1) printf(“socket:%m\n”),exit(-1);       printf(“建立服务器socket成功!\n”);       //2.bind       saddr.sin_family=AF_INET;       saddr.sin_port=htons(9999);       inet_aton(“127.0.0.1”,&saddr.sin_addr);       r=bind(serverfd,(structsockaddr*)&saddr,sizeof(saddr));       if(r==-1) printf(“bind :%m\n”),exit(-1);       printf(“服务器地址绑定成功!\n”);       //3.listen       r=listen(serverfd,10);//参数1:服务器sock描述符,参数2:连//接最大数       if(r==-1) printf(listen:%m\n),exit(-1);       printf(“监听服务器成功!\n”);       //4.accept       len=sizeof(caddr);       While(1)       {            cfd=accept(serverfd,(structsockaddr*)&caddr,&len);            //参数1:服务器端socket描述符,参数2:返回客户端地址,//参数3:返回客户端地址实际的长度(传入和返回双向参数)            printf(“有人连接:%d,IP:%s,%u\n”, cfd,inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));        } }   客户端:浏览器地址栏输入:http://127.0.0.1:9999         服务器端会显示浏览器连接服务器端        案例2:             每个客户的代理描述符号的通信(客户端浏览器)             通过accept返回的描述符进行通信 代码:   #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>   void   main() {       int serverfd;       int cfd;       struct sockaddr_in  saddr;       struct sockaddr_in  caddr;           int  r;           socklen_t  len;           char buf[1024];       //1.socket       serverfd=socket(AF_INET,SOCK_STREAM,0);       if(serverfd==-1) printf(“socket:%m\n”),exit(-1);       printf(“建立服务器socket成功!\n”);       //2.bind       saddr.sin_family=AF_INET;       saddr.sin_port=htons(9999);       inet_aton(“127.0.0.1”,&saddr.sin_addr);       r=bind(serverfd,(structsockaddr*)&saddr,sizeof(saddr));       if(r==-1) printf(“bind :%m\n”),exit(-1);       printf(“服务器地址绑定成功!\n”);       //3.listen       r=listen(serverfd,10);//参数1:服务器sock描述符,参数2:连//接最大数       if(r==-1) printf(listen:%m\n),exit(-1);       printf(“监听服务器成功!\n”);       //4.accept       len=sizeof(caddr);       cfd=accept(serverfd,(struct sockaddr*)&caddr,&len);       //参数1:服务器端socket描述符,参数2:返回客户端地址, //参数3:返回客户端地址实际的长度(传入和返回双向参数)       printf(“有人连接:%d,IP:%s,%u\n”, cfd,inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));           //5.处理代理客户描述符号的数据           r=recv(cfd,buf,1024,0);           if(r>0)           {               buf[r]= ‘\0’;               printf(“::%s\n”,buf);            }            if(r==0)            {                 printf(“连接断开!\n”);            }            if(r==-1)            {                  Printf(“网络故障!\n”);             } }   //客户端:浏览器地址栏:http://127.0.0.1:9999/index.html           服务器端会接收到数据,并打印             TCP通信特点(相对UDP)  案例3:              有连接:主要连接后,发生数据不用指定IP与端口              数据无边界:TCP数据流,非数据报文              数据准确:TCP协议保证数据完全正确 代码: 服务器端:   #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>   void   main() {       int serverfd;       int cfd;       struct sockaddr_in  saddr;       struct sockaddr_in  caddr;           int  r;           socklen_t  len;           char buf[1024];       //1.socket       serverfd=socket(AF_INET,SOCK_STREAM,0);       if(serverfd==-1) printf(“socket:%m\n”),exit(-1);       printf(“建立服务器socket成功!\n”);       //2.bind       saddr.sin_family=AF_INET;       saddr.sin_port=htons(9999);       inet_aton(“127.0.0.1”,&saddr.sin_addr);       r=bind(serverfd,(structsockaddr*)&saddr,sizeof(saddr));       if(r==-1) printf(“bind :%m\n”),exit(-1);       printf(“服务器地址绑定成功!\n”);       //3.listen       r=listen(serverfd,10);//参数1:服务器sock描述符,参数2:连//接最大数       if(r==-1) printf(listen:%m\n),exit(-1);       printf(“监听服务器成功!\n”);       //4.accept       len=sizeof(caddr);       cfd=accept(serverfd,(structsockaddr*)&caddr,&len);       //参数1:服务器端socket描述符,参数2:返回客户端地址, //参数3:返回客户端地址实际的长度(传入和返回双向参数)       printf(“有人连接:%d,IP:%s,%u\n”, cfd,inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));           //5.处理代理客户描述符号的数据           while(1) {               r=recv(cfd,buf,1024,0);               if(r>0)              {                  buf[r]= ‘\0’;                  printf(“::%s\n”,buf);              }              if(r==0)             {                 printf(“连接断开!\n”);             }              if(r==-1)             {                  Printf(“网络故障!\n”);              } } }   客户端:   #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>   void   main() {       int fd;       struct sockaddr_in  addr;           int  r;       //1.socket       fd=socket(AF_INET,SOCK_STREAM,0);       if(fd==-1) printf(“socket:%m\n”),exit(-1);       printf(“建立客户端socket成功!\n”);       //2.connect       addr.sin_family=AF_INET;       addr.sin_port=htons(9999);       inet_aton(“127.0.0.1”,&addr.sin_addr);       r=connect(fd,(structsockaddr*)&addr,sizeof(addr));       if(r==-1) printf(“bind :%m\n”),exit(-1);       printf(“连接服务器成功!\n”);       for(i=0;i<20;i++) {                r=send(fd,“hello”,5,0);          } }   总结:客户端循环发生20次hello,服务器端接收的数据,并不是分20次接收20个hello。说明:TCP是通过流式数据发生的,而不是数据报文。 而UDP是通过数据报文传送的,是分20次接收20个hello   recv函数的flags参数:      MSG_WAITALL :一定要把缓冲填满在发送                   TCP流式数据传送(数据无边界),有可能缓冲没有填满,数据就发送了。   案例4:        使用TCP发送数据注意:             不要以为固定长的数据,一定接收正确,要使用MSG_WAITALL使固定长的数据接收正确。   代码:      服务器端:   #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>   void   main() {       int serverfd;       int cfd;       struct sockaddr_in  saddr;       struct sockaddr_in  caddr;           int  r;           socklen_t  len;           int a;       //1.socket       serverfd=socket(AF_INET,SOCK_STREAM,0);       if(serverfd==-1) printf(“socket:%m\n”),exit(-1);       printf(“建立服务器socket成功!\n”);       //2.bind       saddr.sin_family=AF_INET;       saddr.sin_port=htons(9999);       inet_aton(“127.0.0.1”,&saddr.sin_addr);       r=bind(serverfd,(structsockaddr*)&saddr,sizeof(saddr));       if(r==-1) printf(“bind :%m\n”),exit(-1);       printf(“服务器地址绑定成功!\n”);       //3.listen       r=listen(serverfd,10);//参数1:服务器sock描述符,参数2:连//接最大数       if(r==-1) printf(listen:%m\n),exit(-1);       printf(“监听服务器成功!\n”);       //4.accept       len=sizeof(caddr);       cfd=accept(serverfd,(structsockaddr*)&caddr,&len);       //参数1:服务器端socket描述符,参数2:返回客户端地址, //参数3:返回客户端地址实际的长度(传入和返回双向参数)       printf(“有人连接:%d,IP:%s,%u\n”, cfd,inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));           //5.处理代理客户描述符号的数据           while(1) {               r=recv(cfd,&a,4,MSG_WAITALL);               if(r>0)              {                  buf[r]= ‘\0’;                  printf(“::%s\n”,buf);              }              if(r==0)             {                 printf(“连接断开!\n”);             }              if(r==-1)             {                  Printf(“网络故障!\n”);              } } }   客户端:   #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>   void   main() {       int fd;       struct sockaddr_in  addr;           int  r;       //1.socket       fd=socket(AF_INET,SOCK_STREAM,0);       if(fd==-1) printf(“socket:%m\n”),exit(-1);       printf(“建立客户端socket成功!\n”);       //2.connect       addr.sin_family=AF_INET;       addr.sin_port=htons(9999);       inet_aton(“127.0.0.1”,&addr.sin_addr);       r=connect(fd,(structsockaddr*)&addr,sizeof(addr));       if(r==-1) printf(“bind :%m\n”),exit(-1);       printf(“连接服务器成功!\n”);       for(i=0;i<20;i++) {                r=send(fd,&i,4,0);  //发送i的整数          } }   总结:当客户端发送整数时,因为TCP是流式数据无边界,发送数据时,缓冲有可能填不满就发送给服务器端了,这使得服务器端接收的数据有误。 解决方案:recv函数的flags参数使用MSG_WAITALL标记,这样没回发送数据的缓冲都会被填满在发送,这样服务器端就能正确接收整数了。           案例5:          TCP数据发送的分析:              基本数据intshort long float double              结构体数据struct              建议使用MSG_WAITALL(固定长)                字符串数据以及文件数据等不固定长度的数据怎么发送?                        指定数据包:                 头:大小固定(存放数据大小)                 体:大小变化(数据)          使用TCP传送文件              定义文件数据包              int 数据大小              char[]数据           传递文件名           传递数据(循环)           传递0长度的数据表示文件结束 代码: 客户端: #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<fcntl.h>     void   main() {       int  sfd;       int  ffd;       int  size;       int r;       int len;       char buf[128];       struct sockaddr_in  dr;       char filename[]= “udp_a.c”;      //1建立socket      sfd=socket(AF_INET,SOCK_STREAM,0);      if(sfd==-1) printf(“:%m\n”),exit(-1);      printf(“socket成功!\n”);      //2连接到服务器      dr.sin_family=AF_INET;       dr.sin_port=htons(9988);       inet_aton(“127.0.0.1”,&dr.sin_addr);       r=connect(sfd,(structsockaddr*)&dr,sizeof(dr));       if(r==-1)printf(:%m\n),exit(-1);       printf(“connect成功!\n”);      //3发送文件名      len=strlen(filename);      r=send(sfd,&len,sizeof(len),0);      if(r==-1)printf(“:%m\n”),exit(-1);      r=send(sfd,filename,len,0);      //4打开文件      ffd=open(filename,O_RDONLY);      if(ffd==-1)printf(“:%m\n”),exit(-1);      printf(“open文件成功!\n”);      //5循环发送数据      while(1)      {           size=read(ffd,buf,128);           if(size==-1)break;           if(size==0) break;           if(size>0)           {                r=send(sfd,&size,sizeof(size),0);//发送数据长度                if(r==-1)break;                r=send(sfd,buf,size,0);//发送数据                if(r==-1)break;           }      }      //6读取到文件尾,发送0的数据包      size=0;     r=send(sfd,&size,sizeof(size),0);      close(ffd);      close(sfd);      printf(“ok\n”); }   服务器端:   #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<sys/socket.h> #include<netinet/in.h> #include<aarpa/inet.h> #include<fcntl.h>   void  main() {       int sfd,cfd,ffd;       int r;       int len;       char buf[128];       char filename[100];       struct sockaddr_in dr;      //1建立服务器socket      sfd=socket(AF_INET,SOCK_STREAM,0);      if(sfd==-1)printf(“:%m\n”),exit(-1);      printf(“建立服务器成功!\n”);      //2绑定ip地址端口      dr.sin_family=AF_INET;      dr.sin_port=htons(9988);     dr.sin_addr.s_addr=inet_addr(“127.0.0.1”);      r=bind(sfd,(structsockaddr*)&dr,sizeof(dr));      if(r==-1)printf(“:%m\n”),exit(-1);      printf(“绑定地址成功!\n”);      //3监听      r=listen(sfd,10);      if(r==-1)printf(“:%m\n”),exit(-1);      printf(“监听成功!\n”);      //4接收连接      cfd=accept(sfd,0,0);      if(cfd==-1)printf(“:%m\n”),exit(-1);      printf(“开始接收文件\n”);      //接收文件名      r=recv(cfd,&len,sizeof(len),MSG_WAITALL);     r=recv(cfd,filename,len,MSG_WAITALL);      filename[len]= ‘\0’;      printf(“传递的文件名是:%s\n”,filename);      //6创建文件      ffd=open(filename,O_RDWR|O_CREAT,0666);      //7循环接收文件数据      while(1)      {           r=recv(cfd,&len,sizeof(len),MSG_WAITALL);           if(len==0) break;          r=recv(cfd,buf,len,MSG_WAITALL);           write(ffd,buf,len);      }      close(ffd);      close(cfd);      close(sfd);      printf(“接收文件完成!\n”);      }   TCP的服务器的编程 TCP的服务器端维护多个客户的网络文件描述符。 对服务器多个客户描述符同时做读操作,是不可能的,需要使多任务 多任务模型? 1.    多进程 2.    IO的异步模式(select模式/poll模式) 3.    多线程模式 4.    多进程池 5.    多线程池     综合应用--多进程应用        1.怎样使用多进程        2.多进程的缺陷,以及怎么解决         客户端               1.建立socket               2.连接服务器               3.创建子进程               4.在父进程中,输入,发送聊天信息               5.在子进程中,接收服务器传递其他客户聊天信息   客户端: #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>   int fd; int r; struct sockaddr_in dr; int initSocket(); void destroy;   void handle(int s) {      int  status;      wait(&status);      destroy();      exit(-1); }     void main() {       fd=initSocket();       if(fd== -1) printf(“连接网络失败!\n”),exit(-1);       printf(“连接网络\n”);       signal(SIGCHLD,handle);       if(fork())       {             //输入,发送             char  buf[256];             while(1)             {                  printf(“请输入消息:\n”);                  scanf(“%s”,buf);                   if(strcmp(buf, “1”)) break;                   send(fd,buf,strlen(buf),0);             }       }       else       {             //接收,显示             char  buf[256];             while(1)             {                     r=recv(fd,buf,255,0);                     buf[r]= ‘\0’;                    printf(“MSG:%s\n”,buf);                               }       }       destroy(); }   int initSocket() { fd=socket(AF_INET,SOCK_STREAM,0); if(fd== -1)return -1; dr.sin_family=AF_INET; dr.sin_port=htons(9988); dr.sin_addr.s_addr=inet_addr(“127.0.0.1”); r=connect(fd,(structsockaddr*)&dr,sizeof(dr)); if(r== -1) {   close(fd);   return -1; }   return fd; }   void destroy() {    close(fd); }   服务器端:   #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/men.h>   int sfd; int *fds;//存放所以客户代理描述符号 int idx=0;//客户在数组中的下标 struct sockaddr_in  dr; int  r;   void main() {      fds=mmap(0,4*100,PROT_READ|PROT_WRITE|MAP_ANONYMOUS|MAP_SHARED,0,0);      //1建立服务器socket      bzero(fds,sizeof(fds));      sfd=socket(AF_INET,SOCK_STREAM,0);      if(sfd== -1) printf(“:%m\n”),exit(-1);      printf(“socket ok \n”);      //2绑定地址      dr.sin_family=AF_INET;      dr.sin_port=htons(9988);      dr.sin_addr.s_addr=inet_addr(“127.0.0.1”);      r=bind(sfd,(struct sockaddr*)&dr,sizeof(dr));      if(r== -1) printf(“:%m\n”) ,exit(-1);      printf(“bind ok\n”);      //3监听      r=listen(sfd,10);      if(r== -1) printf(“:%m\n”),exit(-1);      //4循环接收客户连接      while(1)      {             fds[idx]=accept(sfd,0,0);             if(fds[idx]= = -1) break;             printf(“有客户连接\n”);             //5建立一个子进程             if(fork())             {                    idx++;                    continue;             }              else             {                      //6.子进程任务:接收客户数据且广播                     char  buf[256];                     int  i;                     while(1)                     {                            //接收客户数据                            r=recv(fds[idx],buf,255,0);                            if(r==0)                            {                                   printf(“有客户退出\n”);                                  close(fds[idx]);                                   fds[idx]=0;                                   break;                            }                            if(r== -1)                            {                                   printf(“网络故障\n”);                                  close(fds[idx]);                                   fds[idx]=0;                                   break;                            }                            buf[r]= ‘\0’;                            printf(“来自客户的数据:%s\n”,buf);                            //广播                            for(i=0;i<100;i++)                            {                                  if(fds[i]>0)                                   {                                       send(fds[i],buf,r,0);                                   }                              }                                               }                     exit(0);             }        }     }     总结:       建立socket      绑定地址      监听      循环接收客户连接      为客户创建子进程      在子进程接收该客户的数据,并且广播        问题:多进程由于进程资源结构独立。            新进程的文件描述符号的环境在老进程中是无法访问的。       SELECT TCP服务器模式:   1.       select函数:通过异步方式中断处理文件描述符         #include<sys/select.h>           int select(                  int fds,//建议是监控的文件描述符号的最大值+1                  fd_set *readfds,//读文件描述符号集合                                       //该参数既是输入,也是输出                                       //输入:被监控的描述符号                                       //输出:有数据的描述符号                  fd_set *writefds,                  fd_set *errfds,                  struct timeval*timeout);//指定阻塞时间限制                                                     //为NULL,永久           返回:                         >0:发生改变的文件描述符号个数                         =0:时间限制过期                   =-1:异常                      2.IO能否发出信号?                  异步IO就是通过信号工作.   例子:   #include<stdio.h> #include<fcntl.h> #include<signal.h> #include<unistd.h>   void   handle(int s) {        char buf[200];        int   r;        r=read(0,buf,199);        buf[r]= ‘\0’;        printf(“::%s\n”,buf);   }     void   main() {        fcntl(0,F_SETFL,O_ASYNC); //设置输入io可发出信号(当有输入时)        // O_ASYNC     当I/O可用的时候,允许SIGIO信号发送到进程组        fcntl(0,F_SETOWN,getpid()); //设置输入io描述符号有变化SIGIO信号发给本进程        signal(SIGIO,handle);        while(1);                 //进程执行死循环 }                异步io:fcntl函数设置输入io为异步io,该进程执行死循环,当输入io的文件描述符有变化(有输入发生)时,io就会发出信号SIGIO,死循环就会停止执行,并执行信号处理函数,信号处理函数执行完毕后,死循环再次继续执行。   Select的结构:              Select管理描述符集合,设置描述符为异步,并设置信号处理,执行死循环等待描述符的改变。     3.select应用:   #include<stdio.h> #include<fcntl.h> #include<signal.h> #include<unistd.h> #include<sys/select.h>     void   main() {        fd_set fds;        int r;        char buf[100];        while(1)        {              FD_ZERO(&fds); //FD_ZERO函数宏,清空文件描述符集合              FD_SET(0,&fds); //FD_SET函数宏,添加文件描述符到描述符集合中              r=select(1,&fds,0,0,0);              printf(“有数据输入\n”);              r=read(0,buf,99);              printf(“%s\n”,buf);        } }   4.使用select实现TCP的多客户连接与处理   #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/select.h>   void  main() {      int sfd;           //服务器描述符      int fdall[100];    //客户描述符      int count;       //客户的个数      int r;           //返回值(异常处理)      struct sockaddr_in  dr;   //ip地址与端口      fd_set  fds;      //被select监控的描述符集合      int maxfd;      //最大文件描述符号      char buf[1024];      int i,j; //循环变量      //1.建立socket      sfd=socket(AF_INET,SOCK_STREAM,0);      if(sfd== -1)  printf(“:%m\n”),exit(-1);      //2.绑定地址与端口      dr.sin_family=AF_INET;      dr.sin_port=htons(8866);      dr.sin_addr.s_addr=inet_addr(“127.0.0.1”);      r=bind(sfd,(struct sockaddr *)&dr,sizeof(dr));      if(r == -1) printf(“:%m\n”),close(sfd),exit(-1);      //3.监听      r=listen(sfd,10);      if(r== -1) printf(“:%m\n”),close(sfd),exit(-1);      count=0;      maxfd=0;      FD_ZERO(&fds);      for(i=0;i<100;i++)      {          fdall[i]= -1;      }      while(1)      {              //4.构造监听的描述符号集合【服务器描述符号,客户描述符集合】              //4.1.清空              FD_ZERO(&fds);  //每次都要清空,因为select返回的fds中是有变化的描//述符,而不是所以连接的客户端描述符              maxfd=0;              //4.2加入服务器描述符              FD_SET(sfd,&fds);             maxfd=maxfd>=sfd?maxfd:sfd;              //4.3加入客户描述符              for(i=0;i<count;i++)              {                     if(fdall[i]!= -1)                     {                          FD_SET(fdall[i],&fds);                           maxfd=maxfd>=fdall[i]?maxfd:fdall[i];                     }              }              //5.使用select循环控制描述符号集合              r=select(maxfd+1,&fds,0,0,0);              //6.分两种情况处理:              //6.1.有客户连接:服务器描述符号              if(FD_ISSET(sfd,&fds))              {                     fdall[count]=accept(sfd,0,0);                     if(fdall[count]== -1)                     {                           printf(“服务器崩溃\n”);                           exit(-1);                     }                     printf(“有客户连接\n”);                     count++;              }              //6.2.有客户发送数据:客户描述符号              for(i=0;i<count;i++)              {                    //判断改变描述号是否存在                    if(fdall[i]!= -1 && FD_ISSET(fdall[i],&fds))                    {                         //读取数据                         r=recv(fdall[i],buf,1023,0);                         if(r==0)                         {                               printf(“有客户退出\n”);                               close(fdall[i]);                               fdall[i]= -1;                         }                         if(r== -1)                         {                               printf(“网络故障\n”);                               close(fdall[i]);                               fdall[i]= -1;                         }                         if(r>0)                         {                              buf[r]= ‘\0’;                              printf(“广播数据\n”);                              //广播数据                              for(j=0;j<count;j++)                              {                                                                             if(fdall[j]!=-1)                                       {                                               send(fdall[j],buf,r,0);                                      }                              }                                                      }                     }                } }  }     poll模式:    poll函数:          int poll(                         struct pollfd *fds,//监控的描述符号,监听的结构体数组                         int nfds,//监控的描述符号的个数                         int timeout      );     //阻塞超时,-1为永久              struct pollfd{                 int   fd;       //监听的描述符号                 short  events;  //监听的操作POLLIN监听输入,POLLOUT监听输出                 short  revents; //返回的事件            };     例子代码:                      #include<stdio.h> #include<unistd.h> #include<sys/poll.h>     void   main() {        struct pollfd  fds[1];        int r;        char buf[100];        fds[1].fd=0;            //监听的描述符号        fds[1].events=POLLIN;  //监听读取        while(1)        {                           r=poll(fds,1,-1);              if(fds[0].revents == POLLIN)              {                    printf(“有数据输入\n”);              }              r=read(0,buf,99);              printf(“%s\n”,buf);        } }       poll模式和select模式基本一样,只是使用风格不同     socket选型设置:      通用选项:                 SOL_SOCKET                         SO_BROADCAST    广播                         SO_RCVBUF        描述符号的缓冲大小                         SO_SNDBUF        描述符号的缓冲大小                         SO_REUSEADDR    地址可多次绑定                         SO_TYPE           描述符号类型SOCK_STREAM 等       ICMP选项                 IPPROTO_ICMP                      ICMP_FILTER       IP选项(干预系统怎么生产ip头)             IPPROTO_IP                    UDP选项            IPPROTO_UDP       TCP选项             IPPROTO_TCP   设置选项:setsocket 获取选项:getsocket     #include<sys/socket.h>   int  getsocket(int s , int level , int  optname,  void * optval ,  socklen_t optlen); int  setsocket(int  s ,int  level ,int optname , const void * optval,socklen_t optlen); 参数1:socket描述符号 参数2:设置那一层的选项(SOL_SOCKET(通用),IPPROTO_UDP,IPPROTO_TCP 等) 参数3:选项名 参数4:选项值 参数5:值的大小     案例:      判定一个socket的数据类型SOCK_STREAM SOCK_DGRAM SOCK_RAW   代码:   #include<stdio.h> #include<sys/socket.h> #include<netinet/in.h>     void  main() {      int  fd;      int  type;      int  len;      len=sizeof(type);      fd=socket(AF_INET,SOCK_STREAM,0);      getsocket(fd,SOL_SOCKET,SO_TYPE,&type,&len);      printf(“%u:%u”,SOCK_STREAM,type);      if(type&SOCK_STREAM)      {            printf(“流!\n”);      }      if(type&SOCK_DGRAM)      {             printf(“报文\n”);      } }     案例2:       获得socket缓冲大小   代码:   #include<stdio.h> #include<sys/socket.h> #include<netinet/in.h>     void  main() { int   fd; int   type; int   len; len=sizeof(type); fd=socket(AF_INET,SOCK_STREAM,0); getsocket(fd,SOL_SOCKET,SO_RCVBUF,&type,&len); printf(“缓冲大小:%u\n”,type); }     案例:      使用选项进行数据广播SO_BROADCAST(对流无效)      cast_A发送           建立socket           设置广播选型           发送数据(广播方式发送)      cast_B接收            建立socket            设置地址可重用选型SO_REUSEADDR            绑定地址            接收数据   代码: Cast_A.c   #include<stdio.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>     void   main() {          int   fd;          int   opt=1;    //广播操作,1为广播,0为不广播          struct  sockaddr_in  dr;          fd=socket(AF_INET,SOCK_DGRAM,0);          setsocket(fd,SOL_SOCKET,SO_BROADCAST,&opt,sizeof(opt));          dr.sin_family=AF_INET;          dr.sin_port=htons(9999);          dr.sin_addr.s_addr=inet_addr(“/*广播地址*/”); //地址不在是目标地址,而是广播地址          sendto(fd, “hello”,5,0,(struct sockaddr*)&dr,sizeof(dr)); }         Cast_B.c   #include<stdio.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<stdlib.h> #include<unistd.h>     void   main() {          int   fd;          int   opt=1;    //广播操作,1为广播,0为不广播          struct  sockaddr_in  dr;          char   buf[100];          int  r;          fd=socket(AF_INET,SOCK_DGRAM,0);          if(fd == -1) printf(“%m\n”),exit(-1);          r=setsocket(fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));          if(r == -1) printf(“%m\n”),exit(-1);          dr.sin_family=AF_INET;          dr.sin_port=htons(9999);          dr.sin_addr.s_addr=inet_addr(“/*广播地址*/”); //地址不在是目标地址,而是广播地址          r=bind(fd, (struct sockaddr*)&dr,sizeof(dr));          if(r == -1) printf(“%m\n”),exit(-1);          r=recv(fd,buf,100,0);          buf[r]= ‘\0’;          printf(“接收广播数据:%s\n”,buf);          close(fd); }         OOB数据(TCP)      优先数据      send(,MSG_OOB)      recv(,MSG_OOB); 案例:      Oob_server.c            Recv   MSG_OOB      Oob_client.c            Send   MSG_OOB   代码: Oob_server.c   #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>     void main() {        int  fd,cfd;        char buf[100];        int  r;        struct sockaddr_in dr;        fd=socket(AF_INET,SOCK_STREAM,0);        if(fd == -1) printf(“%m\n”),exit(-1);        dr.sin_family=AF_INET;        dr.sin_port=htons(9999);        dr.sin_addr.s_addr=inet_addr(“127.0.0.1”);        r=bind(fd,(structsockaddr*)&dr,sizeof(dr));        if(r == -1) printf(“%m\n”),exit(-1);        r=listen(fd,10);        if(r == -1) printf(“%m\n”),exit(-1);        cfd=accept(fd,0,0);        if(cfd == -1) printf(“%m\n”),exit(-1);        while(1)         {             r=recv(cfd,buf,100,MSG_OOB);             if(r>0)             {                buf[r]= ‘\0’;                printf(“%s\n”,buf);               }               if(r<=0)                   break;        }        close(cfd);        close(fd); }     Oob_client.c        #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>     void  main() {       int  fd;       int  r;       struct sockaddr_in dr;       fd=socket(AF_INET,SOCK_STREAM,0);       if(fd == -1)  printf(“%m\n”),exit(-1);       dr.sin_family=AF_INET;       dr.sin_port=htons(9999);       dr.sin_addr.s_addr=inet_addr(“127.0.0.1”);       r=connect(fd,(struct sockaddr*)&dr,sizeof(dr));       if(r == -1)  printf(“%m\n”),exit(-1);       send(fd, “hello”,5,MSG_OOB);       while(1);          close(fd); }   总结:服务器端程序只接收到一个数据o 1.       OOB数据只能一个字符 2.       普通数据使用一般方式接收与发送,OOB数据使用MSG_OOB接收和发送 3.       一个数据使用MSG_OOB,则最后一个是OOB数据,其他非OOB数据 4.       问题:OOB数据是优先数据,优先体现在什么地方                      当有OOB数据时,程序所有都停下来先处理OOB数据       HTTP协议以及应用      HTTP是应用协议      HTTP协议分成:            请求协议(浏览器发给web服务器)            响应协议   请求协议的格式: 请求行(请求方法请求资源 协议版本) 请求体(请求头:请求值) 空行 数据(querystring:key=value&key=value)       案例:      向服务器192.168.0.72:80 发送请求,获得index.php页面。   代码: #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>   void  main() {         int  fd;         struct sockaddr_in  dr;         char strreq[1024];         char buf[10*1024];         int  r;         //建立socket         fd=socket(AF_INET,SOCK_STREAM,0);         //连接服务器192.168.0.72          dr.sin_family=AF_INET;          dr.sin_port=htons(80);          dr.sin_addr.s_addr=inet_addr(“192.168.0.72”);          r=connect(fd,(struct sockaddr*)&dr,sizeof(dr));         //构建http请求字符串         sprintf(strreq, “GET /index.php HTTP/1.1\r\n” //获得index.php页面 “Host: 192.168.0.72:80\r\n”      “User-Agent: Mozilla/5.0\r\n”  //User-Agent: 使用的浏览器 “Accept:text/html,image/png\r\n”//Accept: text/支持文本(html)//image/png(支持图片) “Accept-Language: zh-ch\r\n”   //支持的语言  zh-ch 中文 “Accept-Charset: gb2312,utf-8\r\n” //支持的字符集 gb2312 utf-8 “Keep_Alive: 300\r\n”         //连接保持300秒 “Connection: keep-alive\r\n” “\r\n”);         //发送http请求字符串         r=send(fd,strreq,strlen(strreq),0);         //等待服务器响应         printf(“=======================\n”);         while(1)         {                r=recv(fd,buf,1024,0);                if(r<=0) break;                printf(“%s\n”,buf); } printf(“=======================\n”); close(fd); }     响应请求格式:     响应行(协议版本响应码 响应码的文本描述)     响应体(响应头: 响应值)     空行     数据(普通数据/分块数据)                 响应码分类:                    1XX      正在处理              2XX              响应成功200              3XX              继续处理              4XX              客户错误              5XX              服务器错误  

上一篇:谁在用Hadoop
下一篇:4.1 指针和链式结构

相关文章

相关评论