您的位置:首页 > 理论基础 > 计算机网络

Linux网络编程入门-5

2016-07-14 21:55 363 查看
Linux网络编程--7. TCP/IP协议

你也许听说过TCP/IP协议,那么你知道到底什么是TCP,什么是IP吗?在这一章里面,我们一起来学习这个目前网络上用最广泛的协议.

7.1 网络传输分层 

    如果你考过计算机等级考试,那么你就应该已经知道了网络传输分层这个概念.在网络上,人们为了传输数据时的方便,

    把网络的传输分为7个层次.分别是:应用层,表示层,会话层,传输层,网络层,数据链路层和物理层.分好了层以后,传输数据时,

    上一层如果要数据的话,就可以直接向下一层要了,而不必要管数据传输的细节.下一层也只向它的上一层提供数据,

    而不要去管其它东西了.如果你不想考试,你没有必要去记这些东西的.只要知道是分层的,而且各层的作用不同.

7.2 IP协议 

    IP协议是在网络层的协议.它主要完成数据包的发送作用. 下面这个表是IP4的数据包格式

0      4       8       16                      32

--------------------------------------------------

|版本   |首部长度|服务类型|    数据包总长       |

--------------------------------------------------

|    标识                 |DF |MF| 碎片偏移      |

--------------------------------------------------

|   生存时间    |  协议   |  首部较验和         |

------------------------------------------------

|               源IP地址                        |

------------------------------------------------

|               目的IP地址                      |

-------------------------------------------------

|               选项                            |

=================================================

|               数据                            |

-------------------------------------------------                      

下面我们看一看IP的结构定义

struct ip

  {

#if __BYTE_ORDER == __LITTLE_ENDIAN

        unsigned int ip_hl:4;           /* header length */

        unsigned int ip_v:4;            /* version */

#endif

#if __BYTE_ORDER == __BIG_ENDIAN

        unsigned int ip_v:4;            /* version */

        unsigned int ip_hl:4;           /* header length */

#endif

        u_int8_t ip_tos;                /* type of service */

        u_short ip_len;                 /* total length */

        u_short ip_id;                  /* identification */

        u_short ip_off;                 /* fragment offset field */

#define IP_RF 0x8000                    /* reserved fragment flag */

#define IP_DF 0x4000                    /* dont fragment flag */

#define IP_MF 0x2000                    /* more fragments flag */

#define IP_OFFMASK 0x1fff               /* mask for fragmenting bits */

        u_int8_t ip_ttl;                /* time to live */

        u_int8_t ip_p;                  /* protocol */

        u_short ip_sum;                 /* checksum */

        struct in_addr ip_src, ip_dst;  /* source and dest address */

  };

ip_vIP协议的版本号,这里是4,现在IPV6已经出来了

ip_hlIP包首部长度,这个值以4字节为单位.IP协议首部的固定长度为20个字节,如果IP包没有选项,那么这个值为5.

ip_tos服务类型,说明提供的优先权.

ip_len说明IP数据的长度.以字节为单位.

ip_id标识这个IP数据包.

ip_off碎片偏移,这和上面ID一起用来重组碎片的.

ip_ttl生存时间.没经过一个路由的时候减一,直到为0时被抛弃.

ip_p协议,表示创建这个IP数据包的高层协议.如TCP,UDP协议.

ip_sum首部校验和,提供对首部数据的校验.

ip_src,ip_dst发送者和接收者的IP地址

关于IP协议的详细情况,请参考 RFC791

7.3 ICMP协议 

ICMP是消息控制协议,也处于网络层.在网络上传递IP数据包时,如果发生了错误,那么就会用ICMP协议来报告错误.

ICMP包的结构如下:

0              8               16                              32

---------------------------------------------------------------------

|       类型    |       代码    |       校验和                  |

--------------------------------------------------------------------

|               数据            |       数据                    |

--------------------------------------------------------------------

ICMP在中的定义是 

struct icmphdr

{

  u_int8_t type;                /* message type */

  u_int8_t code;                /* type sub-code */

  u_int16_t checksum;

  union

  {

    struct

    {

      u_int16_t id;

      u_int16_t sequence;

    } echo;                     /* echo datagram */

