TCP/IP 协议概述 OSI 参考模型 TCP/IP 协议族 TCP UDP 三次握手协议 socket 概述 sockaddr sockaddr_in 存储优先 地址格式转化 名字地址转化

                                       粉丝不过W

TCP/IP 协议概述

    OSI 参考模型及 TCP/IP 参考模型

          基于国际标准化组织(ISO), 共七层:应用层表示层会话层传输层网络层数据链路层物理层

         将 TCP/IP 的 7层协议模型 简化为 4 层,便更有利于实现和使用

     OSI 模型和 TCP/IP 参考模型对应关系:

        网络接口层:负责将二进制流转换为数据帧,并进行数据帧的发送接收

               note:  数据帧是独立的网络信息传输单元

        网络层:负责将数据帧封装成 IP 数据报,并运行必要的路由算法

        传输层:负责端对端之间的通信会话连接建立。传输协议的选择根据数据传输方式而定

        应用层:负责应用程序的网络访问,这里通过端口号来识别各个不同的进程

   TCP/IP 协议族

         ARP:用于获得同一物理网络中的硬件主机地址

         MPLS:多协议标签协议

         IP:负责在主机和网络之间寻址和路由数据包

         ICMP:用于发送报告有关数据包的传送错误的协议

         IGMP:被 IP 主机用来向本地多路广播路由器报告主机组成员的协议

         TCP:为应用程序提供可靠的通信连接。适合于一次传输大批数据的情况。也适用于要求得到响应的应用程序

         UDP:提供了无连接通信,且不对传送包进行可靠的保证。适合于一次传输少量数据可靠性则由应用层来负责

  TCP 和 UDP

    传输层 TCP 和 UDP 协议

     TCP

        概述

           TCP 向相邻的高层提供服务

           由于 TCP 的上一层是应用层,所以 数据传输实现了从一个应用程序到另一个应用程序的数据传递

           打开 socket 来使用 TCP 服务,TCP 管理到其他 socket 的数据传递

       三次握手协议

           TCP 对话通过三次握手来初始化。目的:使数据段的发送和接收同步,告诉其他主机要一次可接收的数据量,并建立虚连接

            三次握手的简单过程:

                 初始化主机,用一个同步标志置位的数据段发出会话请求

                 接收主机通过发回下列数据段表示回复:同步标志置位、即将发送的数据段的起始字节的顺序号、应答并带有将收到的下一个数据段的字节顺序号

                请求主机再回送一个数据段,并带有确认顺序号确认号

          TCP 三次握手协议流程:

           TCP 实体采用的基本协议:滑动窗口协议

          当发送方传送一个数据报时,它将启动计时器,当该数据报到达目的地后,接收方的 TCP 实体向回发送一个数据报,其中包含有一个确认序号,意思:要收到的下一个数据报的顺序号。如 发送方的定时器在确认信息到达之前超时,那发送方会重发该数据报

      TCP 数据报头

         TCP 数据报头的格式:

          源端口、目的端口:16 位长。标识 远端和本地的端口号

          序号:32 位长。标识 发送的数据报的顺序

          确认号:32 位长。希望收到的下一个数据报的序列号

          TCP 头长:4 位长。表明 TCP 头中包含多少个 32 位字

          6 位未用

          ACK:ACK : 1 表明 确认号为 合法。如 ACK 为 0,那 数据报不包含确认信息,确认字段被省略

          PSH:表示 带有 PUSH 标志的数据。接收方请求数据报 一到 便可送往应用程序,不用等到缓冲区装满时才传送

          RST:用于复位由于主机崩溃或其他原因而出现的错误的连接。还可以用于 拒绝非法的数据报或 拒绝连接请求

         SYN:用于建立连接

         FIN:用于释放连接

         窗口大小:16 位长。窗口大小字段:在确认了字节之后还可以发送多少个字节

        校验和:16 位长。是为了确保高可靠性。校验头部、数据和伪 TCP 头部之和

        可选项:0 个或多个 32 位字。包括最大 TCP 载荷,窗口比例、选择重发数据报等选项

   UDP

        概述

          UDP :用户数据报协议,一种无连接协议,因此不需要像 TCP 那样通过三次握手来建立一个连接

       UDP 数据包头

          

             源地址、目的地址:16 位长。标识出远端和本地的端口号

            数据报的长度:包括报头和数据部分在内的总的字节数,报头的长度是固定,该域:计算可变长度的数据部分(别名:数据负载)

   协议的选择

        对数据可靠性的要求

             对数据要求高可靠性的应用: TCP 协议,如验证、密码字段的传送都是不允许出错

             对数据的可靠性要求不那么高的应用: UDP 传送 

      应用的实时性

          由于 TCP 协议在传送过程中要进行三次握手重传确认等手段来保证数据传输的可靠性,所以TCP 协议会有较大的时延,因此不适合对实时性要求较高的应用,如 VOIP、视频监控。

          UDP 协议:在这些应用中能发挥很好的作用

      网络的可靠性

           TCP 协议的提出:解决网络的可靠性问题,它通过各种机制来减少错误发生的概率

          在网络状况不是很好的情况下: TCP 协议,如 广域网

          在网络状况很好的情况下: UDP 协议,减少网络负荷,如 局域网

