网络通信协议TCP

来源:互联网 发布:影视公司 知乎 编辑:程序博客网 时间:2024/06/03 19:04

TCP的上一层是应用层,TCP向应用层提供可靠的面向对象的数据流传输服务,TCP数据传输实现了从一个应用程序到另一个应用程序的数据传递。它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信。),应用程序通过向TCP层提交数据接发送/收端的地址和端口号而实现应用层的数据通信。

通过IP的源/目的可以惟一地区分网络中两个设备的连接,通过socket的源/目的可以惟一地区分网络中两个应用程序的连接。

三次握手

TCP是面向连接的,所谓面向连接,就是当计算机双方通信时必需先建立连接,然后进行数据通信,最后拆除连接三个过程。TCP在建立连接时又分三步走:

第一步(A->B):主机A向主机B发送一个包含SYN即同步(Synchronize)标志的TCP报文,SYN同步报文会指明客户端使用的端口以及TCP连接的初始序号;

第二步(B->A):主机B在收到客户端的SYN报文后,将返回一个SYN+ACK的报文,表示主机B的请求被接受,同时TCP序号被加一,ACK即确认(Acknowledgement)。

第三步(A->B):主机A也返回一个确认报文ACK给服务器端,同样TCP序列号被加一,到此一个TCP连接完成

TCP/IP核心协议

三次握手

 

UDP

UDP即用户数据报协议,是一种面向无连接的不可靠传输协议,不需要通过3次握手来建立一个连接,同时,一个UDP应用可同时作为应用的客户或服务器方。

由于UDP协议并不需要建立一个明确的连接,因此建立UDP应用要比建立TCP应用简单得多。UDP比TCP协议更为高效,也能更好地解决实时性的问题,如今,包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都使用UDP协议。


TCP/IP核心协议

协议选择

协议的选择应该考虑到数据可靠性、应用的实时性和网络的可靠性。

对数据可靠性要求高的应用需选择TCP协议,而对数据的可靠性要求不那么高的应用可选择UDP传送。


TCP协议中的3次握手、重传确认等手段可以保证数据传输的可靠性,但使用TCP协议会有较大的时延,因此不适合对实时性要求较高的应用;而UDP协议则有很好的实时性。

网络状况不是很好的情况下需选用TCP协议(如在广域网等情况),网络状况很好的情况下选择UDP协议可以减少网络负荷

   

Socket是一个网络接口,不同的协议之间的差异性操作,则交给socket自行解决

是一种特殊的IO接口,是一种文件描述符;

是一种常用的进程之间的通信,本地,不同主机之间通信;

Socket可用网络地址结构

{协议,本地地址,本地端口}表示

 

套接字类型:

1.流式套接字SOCK_STREAM

流式的套接字可以提供可靠的、面向连 接的通讯流。它使用了TCP协议。TCP保证了数据传输的正确性和顺序性;

2. 数据报套接字SOCK_DGRAM

数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错,它使用数据报协议UDP。

3. 原始套接字SOCK_RAM

原始套接字允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议的测试等。

对底层协议进行访问,不方便,对一些协议开发。

           

地址结构:

struct sockaddr_in

    {

       short int sin_family;  /* Internet地址族 */

       unsigned short int sin_port;  /* 端口号 */

       struct in_addr sin_addr;   /* IP地址 */

       unsigned char sin_zero[8];  /* 填0 */

     };

       编程中一般并不直接针对sockaddr数据结构操作,而是使用与sockaddr等价的sockaddr_in数据结构

 

现在让我们来看一下一个基于TCP/IP的服务器的流程

1. 创建一个socket,用函数socket()

2. 绑定IP地址、端口等信息到socket上,用函数bind()

3.设置允许的最大连接数,用函数listen()

4.接收客户端上来的连接,用函数accept()

5.收发数据,用函数send()和recv(),或者read()和write()

6.关闭网络连接

 

基于TCP/IP的客户端

1.创建一个socket,用函数socket()

2.设置要连接的对方的IP地址和端口等属性

3.连接服务器,用函数connect()

4.收发数据,用函数send()和recv(),或者

read()和write()

5.关闭网络连接

 

1、socket()

函数的作用:创建一个socket()套接字

函数的头文件:#include <sys/socket.h>

函数的原型:int socket(int domain, int type, int protocol);

