网络编程(二)

来源:互联网 发布:json key 数字 编辑:程序博客网 时间:2024/05/21 19:32

一、流式服务和数据报服务


1、字节流服务:发送端send()只是将数据写到TCP发送缓冲区中,然后将发送缓冲区中的数据打包成报文段发送出去。接收端又将接收到的报文段写到缓冲区中,最后recv()直接取数据。
      字节流服务特点:数据没有明确分割(由底层做分割),不分一定的报文段,什么时候想发便可将写入缓冲区的数据,进行打包再发送,即send()与recv()的次数不完全对等,没有必然联系。

2、数据报服务:发送端sendto()将数据直接打包成相对应的报文段发送。
      数据报服务特点:数据有明确分割,拿数据按报文段拿。发几次接收端收几次。


上一节《TCP》中介绍了TCP报头,再来看一下UDP报头。


源端口号和目的端口号是必须要有的,接下来是16位数据包长度,指UDP报头和数据总长度。这一点和TCP是不同的。16位校验和检验UDP报头和数据的准确性,和TCP不同的是,UDP报头中校验和不是必须有的。


二、UDP编程步骤:

服务器:
1、 socket
2、 bind
3、 recvfrom/sendto
4、 close
客户端:
1、 socket
2、 sendto/recvfrom
3、 close

 //客户端void main() {     int sockfd = socket(AF_INET,SOCK_DGRAM,0);      assert(sockfd != -1);      struct sockaddr_in ser,cli;     ser.sin_family = AF_INET;      ser.sin_port = htons(6000);      ser.sin_addr.s_addr = inet_addr("127.0.0.1");        sendto(sockfd,"Hello World",strlen("Hello World"),0,(struct sockaddr*)&ser,sizeof(ser));        char buff[128] = {0};      recvfrom(sockfd,buff,127,0,NULL,NULL);        printf("recv::%s\n",buff);        close(sockfd);  }//服务器void main() {      int sockfd = socket(AF_INET,SOCK_DGRAM,0);      assert(sockfd != -1);        struct sockaddr_in ser,cli;      ser.sin_family = AF_INET;      ser.sin_port = htons(6000);      ser.sin_addr.s_addr = inet_addr("127.0.0.1");        int res = bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));      assert(res != -1);        while(1)      {          char buff[128] = {0};          int len_cli = sizeof(cli);          recvfrom(sockfd,buff,127,0,(struct sockaddr*)&cli,&len_cli);          printf("IP:%s\n,D:%d\n,recv::%s\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port),buff);            sendto(sockfd,"ok",strlen("ok"),0,(struct sockaddr*)&cli,len_cli);      }        close(sockfd);   }

结果如下:



再详细介绍一下和TCP不同的两个函数:

ssize_t  recvfrom( int sockfd, //通过哪个socket进行通信
                      void *buf, //缓冲区
size_t len, //缓冲区大小
int flags,//暂时设0
struct sockaddr *src_addr, //把数据发送给谁
socklen_t *addrlen );//地址结构体长度
ssize_t sendto( int sockfd,
const void *buf, 
size_t len, 
int flags,
const struct sockaddr *dest_addr, 
socklen_t addrlen );

三、IP

介绍完传输层的两个协议,看看网络层的IP协议。IP协议为上层提供无状态、无连接、不可靠的服务。

无状态指IP通信双方不同步传输数据的状态信息,因此所有IP数据报的发送、传输和接收都是独相互立的、没有上下文关系的。最大的缺点就是无法处理乱序和重复的IP数据报。面向连接的协议,如TCP能够自己处理乱序的、重复的报文段,它交给上层的一定是有序的、正确的。无状态服务的优点也很明显:简单、高效。UDP协议和HTTP协议都是无状态协议。

其IPv4头部结构如下:


4为版本号:指定IP协议的版本,对IPv4来说是4.

4位头部长度:标识IP头部有多少个32位,这一点和TCP是一样的,最长60字节。

8位服务类型:包括一个3位的优先权字段(现已被忽略)、4位的TOS字段和一位保留字段(必须置0)。4位TOS字段分别表示:最小延时(例如直播)、最大吞吐量(例如FTP)、最高可靠性(例如银行系统)、最小费用(省流量)。这4位最多一个置1,也就是说是互斥的。

16位总长度:指整个IP数据报的长度(字节)。

16位标识:唯一地标识主机发送的每一个数据报。其初始值由系统随机生成:每发一个数据报,其值加1。TCP中的32位序号也是初始值随机生成,随后其值加1,但是TCP中的序号是排序用的,而IP中的16位标识是分片用的,同一个数据报中的所有分片都具有相同的标识符。

3位标志:第一位保留置0。第二位表示“禁止分片”。第三位表示“更多分片”,除最后一个分片外,其它都置1。

13位片偏移:分片相对原始IP数据报开始处(仅指数据部分)的偏移。实际的偏移值左移3位(乘8)得到。

8位生存时间(TTL):数据报到达目的地之前允许经过的路由器跳数。TTl被发送端设置(常见64),转发过程中每经过一个路由器其值减1。当TTL值减为0时,路由器将丢弃该数据报,并向源端发送一个ICMP差错报文。该值可防止数据报陷入路由循环。

8位协议:区分上层协议。ICMP是1,TCP是6,UDP是17。

16位头部校验和:由发送端填充,仅检验IP数据报头部。(IP数据报头部没错就可以正确转发,不检验数据,不可靠。)

32位源端IP地址和目的端IP地址:标识数据报的发送端和接收端。


再详细说一下IP分片。当IP数据报的长度超过帧的MTU(网络最大传输单元)时,它将被分片传输。分片可能发生在发送端也可能发生在中转路由上,但是只有在最终的目标机器上才会被内核的IP模块重新组装。IP数据报头部的三个字段给分片提供了足够的信息:数据报标识,标志,片偏移。下面是一个分片示意图



原创粉丝点击