zoukankan      html  css  js  c++  java
  • Linux C Socket 编程

    1 Socket 是什么

    Socket(套接字),就是对 网络上进程通信端点抽象。一个 Socket 就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制

    从所处的位置来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信交互的接口。如下图所示:

    2 Socket 类型

    2.1 标准套接字

    标准套接字是在传输层使用的套接字,分为流式套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM)。

    标准套接字在接收和发送时只能操作数据部分(TCP Payload / UDP Payload),而不能对 IP 首部或TCP 首部和 UDP 首部进行操作。

    2.1.1 流套接字(SOCK_STREAM)

    流套接字(SOCK_STREAM)用于提供 面向连接(可靠)的数据传输服务。

    流套接字保证数据能够实现无差错、无重复发数据,并按顺序接收。

    流套接字(SOCK_STREAM)使用 TCP(The Transmission Control Protocol)协议 进行数据的传输

    2.1.2 数据报套接字(SOCK_DGRAM)

    数据报套接字(SOCK_DGRAM)用于提供 无连接(不可靠)的数据传输服务。

    数据报套接字不保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。

    数据报套接字(SOCK_DGRAM)使用 UDP(User DatagramProtocol)协议 进行数据的传输

    2.2 原始套接字(SOCK_RAW)

    原始套接字(SOCK_RAW)可以做到标准套接字做到的事,更可以做到标准套接字做不到的事。

    原始套接字是在传输层及传输层以下使用的套接字。

    原始套接字在接收和发送时不仅能操作数据部分(TCP Payload / UDP Payload),也能对 IP 首部或TCP 首部和 UDP 首部进行操作。

    因此如果我们开发的是更底层的应用,比如发送一个自定义的 IP 包、UDP 包、TCP 包或 ICMP 包,捕获所有经过本机网卡的数据包(sniffer),伪装本机的 IP ,拒绝服务攻击(DOS)等,都可以通过原始套接字(SOCK_RAW)实现。

    注意:必须在管理员权限下才能使用原始套接字。

    3 Socket() 函数 介绍

    3.1 功能

    分配文件描述符,创建 socket,即创建网络上进程通信的端点。

    3.2 头文件

    #include <sys/types.h>
    #include <sys/socket.h>
    

    3.3 函数原型

    int socket(int domain, int type, int protocol)

    3.4 参数

    注意:type 和 protocol 不可以随意组合,如 SOCK_STREAM 不可以跟 IPPROTO_UDP 组合。

    具体的组合和应用场景可以参考 4 创建 Socket 及其应用场景

    3.4.1 domain

    domain:即协议域,又称为协议族(family),如下所示:

    • AF_INET / PF_INET(2):IPv4,获取 网络层的数据

    • AF_INET6:IPv6

    • AF_UNIX:UNIX 系统本地通信

    • AF_PACKET / PF_PACKET(17):以太网包,获取 数据链路层的数据

    注:

    1. AF = Address Family(地址族),PF = Protocol Family(协议族)

    2. 理论上建立 socket 时是指定协议,应该用 PF_xxxx,设置地址时应该用 AF_xxxx。当然 AF_xxxx和 PF_xxxx 的值是相同的,混用也不会有太大的问题。

    3.4.2 type

    type:指定 socket 类型,如下所示:

    • SOCK_STREAM(1):面向连接的流式套接字(TCP)

    • SOCK_DGRAM(2):面向无连接的数据包套接字(UDP)

    • SOCK_RAW(3):接收 底层数据报文 的原始套接字

    • SOCK_PACKET(10):过时类型,可以使用,但是已经废弃,以后不保证还能支持,不推荐使用。

    3.4.3 protocol

    protocol:指定协议,如下所示:

    • 0:自动选择 type 类型对应的默认协议。

    • IPPROTO_IP(0):接受 TCP 类型的数据帧

    • IPPROTO_ICMP(1):接受 ICMP 类型的数据帧

    • IPPROTO_IGMP(2)接受 IGMP 类型的数据帧

    • IPPROTO_TCP(6):接受 TCP 类型的数据帧

    • IPPROTO_UDP(17):接受 UDP 类型的数据帧

    • ETH_P_IP(0x800):接收发往本机 MAC 的 IP 类型的数据帧

    • ETH_P_ARP(0x806):接受发往本机 MAC 的 ARP 类型的数据帧

    • ETH_P_RARP(0x8035):接受发往本机 MAC 的 RARP 类型的数据帧

    • ETH_P_ALL(0x3):接收发往本机 MAC 的所有类型 IP ARP RARP 的数据帧,接收从本机发出的所有类型的数据帧。(混杂模式打开的情况下,会接收到非发往本地 MAC 的数据帧)

    3.5 返回值

    • 成功:返回一个文件描述符

    • 失败:返回 -1,并设置 errno

    3.6 备注

    详情查看 man 手册:man 2 socket

    4 创建 Socket 及其应用场景

    5 bind() 函数

    5.1 功能

    将 IP 地址信息绑定到 socket。

    5.2 头文件

    #include <sys/types.h>          
    #include <sys/socket.h>
    

    5.3 函数原型

    int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

    5.4 参数

    5.4.1 sockfd

    通信 socket

    5.4.2 addr

    要绑定的地址信息(包括IP地址,端口号)。

    通用地址结构体定义:

    struct sockaddr 
    {
        sa_family_t sa_family;   // 地址族, AF_xxx
        char        sa_data[14]; // 包括 IP 和端口号
    }
    

    新型的地址结构体定义:(查看新型的结构体信息: gedit /usr/include/linux/in.h )

    struct sockaddr_in 
    {
      __kernel_sa_family_t    sin_family;    // 地址族,IP 协议。默认:AF_INET
      __be16                  sin_port;      // 端口号
      struct in_addr          sin_addr;      // 网络 IP 地址
    
      unsigned char           __pad          // 8 位的预留接口
    };
    

    5.4.3 addrlen

    地址信息大小

    5.5 返回值

    • 成功:返回 0

    • 失败:返回 -1,并设置 errno

    5.6 备注

    详细查看 man 手册:man 2 bind

    6 listen() 函数

    6.1 功能

    监听指定端口,socket() 创建的 socket 是主动的,调用 listen 使得该 socket 成为 监听 socket ,变主动为被动。

    6.2 头文件

    #include <sys/socket.h>

    6.3 函数原型

    int listen(int sockfd, int backlog);

    6.4 参数

    6.4.1 sockfd

    通信 socket

    6.4.2 backlog

    同时能处理的最大连接要求

    6.5 返回值

    • 成功:返回 0

    • 失败:返回 -1,并设置 errno

    6.6 备注

    详细查看 man 手册:man 2 listen

    7 accept() 函数

    7.1 功能

    提取出 监听 socket 的等待连接队列中 第一个连接请求,创建 一个新的 socket,即 连接 socket

    新建立的 连接 socket 用于发送数据和接受数据。

    7.2 头文件

    #include <sys/socket.h>

    7.3 函数原型

    #include <sys/types.h>          
    #include <sys/socket.h>
    

    7.4 参数

    7.4.1 sockfd

    监听 socket,即 在 调用 listen() 后的 监听 socket。

    7.4.2 addr

    (可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。

    7.4.3 addrlen

    (可选)指针,输入参数,配合addr一起使用,指向存有addr地址长度的整型数。

    7.5 返回值

    • 成功:指向 新的 socket(连接 socket)的文件描述符。

    • 失败:返回 -1,并设置 errno

    7.6 备注

    详细查看 man 手册:man 2 listen

    8 connect() 函数

    8.1 功能

    发送连接请求

    8.2 头文件

    #include <sys/types.h>          
    #include <sys/socket.h>
    

    8.3 函数原型

    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

    8.4 参数

    8.4.1 sockfd

    通信 socket

    8.4.2 addr

    要连接的服务器地址

    8.4.3 addrlen

    地址信息大小

    8.5 返回值

    • 成功:返回 0

    • 失败:返回 -1,并设置 errno

    8.6 备注

    详细查看 man 手册:man 2 connect

    9 sendto() 函数

    9.1 功能

    将数据由指定的 socket 传给对方主机

    9.2 头文件

    #include <sys/types.h>          
    #include <sys/socket.h>
    

    9.3 函数原型

    int sendto (int sockfd , const void * msg, int len, unsigned int flags, const
    struct sockaddr * to , int tolen);
    

    9.4 参数

    9.4.1 sockfd

    已建立连接的 socket,如果利用 UDP 协议则不需建立连接。

    9.4.2 msg

    发送数据的缓冲区。

    9.4.3 len

    缓冲区长度。

    9.4.4 flags

    调用方式标志位,一般设为 0 。

    9.4.5 to

    用来指定要传送的网络地址,结构 sockaddr

    9.4.6 tolen

    sockaddr 的长度

    9.5 返回值

    • 成功:返回实际传送出去的字符数

    • 失败:返回 -1,并设置 errno

    9.6 备注

    详细查看 man 手册:man 2 sendto

    10 recvfrom() 函数

    10.1 功能

    接收远程主机经指定的 socket 传来的数据,并把数据传到由参数 buf 指向的内存空间。

    10.2 头文件

    #include <sys/types.h>          
    #include <sys/socket.h>
    

    10.3 函数原型

    int recvfrom(int sockfd,void *buf,int len,unsigned int flags, struct sockaddr *from,int *fromlen);

    10.4 参数

    10.4.1 sockfd

    已建立连接的 socket,如果利用 UDP 协议则不需建立连接。

    10.4.2 buf

    接收数据缓冲区。

    10.4.3 len

    缓冲区长度。

    10.4.4 flags

    调用方式标志位,一般设为 0 。

    10.4.5 from

    (可选)指针,指向装有源地址的缓冲区,结构 sockaddr

    10.4.6 fromlen

    (可选)指针,指向 from 缓冲区长度值,sockaddr 的结构长度

    10.5 返回值

    • 成功:返回实际接受到的字符数

    • 失败:返回 -1,并设置 errno

    10.6 备注

    详细查看 man 手册:man 2 recvfrom

    11 字节序

    字节序,是 大于一个字节类型的数据在内存中的存放顺序,由 CPU 架构决定,与操作系统无关。是在跨平台和网络编程中,时常要考虑的问题。

    11.1 高低地址

    在内存中,栈是向下生长的,以char arr[4]为例,(因为 char 类型数据只有一个字节,不存在字节序的问题)依次输出每个元素的地址,可以发现,arr[0] 的地址最低,arr[3] 的地址最高,如图:

    11.2 高低字节

    在十进制中靠左边的是高位,靠右边的是低位,在其他进制也是如此。

    例如: 0x12345678,从高位到低位的字节依次是 0x12、0x34、0x56 和 0x78。

    11.3 字节序分类 - 大小端模式

    字节序被分为两类:

    1. 大端模式(Big-endian):内存的 低地址 存放 数据的高字节,内存的 高地址 存放 数据的低字节。(与人类阅读顺序一致)

    2.** 小端模式**(Little-endian),是指内存的 低地址 存放 数据的低字节,内存的 高地址 存放 数据的高字节

    大端模式 CPU 代表是 IBM Power PC,小端模式 CPU 代表是 Intel X86、ARM。

    11.4 大小端示例

    以 0x12345678 为例,两种模式在内存中的存储情况,如下表所示:

    11.5 判断大小端

    利用 C 语言 union 联合体所有成员共用同一块内存的特性,可以用联合体快速实现判断大小端。

    #include <stdio.h>
    union u
    {
        char c[4];
        int i;
    };
    int main(void)
    {
        union u test;
        int j;
    
        test.i = 0x12345678;
    
        for(j = 0; j < sizeof(test.c); j++)
        {
            printf("0x%x
    ",test.c[j]);
        }
    
        return 0;
    }
    

    运行后结果:

    可以看出,我的机器是小端字节序。

    11.6 网络字节序与本机字节序

    网络字节序(NBO,Network Byte Order),是 TCP/IP 中规定好的一种数据表示格式。它与具体的 CPU 类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。

    网络字节序采用大端(Big-endian)字节序排序方式。

    主机字节顺序(HBO,Host Network Order),与机器 CPU 相关,数据的存储顺序由 CPU 决定。

    11.6.1 转换函数

    socket 编程中经常会用到 4 个网络字节顺序与本地字节顺序之间的转换函数:htons()、ntohl()、 ntohs()、htons()。

    htonl()--"Host to Network Long"        // 长整型数据主机字节顺序转网络字节顺序
    ntohl()--"Network to Host Long"        // 长整型数据网络字节顺序转主机字节顺序
    htons()--"Host to Network Short"       // 短整型数据主机字节顺序转网络字节顺序
    ntohs()--"Network to Host Short"       // 短整型数据网络字节顺序转主机字节顺序
    

    在使用小端字节序的系统中,这些函数会把字节序进行转换。

    在使用大端字节序的系统中,这些函数会定义成空宏。

    12 代码示例

    12.1 标准套接字(SOCK_STREAM - TCP)

    12.1.1 TCP Socket 通信过程

    12.1.1.1 服务器

    1. 建立连接阶段

    • 调用 socket(),分配文件描述符,创建 服务器 socket

    • 调用 bind(),将 socket 与本地 IP 地址和端口绑定

    • 调用 listen(),监听指定端口,socket() 创建的 socket 是主动的,调用 listen 使得该 socket 成为监听 socket ,变主动为被动

    • 调用 accept(),获得 连接 socket,阻塞等待客户端发起连接

    2. 数据交互阶段

    • 调用 read(),阻塞等待客户端发送的数据请求,收到请求后从 read() 返回,处理客户端请求

    • 调用 write(),将数据发送给客户端

    3. 关闭连接

    • 当 read() 返回 0 的时候,说明客户端发来了 FIN 数据包,即关闭连接,调用 close() 关闭 连接 socket 和 监听 socket

    12.1.1.2 客户端

    1. 建立连接阶段

    • 调用 socket(),分配文件描述符,创建 客户端 socket

    • 调用 connect(),向服务器发送建立连接请求

    2. 数据交互阶段

    • 调用 write(),向服务器发送数据

    • 调用 read(),阻塞等待服务器应答

    3. 关闭连接

    • 当没有数据发送的时候,调用 close() 关闭 客户端 socket ,即关闭连接,向服务器发送 FIN 数据报

    12.1.2 单个客户端单个服务器的 TCP 通信

    Linux-C TCP简单例子 - nanfeibuyi - https://blog.csdn.net/nanfeibuyi/article/details/88540144 - 例子1

    12.1.3 多线程实现 - 单个客户端单个服务器的 TCP 通信

    Linux-C TCP简单例子 - nanfeibuyi - https://blog.csdn.net/nanfeibuyi/article/details/88540144 - 例子2

    12.1.4 多路复用实现 - 单个客户端单个服务器的 TCP 通信

    Linux-C TCP简单例子 - nanfeibuyi - https://blog.csdn.net/nanfeibuyi/article/details/88540144 - 例子3

    12.1.5 多个客户端单个服务器的 TCP 通信

    Linux-C TCP简单例子 - nanfeibuyi - https://blog.csdn.net/nanfeibuyi/article/details/88540144 - 例子4

    12.1.6 多线程实现 - 多个客户端单个服务器的 TCP 通信

    Linux-C TCP简单例子 - nanfeibuyi - https://blog.csdn.net/nanfeibuyi/article/details/88540144 - 例子5

    12.1.7 多路复用实现 - 多个客户端单个服务器的 TCP 通信

    Linux-C TCP简单例子 - nanfeibuyi - https://blog.csdn.net/nanfeibuyi/article/details/88540144 - 例子6

    12.2 标准套接字(SOCK_DGRAM- UDP)

    12.2.1 UDP Socket 通信过程

    12.2.1.1 服务器

    1. 建立连接阶段

    • 调用 socket(),分配文件描述符,创建 服务器 socket

    • 调用 bind(),将 socket 与本地 IP 地址和端口绑定

    2. 数据交互阶段

    • 调用 recvfrom(),阻塞,接受客户端的数据

    • 调用 sendto(),将数据发送给客户端

    3. 关闭连接

    • 调用 close() 关闭 服务器 socket

    12.2.1.2 客户端

    1. 建立连接阶段

    • 调用 socket(),分配文件描述符,创建 客户端 socket

    2. 数据交互阶段

    • 调用 sendto(),向服务器发送数据

    • 调用 recvfrom(),阻塞,接受服务器的数据

    3. 关闭连接

    • 调用 close() 关闭 客户端 socket ,即关闭连接。

    12.2.2 单个客户端单个服务器的 UDP 通信

    代码来源:Linux-C UDP简单例子 - nanfeibuyi - https://blog.csdn.net/nanfeibuyi/article/details/88540233 - 例子1

    12.2.3 多线程实现 - 单个客户端单个服务器的 UDP 通信

    代码来源:Linux-C UDP简单例子 - nanfeibuyi - https://blog.csdn.net/nanfeibuyi/article/details/88540233 - 例子2

    12.2.4 多路复用实现 - 单个客户端单个服务器的 UDP 通信

    代码来源:Linux-C UDP简单例子 - nanfeibuyi - https://blog.csdn.net/nanfeibuyi/article/details/88540233 - 例子3

    12.2.4 UDP 通信组播

    代码来源:Linux-C UDP简单例子 - nanfeibuyi - https://blog.csdn.net/nanfeibuyi/article/details/88540233 - 例子4

    12.2.4 UDP 通信广播

    代码来源:Linux-C UDP简单例子 - nanfeibuyi - https://blog.csdn.net/nanfeibuyi/article/details/88540233 - 例子5

    12.3 原始套接字

    12.3.1 抓取以太网上的所有数据帧

    代码来源:GitHub - zhouyingjiu - https://github.com/zouyingjiu/sniffer

    
    /* 
     *        sniffer.c 
     * 
     *        功能: 
     *                linux rawSocket 抓取以太网上的所有数据帧 
     * 
     *        参数: 
     *                无 
     * 
     *  注意: 
     *      执行该程序需要 root 权限 sudo ./  
     */ 
     
    #include <stdio.h> 
    #include <stdlib.h> 
    #include <string.h> 
     
    #ifdef __linux__ 
            #include <unistd.h> 
            #include <errno.h> 
            #include <sys/socket.h> 
            #include <sys/types.h> 
            #include <netinet/in.h> 
            #include <netinet/ip.h> 
            #include <netinet/tcp.h> 
            #include <netinet/udp.h> 
            #include <netinet/ip_icmp.h> 
            #include <net/if_arp.h> 
            #include <netinet/if_ether.h> 
            #include <net/if.h> 
            #include <sys/ioctl.h> 
    #elif __win32__ 
            #include <windows.h> 
     
    #endif 
     
    void UnpackARP(char *buff); 
    void UnpackIP(char *buff); 
    void UnpackTCP(char *buff); 
    void UnpackUDP(char *buff); 
    void UnpackICMP(char *buff); 
    void UnpackIGMP(char *buff); 
     
    int main(int argc, char **argv) 
    { 
            int sockfd, i; 
            char buff[2048]; 
             
          /* 
           *   监听以太网上的所有数据帧 
           */ 
            if(0 > (sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))))         
            { 
                    perror("socket error!"); 
             
                    exit(-1); 
            } 
     
            while(1) 
            { 
                    memset(buff, 0, 2048); 
                     
                    int n = recvfrom(sockfd, buff, 2048, 0, NULL, NULL); 
     
                    printf("%s
    ",buff); 
                     
                    printf("开始解析数据包============
    "); 
                     
                    printf("大小: %d
    ", n); 
                     
                    struct ethhdr *eth = (struct ethhdr*)buff; 
                     
                    char *nextStack = buff + sizeof(struct ethhdr); 
                     
                    int protocol = ntohs(eth->h_proto); 
                    switch(protocol)  
                    { 
                            case ETH_P_IP: 
                                    UnpackIP(nextStack); 
                                    break; 
                             
                            case ETH_P_ARP: 
                                    UnpackARP(nextStack); 
                                    break; 
                    } 
                     
                    printf("解析结束=================
    
    "); 
            } 
     
            return 0; 
    } 
     
    void getAddress(long saddr, char *str)  
    { 
            sprintf(str, "%d.%d.%d.%d",                          
                            ((unsigned char*)&saddr)[0],          
                            ((unsigned char*)&saddr)[1],          
                            ((unsigned char*)&saddr)[2],          
                            ((unsigned char*)&saddr)[3]); 
    } 
     
    void UnpackARP(char *buff)  
    { 
            printf("ARP数据包
    "); 
    } 
     
    void UnpackIP(char *buff)  
    { 
            struct iphdr *ip = (struct iphdr*)buff; 
            char *nextStack = buff + sizeof(struct iphdr); 
            int protocol = ip->protocol; 
            char data[20]; 
     
            getAddress(ip->saddr, data); 
            printf("来源ip %s
    ", data); 
             
            bzero(data, sizeof(data)); 
     
            getAddress(ip->daddr, data); 
            printf("目标ip %s
    ", data); 
     
            switch(protocol) 
             { 
                    case 0x06: 
                            UnpackTCP(nextStack); 
                            break; 
     
                    case 0x17: 
                            UnpackUDP(nextStack); 
                            break; 
                     
                    case 0x01: 
                            UnpackICMP(nextStack); 
                            break; 
     
                    case 0x02: 
                            UnpackIGMP(nextStack); 
                            break; 
     
                    default: 
                            printf("unknown protocol
    "); 
                            break; 
            } 
    } 
     
    void UnpackTCP(char *buff)  
    { 
            struct tcphdr *tcp = (struct tcphdr*)buff; 
             
            printf("传输层协议:tcp
    "); 
         
            printf("来源端口:%d
    ", ntohs(tcp->source)); 
            printf("目标端口:%d
    ", ntohs(tcp->dest)); 
    } 
     
    void UnpackUDP(char *buff)  
    { 
            struct udphdr *udp = (struct udphdr*)buff; 
             
            printf("传输层协议:udp
    "); 
         
            printf("来源端口:%d
    ", ntohs(udp->source)); 
            printf("目的端口:%d
    ", ntohs(udp->dest)); 
    } 
     
    void UnpackICMP(char *buff)  
    { 
            printf("ICMP数据包
    ");         
    } 
     
    void UnpackIGMP(char *buff)  
    { 
            printf("IGMP数据包
    "); 
    } 
    

    12.3.2 抓取以太网上的所有数据帧,匹配 HTTP 协议并发送 TCP RST

    代码来源:我的 Github - https://github.com/PikapBai/sniffer_cmpHTTP_sendTCP

    13 参考资料

    1. 套接字 - 百度百科 - https://baike.baidu.com/item/套接字/9637606?fromtitle=socket&fromid=281150&fr=aladdin

    2. RAW SOCKET - 百度百科 - https://baike.baidu.com/item/RAW SOCKET/995623?fromtitle=原始套接字&fromid=23692610&fr=aladdin#ref_[1]_4263346

    3. 原始套接字简介 - chengqiuming - https://blog.csdn.net/chengqiuming/article/details/89577351

    4. 原始套接字概述 - anton_99 - https://blog.csdn.net/anton_99/article/details/95646879?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

    5. Linux 原始套接字抓取底层报文 - 2603898260 - https://blog.csdn.net/s2603898260/article/details/85020006?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

    6. Linux-C TCP 简单例子 - nanfeibuyi - https://blog.csdn.net/nanfeibuyi/article/details/88540144

    7. 【Linux网络编程】socket编程“网络字节顺序”和“主机字节顺序” - qq_20553613 - https://blog.csdn.net/qq_20553613/article/details/86385271

    8. 网络字节序 - 百度百科 - https://baike.baidu.com/item/网络字节序/12610557?fr=aladdin

    9. 字节序(大小端)理解 - sunflower_della - https://blog.csdn.net/sunflower_della/article/details/90439935

    10. 理解大小端字节序 - fan-yuan - https://www.cnblogs.com/fan-yuan/p/10406315.html

    11. linux网络编程之TCP/IP的TCP socket通信过程(含实例代码) - 知乎 - linux服务器开发专栏 - https://zhuanlan.zhihu.com/p/148739946

    12. Linux C Socket UDP编程详解及实例分享 - 知乎 - linux服务器开发专栏 - https://zhuanlan.zhihu.com/p/131402832

    13. Linux-C UDP简单例子 - nanfeibuyi - https://blog.csdn.net/nanfeibuyi/article/details/88540233

    14. 《图解 TCP/IP》(第 5 版)[日]竹下隆史 /[日]村山公保/ [日]荒井透 / [日]苅田幸雄

    15. 浅谈linux下原始套接字 SOCK_RAW 的内幕及其应用 - 知乎 - linux服务器开发专栏 - https://zhuanlan.zhihu.com/p/254912774

    16. GitHub - zhouyingjiu - https://github.com/zouyingjiu/sniffer

  • 相关阅读:
    BZOJ3105: [cqoi2013]新Nim游戏 博弈论+线性基
    BZOJ3759: Hungergame 博弈论+线性基
    NOI模拟赛Day2
    期望dp BZOJ3450+BZOJ4318
    NOI模拟赛 Day1
    NOI模拟 热身赛T1
    【BZOJ4260】 Codechef REBXOR 可持久化Trie
    【BZOJ3673】&&【BZOJ3674】: 可持久化并查集 by zky 可持久化线段树
    【BZOJ3207】花神的嘲讽计划I 可持久化线段树/莫队
    【bzoj3527】[Zjoi2014]力 FFT
  • 原文地址:https://www.cnblogs.com/PikapBai/p/13964866.html
Copyright © 2011-2022 走看看