函数的参数:

         Domain:表示使用何种地址类型

                 AF_INET:IPV4网络络协议

                 AF_INET6:IPV6网络协议

        Type:SOCK_STREAM,TCP:面向数据流的

              SOCK_DGRAM,UDP:使用不连续不可信赖的数据包连接

              SOCK_RAW          提供原始网络协议

        Protocol:指定传输协议编号,一般设为0即可

函数的返回值:

        成功:返回套接字描述符

        出错:-1


2、bind()

函数的作用:绑定IP地址

函数的原型:int bind(int sockfd, struct sockaddr * hostaddr, int addrlen)

函数的头文件:#include <sys/types.h>、#include <sys/socket.h>

函数的参数:

        Sockfd:socket套接字描述符

        Hostaddr:主机的地址

        Addrlen:sockaddr的地址长度

               Struct sockaddr_in{

                     Unsigned short sin_family;       网络协议

                     Uint6_t  sin_port;             端口号

                     Struct in_addr sin_addr;          地址

                     Unsigned char size_zero[8];        未使用

              };


3、listen()

函数的作用:监听网络、等待连接

函数的原型:int listen(int sockfd, int backlog)

函数的参数:

           Sockfd:套接字描述符

           Backlog:允许接入的客户端的数目

函数的返回值:

         成功:返回0,

         出错:返回 -1

*注意:listen并没有连线,只是设置socket的listen模式,真正连线的是accept。

 

4、accept()

函数的作用:接受网络连接,客户端的连接(这是三次握手的地方)

函数的原型:int accept(int sockfd, struct sockaddr *addr, int *addrlen);

函数的参数:

         Sockfd:套接字描述符

         Addr:连接成功、填充远端客户机的地址

         Addrlen:struct  sockaddr的长度

函数的返回值:

      成功:返回新的套接字描述符(new_sockfd)....与之前的描述符不同

      出错:返回 -1


5、send()

函数的作用:经过socket传送数据,向对方发送数据

函数的原型:int send(int new_sockfd, const void *msg, int len, unsigned int flags)

函数的参数:

       New_sockfd:由accept建立起来的socket连接描述符,连接远方的IP地址

       Msg:发送的数据

       Len:发送的数据长度

   Flags:一般设为0

函数的返回值:

       成功:实际传送出去的字节数

       出错:返回 -1.


6、recv()

函数的作用:经过socket接受数据

函数的原型:int recv(int new_sockfd, void *buf, int len, unsigned flags)

函数的参数:

        New_sockfd:accept以后的socket套接字描述符

        Buf:存放地址

        Len:接收数据的长度

        Flags:一般设为0

函数的返回值:

        成功:返回接收的字节数

        出错:返回 -1.


7、connect()

函数的作用:建立socket连接,通常客户端连接服务器使用

函数的原型:int connect(int sockfd, struct sockaddr *serv_addr, int addrlen)

函数的参数:

        Sockfd:socket套接字描述符

        Serv_addr:表示要连接的服务器的地址

        Addrlen:struct sockaddr的长度

函数的返回值:

       成功:返回0

       出错:返回 -1


8、sendto()

函数的作用:传送socket数据,一般UDP使用

函数的原型:int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *todaar, int tolen)

函数的参数:

     Sockfd:套接字描述符

     Msg:发送的消息内存

     Len:消息长度

     Toaddr:要发送消息的目的地址

     Tolen:sizeof(struct  sockaddr)

函数的返回值:

     成功:返回实际传送的字节数

     出错:返回 -1


9、recvfrom()

函数的作用:从socket接收数据

函数的原型:int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *  fromaddr, int fromlen)

函数的参数:同sendto 一样

函数的返回值:

       成功:返回实际接收的字节数

       出错:返回 -1.

 

常用的字节序的转化函数:

函数的头文件:#include <arpa/inet.h>

从主机发送到网络:

Uint32_t htonl(uint32_t  hostint32);  32位数据传送,从主机到网络。

Uint16_t htons(uint16_t  hostint16);  16位数据传送,从主机到网络.

 

从网络到主机:

Uint32_t ntohl(uint32_t  netint32);    32位的数据接收,从网络到主机

Uint16_t ntohs(uint16_t  netint16);    16位的数据接收,从网络到主机

实例代码:

