现在的位置: 首页 > 综合 > 正文

Linux 网络 UDP TCP select模式 http协议

2018年03月21日 ⁄ 综合 ⁄ 共 32378字 ⁄ 字号 评论关闭

网络:

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              服务器错误

 

抱歉!评论已关闭.