网络基础编程

   socket 概述

         socket 定义

               Linux 网络编程通过 socket 接口来进行

              socket 接口:一种特殊的 I/O,一种文件描述符

              每一个 socket 都用一个半相关描述{ 协议,本地地址、本地端口 }来表示

             该函数返回一个整型的 socket 描述符,随后的连接建立、数据传输等操作都是通过 socket 来实现

             一个完整的套接字用一个相关描述{ 协议,本地地址、本地端口、远程地址、远程端口 }

       socket 类型

            流式 socket(SOCK_STREAM)

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

          数据报 socket(SOCK_DGRAM)

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

         原始 socket

              原始套接字允许对底层协议如 IP 或 ICMP 进行直接访问,它功能强大但使用不便,主要用于一些协议的开发

   地址及顺序处理

        数据结构介绍

/* 等效,可相互转化 */
struct sockaddr
{
    unsigned short sa_family; /* 地址族 */
    char sa_data[14]; /* 14 字节的协议地址,含该 socket 的 IP 地址、端口号 */
};

struct sockaddr_in
{
    short int sa_family; /* 地址族 */
    unsigned short int sin_port; /* 端口号 */
    struct in_addr sin_addr; /* IP 地址 */
    unsigned char sin_zero[8]; /*填充 0 以保持与 struct sockaddr 同样大小*/
};

       结构字段

#include <netinet/in.h>

/*
 *AF_INET: IPv4 协议
 *AF_INET6:IPv6 协议
 *AF_LOCAL:UNIX 域协议
 *AF_LINK: 链路地址协议
 *AF_KEY:  密钥套接字(socket)
 */
unsigned short sa_family;

    数据存储优先顺序

          函数说明

            计算机数据存储有两种字节优先顺序:高位字节优先低位字节优先

           Internet 上数据以高位字节优先顺序在网络上传输

          两个字节存储优先顺序进行相互转化时,需用到:htons、ntohs、htonl、ntohl

           h 代表 host,n 代表 network,s 代表 short,l 代表 long

           通常 16 位的 IP 端口号用 s 代表,而 IP 地址用 l 来代表

#include <netinet/in.h>

/*
 *parameter:
 *  host16bit:主机字节序的 16bit 数据
 *  host32bit:主机字节序的 32bit 数据
 *  net16bit: 网络字节序的 16bit 数据
 *  net32bit: 网络字节序的 32bit 数据
 *return:
 *  成功:转换的字节序
 *  出错:-1
 */
uint16_t htons(unit16_t host16bit)
uint32_t htonl(unit32_t host32bit)
uint16_t ntohs(unit16_t net16bit)
uint32_t ntohs(unit32_t net32bit)

    地址格式转化

         函数说明

            用户在表达地址时采用的是点分十进制表示的数值(或 以冒号分开的十进制IPv6 地址),而 使用的 socket 编程中所使用的是二进制值

#include <arpa/inet.h>

