• socket编程基础


    参考:https://www.cnblogs.com/jikexianfeng/p/5729168.html

    函数及参数意义

    socket

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

    domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。

    type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。

    protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

    sockaddr_in

    struct sockaddr_in

    {

    short sin_family;/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/

    unsigned short sin_port;/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/

    struct in_addr sin_addr;/*IP address in network byte order(Internet address)*/

    unsigned char sin_zero[8];/*Same size as struct sockaddr没有实际意义,只是为了跟SOCKADDR结构在内存中对齐*/

    };

    sockaddr_in 与 sockaddr :  https://blog.csdn.net/will130/article/details/53326740

    in_addr

    typedef uint32_t in_addr_t;

    struct in_addr

    {

        in_addr_t s_addr;

    };

    地址转换函数

    UDP 发送

    #include <stdio.h>

    #include <stdlib.h>

    #include <string.h>

    #include <arpa/inet.h>

    #include <sys/socket.h>

    #define  MAX_RCVBUF_LEN   60000

    char dstIp[16] = "10.10.10.10";

    unsigned short dstPort = 1083;

    int main(int argc, char *argv[])

    {

        int sock_send;

       

        unsigned char buf[MAX_RCVBUF_LEN];

        int sendLen = 20;

        struct sockaddr_in serv_adr;

        sock_send = socket(PF_INET, SOCK_DGRAM, 0);

        if(sock_send == -1)

        {

            exit(1);

        }

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

           serv_adr.sin_family = AF_INET;

           inet_pton(AF_INET, dstIp, &serv_adr.sin_addr);

           serv_adr.sin_port = htons(dstPort);

       

           sendto(sock_send, buf, sendLen, 0, (struct sockaddr*)&serv_adr, sizeof(serv_adr));

           printf("send buf %d. ", sendLen);

           close(sock_send);

       

        return 0;

    }

    UDP接收

    void* udp_server(void *arg)

    {

        struct sockaddr_in sin;

        struct sockaddr_in rin;

        int sock_fd;

        int bindrt;

        int address_size;

        unsigned int i;

        unsigned char rcvbuf[MAX_RCVBUF_LEN];

        char str[MAX_RCVBUF_LEN];

        unsigned short uwMsglen;

       

        bzero(&sin, sizeof(sin));

        bzero(rcvbuf, MAX_RCVBUF_LEN);

        sin.sin_family = AF_INET;

        sin.sin_addr.s_addr = INADDR_ANY;

        sin.sin_port = htons(rcvPort);   

       

        sock_fd = socket(AF_INET, SOCK_DGRAM, 0);

        bindrt = bind(sock_fd, (struct sockaddr *)&sin, sizeof(sin));

        while(1)

        {

            address_size = sizeof(rin);

            uwMsglen = recvfrom(sock_fd, rcvbuf, MAX_RCVBUF_LEN, 0, (struct sockaddr *)&rin, &address_size);

            if(-1 == uwMsglen)

            {

                perror("call to recvfrom. ");

                exit(1);

            }           

        }

    }

    RAW SOCKET

    两种raw socket应用:http://blog.chinaunix.net/uid-21757287-id-194935.html

    1.socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送接收ip数据包

    2.socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧

    参考了以下文章的代码。

    源码:https://blog.csdn.net/luchengtao11/article/details/73878760

    #include <unistd.h>

    #include <stdio.h>

    #include <sys/socket.h>

    #include <netinet/ip.h>

    #include <netinet/udp.h>

    #include<memory.h>

    #include<stdlib.h>

    #include <linux/if_ether.h>

    #include <linux/if_packet.h> // sockaddr_ll

    #include<arpa/inet.h>

    #include<netinet/if_ether.h>

    #include<iomanip>

    #include<iostream>

    // The packet length

    #define PCKT_LEN 100

    //UDP的伪头部

    struct UDP_PSD_Header

    {

        u_int32_t src;

        u_int32_t des;

        u_int8_t  mbz;

        u_int8_t ptcl;

        u_int16_t len;

    };

    //计算校验和

    unsigned short csum(unsigned short *buf, int nwords)

    {

        unsigned long sum;

        for (sum = 0; nwords > 0; nwords--)

        {

            sum += *buf++;

        }

           while(sum>>16){

                  sum = (sum & 0xffff) + (sum >>16);

           }   

    return (unsigned short)(~sum);

    }

    // Source IP, source port, target IP, target port from the command line arguments

    int main(int argc, char *argv[])

    {

        int sd;

        char buffer[PCKT_LEN] ;

        //查询www.chongfer.cn的DNS报文

    #if 1

        unsigned char DNS[] = { 0xd8, 0xcb , 0x01, 0x00, 0x00, 0x01, 0x00 ,0x00,

            0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77,

            0x08, 0x63, 0x68, 0x6f, 0x6e, 0x67, 0x66, 0x65,

            0x72, 0x02, 0x63, 0x6e, 0x00, 0x00, 0x01, 0x00,

            0x01 };  

    #endif

        struct iphdr *ip = (struct iphdr *) buffer;

        struct udphdr *udp = (struct udphdr *) (buffer + sizeof(struct iphdr));

        // Source and destination addresses: IP and port

        struct sockaddr_in sin, din;

        int  one = 1;

        const int *val = &one;

        //缓存清零

        memset(buffer, 0, PCKT_LEN);

        if (argc != 5)

        {

            printf("- Invalid parameters!!! ");

            printf("- Usage %s <source hostname/IP> <source port> <target hostname/IP> <target port> ", argv[0]);

            exit(-1);

        }

        // Create a raw socket with UDP protocol

        sd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);

        if (sd < 0)

        {

            perror("socket() error");

            // If something wrong just exit

            exit(-1);

        }

        else

            printf("socket() - Using SOCK_RAW socket and UDP protocol is OK. ");

        //IPPROTO_TP说明用户自己填写IP报文

        //IP_HDRINCL表示由内核来计算IP报文的头部校验和,和填充那个IP的id

        if (setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int)))

        {

            perror("setsockopt() error");

            exit(-1);

        }

        else

            printf("setsockopt() is OK. ");

        // The source is redundant, may be used later if needed

        // The address family

        din.sin_family = AF_INET;

        // Port numbers

        din.sin_port = htons(atoi(argv[4]));

        // IP addresses

        din.sin_addr.s_addr = inet_addr(argv[3]);

        // Fabricate the IP header or we can use the

        // standard header structures but assign our own values.

        ip->ihl = 5;

        ip->version = 4;//报头长度,4*32=128bit=16B

        ip->tos = 0; // 服务类型

        ip->tot_len = htons ((sizeof(struct iphdr) + sizeof(struct udphdr)+sizeof(DNS)));

        //ip->id = htons(54321);//可以不写

        ip->ttl = 64; // hops生存周期

        ip->protocol = 17; // UDP

        ip->check = 0;

        // Source IP address, can use spoofed address here!!!

        ip->saddr = inet_addr(argv[1]);

        // The destination IP address

        ip->daddr = inet_addr(argv[3]);

        // Fabricate the UDP header. Source port number, redundant

        udp->source = htons(atoi(argv[2]));//源端口

        // Destination port number

        udp->dest = htons(atoi(argv[4]));//目的端口

        udp->len = htons(sizeof(struct udphdr)+sizeof(DNS));//长度

        //forUDPCheckSum用来计算UDP报文的校验和用

        //UDP校验和需要计算 伪头部、UDP头部和数据部分

        char * forUDPCheckSum = new char[sizeof(UDP_PSD_Header) + sizeof(udphdr)+sizeof(DNS) +1];

        memset(forUDPCheckSum, 0, sizeof(UDP_PSD_Header) + sizeof(udphdr) + sizeof(DNS) +1 );

        UDP_PSD_Header * udp_psd_Header = (UDP_PSD_Header *)forUDPCheckSum;

        udp_psd_Header->src = inet_addr(argv[1]);

        udp_psd_Header->des = inet_addr(argv[3]);

        udp_psd_Header->mbz = 0;

        udp_psd_Header->ptcl = 17;

        udp_psd_Header->len = htons(sizeof(udphdr)+sizeof(DNS));

        memcpy(forUDPCheckSum + sizeof(UDP_PSD_Header), udp, sizeof(udphdr));

        memcpy(forUDPCheckSum + sizeof(UDP_PSD_Header) + sizeof(udphdr), DNS, sizeof(DNS));

        ip->check = csum((unsigned short *)ip, sizeof(iphdr)/2);//可以不用算

        //计算UDP的校验和,因为报文长度可能为单数,所以计算的时候要补0

        udp->check = csum((unsigned short *)forUDPCheckSum,(sizeof(udphdr)+sizeof(UDP_PSD_Header)+sizeof(DNS) +1)/2);

        setuid(getpid());//如果不是root用户,需要获取权限   

        printf("Using Source IP: %s port: %u, Target IP: %s port: %u. ", argv[1], atoi(argv[2]), argv[3], atoi(argv[4]));

        std::cout << "Ip length:" << ip->tot_len << std::endl;

        int count;

        //将DNS报文拷贝进缓存区

        memcpy(buffer + sizeof(iphdr) + sizeof(udphdr), DNS, sizeof(DNS));

       

        if (sendto(sd, buffer, ip->tot_len, 0, (struct sockaddr *)&din, sizeof(din)) < 0)

        {

            perror("sendto() error");

            exit(-1);

        }

        close(sd);

        return 0;

    }

    IP/UDP 头填充

    IP、TCP、UDP头详解:https://blog.csdn.net/mrwangwang/article/details/8537775

      /*IPv4 Header*/

    struct IpHead

    {

        unsigned char     verHeaderLen; /*version (0100 0110) and header len (4 bytes per unit)*/

        unsigned char     typeOfService;/*type of service: PPP D T R M Rsvd*/

        unsigned short     totalLen;     /*Total length: header + data*/

        unsigned short     identifier;   /*Identifier*/

        unsigned short     flagFragOff;  /*Flags and Fragment Offset*/

        unsigned char     ttl;          /*TTL*/

        unsigned char     protocol;     /*Protocol: 1-ICMP  6-TCP 17-UDP*/

        unsigned short     checkSum;     /*Header Checksum*/

        unsigned int    srcIp;  

        unsigned int    dstIp;

        char pData[0];

    };

    /*UDP Header*/

    struct UdpHead

    {

        unsigned short   srcPort;

        unsigned short   dstPort;

        unsigned short   dataLen;

        unsigned short   checkSum;

        char pData[0];

    };

    IP头部校验和字段:

    当发送 IP 包时,需要计算 IP 报头的校验和:

    1 、把校验和字段置为 0 ;

    2 、对 IP 头部中的每 16bit 进行二进制求和;

    3 、 如果和的高 16bit 不为 0 ,则将和的高 16bit 和低 16bit 反复相加,直到和的高 16bit 为 0 ,从而获得一个 16bit 的值;

    4 、将该 16bit 的值取反,存入校验和字段。

    当接收 IP 包时,需要对报头进行确认,检查 IP 头是否有误,算法同上 2 、 3 步,然后判断取反的结果是否为 0 ,是则正确,否则有错。

    unsigned short csum(unsigned short *buf, int nwords)

    {

        unsigned long sum;

        for (sum = 0; nwords > 0; nwords--)

        {

            sum += *buf++;

        }

           while(sum>>16){

                  sum = (sum & 0xffff) + (sum >>16);

           }

        return (unsigned short)(~sum);

    }

    UDP头中校验和字段:占16比特。用来对UDP头部和UDP数据进行校验。和TCP不同的是,对UDP来说,此字段是可选项,而TCP数据段中的校验和字段是必须有的。UDP头中的校验和可以设置为0,表示发送端没有计算校验和。

    文件系统、linux协议栈、系统调用与socket

    牛人总结:https://www.cnblogs.com/kakawater/p/7085122.html

  • 相关阅读:
    语音识别系列之区分性训练和LF-MMI【转】
    node、npm安装与升级
    Vue3.0 新特性以及使用经验总结
    div垂直居中的方法
    前端性能优化
    大型网站设计总结
    前端SEO
    前端埋点总结
    jenkins自动构建、自动部署
    Python常见whl文件集合
  • 原文地址:https://www.cnblogs.com/sunnypoem/p/9551393.html
Copyright © 2020-2023  润新知