socket编程基础

Posted sunnypoem

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

以上是关于socket编程基础的主要内容,如果未能解决你的问题,请参考以下文章

SocketJava Socket基础编程

Python 基础之socket编程

Java socket编程API基础

Java Socket编程基础篇

Python基础-第七天-面向对象编程进阶和Socket编程简介

Python之路--Python基础9--Socket编程