/*
 *function:
 *  点分十进制地址映射为二进制地址
 *parameter:
 *  family:
 *    AF_INET: IPv4 协议
 *    AF_INET6:IPv6 协议
 *  strptr:  转化的值
 *  addrptr: 转化后的地址
 *return:
 *  成功:0
 *  失败:-1
 */
int inet_pton(int family, const char *strptr, void *addrptr)

/*
 *function:
 *  二进制地址映射为点分十进制地址
 *parameter:
 *  family:
 *    AF_INET: IPv4 协议
 *    AF_INET6:IPv6 协议
 *  addrptr:转化后的地址
 *  strptr: 要转化的值
 *  Len:    转化后值的大小
 *return:
 *  成功:0
 *  失败:-1
 */
int inet_ntop(int family, void *addrptr, char *strptr, size_t len)

     名字地址转化

          函数说明

             实现 IPv4 和 IPv6 的地址和主机名之间的转化

struct hostent
{
    char *h_name;        /* 正式主机名 */
    char **h_aliases;    /* 主机别名 */
    int h_addrtype;      /* 地址类型 */
    int h_length;        /* 地址长度 */
    char **h_addr_list;  /* 指向 IPv4 或 IPv6 的地址指针数组 */
};
#include <netdb.h>

/*
 *ai_flags:
 *  AI_PASSIVE:  该套接口是用作被动地打开
 *  AI_CANONNAME:通知 getaddrinfo 函数返回主机的名字
 *family:
 *  AF_INET: IPv4 协议
 *  AF_INET6:IPv6 协议
 *  AF_UNSPE:IPv4 或 IPv6 均可
 *ai_socktype:
 *  SOCK_STREAM:字节流套接字 socket(TCP)
 *  SOCK_DGRAM: 数据报套接字 socket(UDP)
 *ai_protocol:
 *  IPPROTO_IP:  IP 协议
 *  IPPROTO_IPV4:IPv4 协议
 *  IPPROTO_IPV6:IPv6 协议
 *  IPPROTO_UDP: UDP
 *  IPPROTO_TCP: TCP
 *note:
 *  服务器端, ai_flags: AI_PASSIVE,主机名 nodename: NULL
 *  客户端,,ai_flags != AI_PASSIVE,且 主机名 nodename 和服务名 servname(端口)!= 空
 */
struct addrinfo
{
    int ai_flags;             /* AI_PASSIVE,AI_CANONNAME */
    int ai_family;            /* 地址族 */
    int ai_socktype;          /* socket 类型 */
    int ai_protocol;          /* 协议类型 */
    size_t ai_addrlen;        /* 地址长度 */
    char *ai_canoname;        /* 主机名 */
    struct sockaddr *ai_addr; /* socket 结构体 */
    struct addrinfo *ai_next; /* 下一个指针链表 */
};

      函数格式 

#include <netdb.h>

/*
 *function:
 *  主机名转化为 IP 地址
 *parameter:
 *  hostname:主机名
 *return:
 *  成功:hostent 类型指针
 *  失败:-1
 */
struct hostent *gethostbyname(const char *hostname)
#include <netdb.h>

/*
 *function:
 *  自动识别 IPv4 地址和 IPv6 地址
 *parameter:
 *  hostname:主机名
 *  service: 服务名或十进制的串口号字符串
 *  hints:   服务线索函数传入值
 *  result:  返回结果
 *return:
 *  成功:0
 *  出错:-1
 */
int getaddrinfo(const char *hostname,
                const char *service,
                const struct addrinfo *hints,
                struct addrinfo **result)

例:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

int main()
{
    struct addrinfo hints, *res = NULL;
    int rc;

    memset(&hints, 0, sizeof(hints));

    /*设置 addrinfo 结构体中各参数*/
    hints.ai_family = PF_UNSPEC;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_protocol = IPPROTO_UDP;

    /*调用 getaddinfo 函数*/
    rc = getaddrinfo("127.0.0.1", "123", &hints, &res);
    if(rc != 0)
    {
        perror("getaddrinfo");
        exit(1);
    }
    else
    {
        printf("getaddrinfo success\n");
    }
}

 

生成结果

生成结果

猜你喜欢

转载自blog.csdn.net/qq_44226094/article/details/105741503
今日推荐