    u_int32_t   gateway;        /* gateway address */

    struct

    {

      u_int16_t __unused;

      u_int16_t mtu;

    } frag;                     /* path mtu discovery */

  } un;

};

关于ICMP协议的详细情况可以查看 RFC792

7.4 UDP协议 

UDP协议是建立在IP协议基础之上的,用在传输层的协议.UDP和IP协议一样是不可靠的数据报服务.UDP的头格式为:

0                      16                      32

---------------------------------------------------

|       UDP源端口       |       UDP目的端口     |

---------------------------------------------------

|       UDP数据报长度   |       UDP数据报校验   |

---------------------------------------------------

UDP结构在中的定义为: 

struct udphdr {

  u_int16_t     source;

  u_int16_t     dest;

  u_int16_t     len;

  u_int16_t     check;

};

关于UDP协议的详细情况,请参考 RFC768

7.5 TCP 

TCP协议也是建立在IP协议之上的,不过TCP协议是可靠的.按照顺序发送的.TCP的数据结构比前面的结构都要复杂.

0       4       8  10           16              24              32

-------------------------------------------------------------------

|               源端口          |               目的端口        |

-------------------------------------------------------------------

|                               序列号                          |

------------------------------------------------------------------

|                               确认号                          |

------------------------------------------------------------------

|        |            |U|A|P|S|F|                               |

|首部长度| 保留       |R|C|S|Y|I|       窗口                    |

|        |            |G|K|H|N|N|                               |

-----------------------------------------------------------------

|               校验和          |               紧急指针        |

-----------------------------------------------------------------

|                       选项                    |    填充字节   |

-----------------------------------------------------------------

TCP的结构在中定义为: 

struct tcphdr

  {

    u_int16_t source;

    u_int16_t dest;

    u_int32_t seq;

    u_int32_t ack_seq;

#if __BYTE_ORDER == __LITTLE_ENDIAN

    u_int16_t res1:4;

    u_int16_t doff:4;

    u_int16_t fin:1;

    u_int16_t syn:1;

    u_int16_t rst:1;

    u_int16_t psh:1;

    u_int16_t ack:1;

    u_int16_t urg:1;

    u_int16_t res2:2;

#elif __BYTE_ORDER == __BIG_ENDIAN

    u_int16_t doff:4;

    u_int16_t res1:4;

    u_int16_t res2:2;

    u_int16_t urg:1;

    u_int16_t ack:1;

    u_int16_t psh:1;

    u_int16_t rst:1;

    u_int16_t syn:1;

    u_int16_t fin:1;

#endif

    u_int16_t window;

    u_int16_t check;

    u_int16_t urg_prt;

};     

source发送TCP数据的源端口 

dest接受TCP数据的目的端口

seq标识该TCP所包含的数据字节的开始序列号

ack_seq确认序列号,表示接受方下一次接受的数据序列号.

doff数据首部长度.和IP协议一样,以4字节为单位.一般的时候为5

urg如果设置紧急数据指针,则该位为1

ack如果确认号正确,那么为1

psh如果设置为1,那么接收方收到数据后,立即交给上一层程序

rst为1的时候,表示请求重新连接

syn为1的时候,表示请求建立连接

fin为1的时候,表示亲戚关闭连接

window窗口,告诉接收者可以接收的大小

check对TCP数据进行较核

urg_ptr如果urg=1,那么指出紧急数据对于历史数据开始的序列号的偏移值

关于TCP协议的详细情况,请查看 RFC793

7.6 TCP连接的建立 

TCP协议是一种可靠的连接,为了保证连接的可靠性,TCP的连接要分为几个步骤.我们把这个连接过程称为"三次握手".

下面我们从一个实例来分析建立连接的过程.

第一步客户机向服务器发送一个TCP数据包,表示请求建立连接. 为此,客户端将数据包的SYN位设置为1,

并且设置序列号seq=1000(我们假设为1000).

第二步服务器收到了数据包,并从SYN位为1知道这是一个建立请求的连接.于是服务器也向客户端发送一个TCP数据包.

因为是响应客户机的请求, 于是服务器设置ACK为1,sak_seq=1001(1000+1)同时设置自己的序列号.seq=2000(我们假设为2000).