[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:18px;">#include <stdlib.h>     
  2. #include <stdio.h>     
  3. #include <errno.h>     
  4. #include <string.h>     
  5. #include <netdb.h>     
  6. #include <sys/types.h>     
  7. #include <netinet/in.h>     
  8. #include <sys/socket.h>     
  9.     
  10. #define portnumber 3333    
  11.     
  12. int main(int argc, charchar *argv[])     
  13. {     
  14.     int sockfd,new_fd;     
  15.     struct sockaddr_in server_addr;     
  16.     struct sockaddr_in client_addr;     
  17.     int sin_size;     
  18.     int nbytes;    
  19.     char buffer[1024];    
  20.         
  21.    //1. socket    
  22.     if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1// AF_INET:IPV4;SOCK_STREAM:TCP    
  23.     {     
  24.         fprintf(stderr,"Socket error:%s\n\a",strerror(errno));     
  25.         exit(1);     
  26.     }     
  27.    //2.bind    
  28.     bzero(&server_addr,sizeof(struct sockaddr_in)); // 初始化,置0    
  29.     server_addr.sin_family=AF_INET;                 // Internet    
  30.     server_addr.sin_addr.s_addr=htonl(INADDR_ANY);  // (将本机器上的long数据转化为网络上的long数据)和任何主机通信  //INADDR_ANY 表示可以接收任意IP地址的数据,即绑定到所有的IP    
  31.     //server_addr.sin_addr.s_addr=inet_addr("192.168.1.1");  //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip    
  32.     server_addr.sin_port=htons(portnumber);         // (将本机器上的short数据转化为网络上的short数据)端口号    
  33.     // server_addr.sin_addr.s_addr=inet_addr("192.168.1.199");//;htonl(INADDR_ANY);    
  34.     
  35.     /* 捆绑sockfd描述符到IP地址 */     
  36.     if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)     
  37.     {     
  38.         fprintf(stderr,"Bind error:%s\n\a",strerror(errno));     
  39.         exit(1);     
  40.     }     
  41.    //3.listen    
  42.       if (listen(sockfd, 5)==-1)    
  43.       {    
  44.           printf("listen error\n");    
  45.       }    
  46.     while(1)    
  47.   {    
  48.      sin_size=sizeof(struct sockaddr_in);    
  49.     printf("1\n");    
  50.    //4.accept    
  51.    if( new_fd =  accept(sockfd, (struct sockaddr *)(&client_addr),  &sin_size) <0)    
  52.    {    
  53.     printf("accept error.\n");    
  54.     }    
  55.   fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr));     
  56.    //5.recv/send    
  57.         if((nbytes=read(new_fd,buffer,1024))==-1)     
  58.         {     
  59.             fprintf(stderr,"Read Error:%s\n",strerror(errno));     
  60.             exit(1);     
  61.         }           
  62.         buffer[nbytes]='\0';    
  63.         printf("Server received %s\n",buffer);    
  64.             
  65.         /* 这个通讯已经结束 */     
  66.         close(new_fd);     
  67.         /* 循环下一个 */     
  68.     }     
  69.     
  70.     /* 结束通讯 */     
  71.     close(sockfd);     
  72.     exit(0);     
  73. }</span>   



[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:18px;">#include <stdlib.h>     
  2. #include <stdio.h>     
  3. #include <errno.h>     
  4. #include <string.h>     
  5. #include <netdb.h>     
  6. #include <sys/types.h>     
  7. #include <netinet/in.h>     
  8. #include <sys/socket.h>     
  9.     
  10. #define portnumber 3333    
  11.     
  12. int main()    
  13. {    
  14.    int sockfd;    
  15.     struct sockaddr_in serv_addr;    
  16.     int addlen=sizeof(struct sockaddr);    
  17.     char buf[512]="hello world";    
  18.     
  19.     
  20.    //1.socket    
  21.     if ((sockfd = socket(AF_INET,  SOCK_STREAM, 0))==-1)    
  22.     {    
  23.     printf("socket error");    
  24.     }    
  25.      
  26.           
  27.      bzero(&serv_addr,sizeof(serv_addr));    
  28.      serv_addr.sin_family = AF_INET;    
  29.      serv_addr.sin_port =  htons(portnumber);    
  30.      serv_addr.sin_addr.s_addr=inet_addr("192.168.1.199");    
  31.     //2.connect    
  32.     if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) <0)    
  33.     {    
  34.         printf("connect error\n");    
  35.     }    
  36.         
  37.         
  38.         
  39.         
  40.         
  41.     //3.send    
  42.    if( send(sockfd, buf, strlen(buf), 0 ) <0)    
  43.    {    
  44.        printf("send error\n");    
  45.    }    
  46.     //4.close    
  47.    close(sockfd);    
  48. } </span>  
0 0