第三步客户机收到了服务器的TCP,并从ACK为1和ack_seq=1001知道是从服务器来的确认信息.于是客户机也向服务器发送确认信息.

客户机设置ACK=1,和ack_seq=2001,seq=1001,发送给服务器.至此客户端完成连接.

最后一步服务器受到确认信息,也完成连接.

通过上面几个步骤,一个TCP连接就建立了.当然在建立过程中可能出现错误,不过TCP协议可以保证自己去处理错误的.

说一说其中的一种错误.

  听说过DOS吗?(可不是操作系统啊).今年春节的时候,美国的五大网站一起受到攻击.攻击者用的就是DOS(拒绝式服务)方式.

  概括的说一下原理.客户机先进行第一个步骤.服务器收到后,进行第二个步骤.按照正常的TCP连接,客户机应该进行第三个步骤.

  不过攻击者实际上并不进行第三个步骤.因为客户端在进行第一个步骤的时候,修改了自己的IP地址,就是说将一个实际上不存在的

  IP填充在自己IP 数据包的发送者的IP一栏.这样因为服务器发的IP地址没有人接收,所以服务端会收不到第三个步骤的确认信号,

  这样服务务端会在那边一直等待,直到超时.这样当有大量的客户发出请求后,服务端会有大量等待,直到所有的资源被用光,

  而不能再接收客户机的请求.这样当正常的用户向服务器发出请求时,由于没有了资源而不能成功.

  于是就出现了春节时所出现的情况.

Linux网络编程--8. 套接字选项

有时候我们要控制套接字的行为(如修改缓冲区的大小),这个时候我们就要控制套接字的选项了.

8.1 getsockopt和setsockopt

int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen)

int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t *optlen)

level指定控制套接字的层次.可以取三种值: 

        1)SOL_SOCKET:通用套接字选项. 

        2)IPPROTO_IP:IP选项. 

        3)IPPROTO_TCP:TCP选项. 

optname指定控制的方式(选项的名称),我们下面详细解释

optval获得或者是设置套接字选项.根据选项名称的数据类型进行转换

选项名称                说明                                    数据类型

========================================================================

                        SOL_SOCKET

------------------------------------------------------------------------

SO_BROADCAST            允许发送广播数据                        int

SO_DEBUG                允许调试                                int

SO_DONTROUTE            不查找路由                              int

SO_ERROR                获得套接字错误                          int

SO_KEEPALIVE            保持连接                                int

SO_LINGER               延迟关闭连接                            struct linger

SO_OOBINLINE            带外数据放入正常数据流                  int

SO_RCVBUF               接收缓冲区大小                          int

SO_SNDBUF               发送缓冲区大小                          int

SO_RCVLOWAT             接收缓冲区下限                          int

SO_SNDLOWAT             发送缓冲区下限                          int

SO_RCVTIMEO             接收超时                                struct timeval

SO_SNDTIMEO             发送超时                                struct timeval

SO_REUSERADDR           允许重用本地地址和端口                  int

SO_TYPE                 获得套接字类型                          int

SO_BSDCOMPAT            与BSD系统兼容                           int

==========================================================================

                        IPPROTO_IP

--------------------------------------------------------------------------

IP_HDRINCL              在数据包中包含IP首部                    int

IP_OPTINOS              IP首部选项                              int

IP_TOS                  服务类型

IP_TTL                  生存时间                                int

==========================================================================

                        IPPRO_TCP

--------------------------------------------------------------------------

TCP_MAXSEG              TCP最大数据段的大小                     int

TCP_NODELAY             不使用Nagle算法                         int

=========================================================================

关于这些选项的详细情况请查看 Linux Programmer's Manual

8.2 ioctl 

ioctl可以控制所有的文件描述符的情况,这里介绍一下控制套接字的选项.

int ioctl(int fd,int req,...)

==========================================================================

                        ioctl的控制选项

--------------------------------------------------------------------------

SIOCATMARK              是否到达带外标记                        int

FIOASYNC                异步输入/输出标志                       int

FIONREAD                缓冲区可读的字节数                      int

==========================================================================

详细的选项请用 man ioctl_list 查看.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  函数 Linux 网络编程