基于tcp raw socket实现的端到端rtt,丢包率检测程序(1)

5 篇文章 0 订阅
3 篇文章 0 订阅

    通常我们检测rtt和丢包率是采用ping(利用icmp请求响应报文),这里提供了一种采用tcp的方式计算rtt和丢包率。原理比较简单,利用tcp raw socket自己封装tcp syn报文,接收对方发过的syn+ack报文,以此来计算平滑rtt和丢包率。当前只实现了一对一的扫描,发送端需要绑定本地IP和一个端口,默认是选择的80端口,当然还需要指定对端IP和端口(默认也是80)。整个实现逻辑比较原始,串行扫描,使用sendto接口发送报文,recvfrom接口接收报文,实际上利用pcap库来接收效率更高。另外也没有利用类似epoll这样的异步模型。在第二版的时候我要改成基于epoll的异步模型。在第三版的时候可以实现一对多,这个多不是一点点,是全网上亿个IP的扫描,第二、三版代码已写完,找个时间更新在博客上。
* Author: ZuoSi<zuosi8968@gmail.com>
* Date: 2015-11-12
***********************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>           // close()
#include <string.h>           // strcpy, memset(), and memcpy()
#include <fcntl.h>
#include <netdb.h>            // struct addrinfo
#include <sys/types.h>        // needed for socket(), uint8_t, uint16_t, uint32_t
#include <sys/socket.h>       // needed for socket()
#include <netinet/in.h>       // IPPROTO_RAW, IPPROTO_TCP, IPPROTO_ICMP, IPPROTO_UDP, INET_ADDRSTRLEN
#include <netinet/ip.h>       // struct ip and IP_MAXPACKET (which is 65535)
#include <netinet/ip_icmp.h>  // struct icmp and ICMP_TIME_EXCEEDED
#define __FAVOR_BSD           // Use BSD format of TCP header and UDP header
#include <netinet/tcp.h>      // struct tcphdr
#include <netinet/udp.h>      // struct udphdr
#include <arpa/inet.h>        // inet_pton() and inet_ntop()
#include <sys/ioctl.h>        // macro ioctl is defined
#include <bits/ioctls.h>      // defines values for argument "request" of ioctl.
#include <net/if.h>           // struct ifreq
#include <linux/if_ether.h>   // ETH_P_IP = 0x0800, ETH_P_IPV6 = 0x86DD
#include <linux/if_packet.h>  // struct sockaddr_ll (see man 7 packet)
#include <net/ethernet.h>
#include <sys/time.h>         // gettimeofday()
#include <getopt.h>
#include <errno.h>            // errno, perror()

// Define some constants.
#define ETH_HDRLEN 14  // Ethernet header length
#define IP4_HDRLEN 20  // IPv4 header length
#define TCP_HDRLEN 20  // TCP header length, excludes options data

#define SYN_ACK     0
#define SYN_NO_ACK  1
#define NO_SYN      2

#define MAX_MSG_LEN 1024
#define TCPING_VERSION "1.0"

#define DEBUG
#ifdef DEBUG
#define DEBUG_OUTPUT(msg) ({ printf("%s", msg); })
#else
#define DEBUG_OUTPUT(msg) ({})
#endif

#define TRUE 1
#define FALSE 0

// Function prototypes
uint16_t checksum (uint16_t *, int);
uint16_t tcp4_checksum (struct ip, struct tcphdr, uint8_t *, int);
int create_ip_frame (uint8_t *, char *, uint16_t, char *, uint16_t, int, uint32_t, uint8_t *, int);
char *allocate_strmem (int);
uint8_t *allocate_ustrmem (int);
int *allocate_intmem (int);

int is_valid_ip(const char* ip)
{
    int n[4];
    char c[4];

    if (sscanf(ip, "%d%c%d%c%d%c%d%c",
             &n[0], &c[0], &n[1], &c[1],
             &n[2], &c[2], &n[3], &c[3])
      == 7)
    {
        int i;
        for(i = 0; i < 3; ++i){
            if (c[i] != '.')
                return FALSE;
        }
        for(i = 0; i < 4; ++i){
            if (n[i] > 255 || n[i] < 0)
                return FALSE;
        }
        return TRUE;
    } else {
        return FALSE;
    }
}


void
print_usage(const char *prog_name)
{
    printf("%s 4.3.0\n", prog_name);
    printf("This is a program stat packet lose\n");
    printf("Usage: %s -s <soure_ip:source_port> -d <dest_ip:dest_port> [options]\n", prog_name);
    printf("    -s --source local addr:port,    e.g. 192.168.10.10,192.168.10.10:80\n");
    printf("    -d --dest   dest  addr:port,    e.g. 192.168.10.10,192.168.10.10:80\n");
    
    printf("Options:\n");
    printf("    -t --timeout response packet wait timeout(ms)   e.g 200ms\n");
    printf("    -c --count send packet count    e.g 1000\n");
    printf("    -m --multiplex set wto equal to multiplex * rtt e.g 4\n");
    printf("    -v --verbose detail outout,default statistic information\n");
}


int
main (int argc, char **argv)
{
    int multiplex;
    float factor, ratio, rtt, rtts, wto;
    int first, set_wto, set_interval, verbose;
    uint32_t interval, idt, recv_again_max, again_cnt;
    double dt;
    int i, status, frame_length, sd, sendsd, recsd, bytes, ttl;
    char rec_ip[INET_ADDRSTRLEN], src_ip[INET_ADDRSTRLEN] = {'\0'}, dst_ip[INET_ADDRSTRLEN]={'\0'};
    uint32_t pack_cnt_max, pack_cnt, pack_lost;
    uint32_t syn_seq, syn_ack_seq, syn_seq_timeout_skip;
    int datalen;
    char *tcp_dat;
    uint16_t src_port, dst_port;
    char hostname[NI_MAXHOST];
    char msg[MAX_MSG_LEN];
    struct ip *iphdr;
    struct tcphdr *tcphdr;
    uint8_t *snd_ip_frame, *rec_ip_frame;
    uint8_t *data;
    struct sockaddr_in dst_sa, src_sa;
    struct sockaddr from;
    struct ifreq ifr;
    socklen_t fromlen;
    struct timeval wait, t1, t2, ti1;
    struct timezone tz;
    int optval = 1;
    char* port_p;
    int8_t arg=0;
    char *short_opts = "hV?s:d:t:c:m:i:v";
    const struct option long_opts[] = {
        {"help",        1,  NULL,   'h'},
        {"source",      1,  NULL,   's'},
        {"dest",        1,  NULL,   'd'},
        {"count",       1,  NULL,   'c'},
        {"timeout",     1,  NULL,   't'},
        {"interval",    1,  NULL,   'i'},
        {"verbose",     0,  NULL,   'v'},
        {"multiplex",   1,  NULL,   'm'},
        {"version",     0,  NULL,   'V'} 
    };

    multiplex = 5;
    pack_cnt = 0;
    pack_lost = 0;
    pack_cnt_max = 1000;
    wto = 1000;
    set_wto = FALSE;
    set_interval = FALSE;
    interval = 0;           
    first = TRUE;
    factor = 0.125;    
    verbose = FALSE;
    src_port = dst_port = 80;
    syn_seq_timeout_skip = 1000;

    while((arg = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1){
        switch(arg){
            case 's':
                port_p = strrchr(optarg, ':');
                if(port_p){
                    if(*(port_p+1) == '\0'){
                        fprintf(stderr, "Error: invalid parameters %s\n", optarg);
                        return(EXIT_FAILURE);
                    }
                    src_port = atoi(port_p+1);
                    *port_p = '\0';
                }
                strncpy(src_ip, optarg, INET_ADDRSTRLEN);
                break;
            case 'd':
                port_p = strrchr(optarg, ':');
                if(port_p){
                    if(*(port_p+1) == '\0'){
                        fprintf(stderr, "Error: invalid parameters %s\n", optarg);
                        return(EXIT_FAILURE);
                    }
                    dst_port = atoi(port_p+1);
                    *port_p = '\0';
                }
                strncpy(dst_ip, optarg, INET_ADDRSTRLEN);
                break;
            case 't':
                wto = atoi(optarg);
                if(wto < 0 || wto > 5000){
                    fprintf(stderr, "Error: impossible timeout value, please set between 0~5000(ms)\n");
                    return(EXIT_FAILURE);
                }
                set_wto = TRUE;
                break;
            case 'c':
                if(atoi(optarg) <= 0){
                    fprintf(stderr, "Error: invalid count.\n");
                    return(EXIT_FAILURE);
                }
                pack_cnt_max = atoi(optarg); 
                break;
            case 'm':
                if(atoi(optarg) <= 0){
                    fprintf(stderr, "Error: invalid multiplex.\n");
                    return(EXIT_FAILURE);
                }
                multiplex = atoi(optarg);
                break;
            case 'i':
                interval = atof(optarg) * 1000;
                set_interval = TRUE;
                break;
            case 'v':
                verbose = TRUE;
                break;
            case 'V':
                printf("version: %s\n", TCPING_VERSION);
                return(EXIT_SUCCESS);
            case 'h':
            case '?':
                print_usage(argv[0]);
                return(EXIT_FAILURE);
        }
    }

    if(!strlen(src_ip) || !strlen(dst_ip)){
        print_usage(argv[0]);
        return(EXIT_FAILURE);
    }
    if(!is_valid_ip(src_ip) || !is_valid_ip(dst_ip)){
        printf("Error: invalid ip addr.\n");
        return(EXIT_FAILURE);
    }

    // Allocate memory for various arrays.
    tcp_dat = allocate_strmem (IP_MAXPACKET);
    data = allocate_ustrmem (IP_MAXPACKET);
    snd_ip_frame = allocate_ustrmem (IP_MAXPACKET);
    rec_ip_frame = allocate_ustrmem (IP_MAXPACKET);


    // Payloads for TCP packets.
    strcpy (tcp_dat, "");

    // SYN init sequence
    syn_seq = 0;
    syn_ack_seq = syn_seq + 1;
    // Check for acceptable payload lengths.
    if (strlen (tcp_dat) > (IP_MAXPACKET - IP4_HDRLEN - TCP_HDRLEN)) {
        fprintf (stderr, "Maximum TCP data length exceeded. Maximum length is %i\n", IP_MAXPACKET - IP4_HDRLEN - TCP_HDRLEN);
        exit (EXIT_FAILURE);
    }

    // Submit request for a raw socket descriptors - one to send, one to receive.
    if ((sendsd = socket (PF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
        perror ("socket() failed to obtain a send socket descriptor ");
        exit (EXIT_FAILURE);
    }
    if(setsockopt(sendsd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(optval)) < 0){
        perror("setsockopt failed ");
        exit(EXIT_FAILURE);
    }

    if ((recsd = socket (PF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
        perror ("socket() failed to obtain a receive socket descriptor ");
        exit (EXIT_FAILURE);
    }
    
    // Set time for the socket to timeout and give up waiting for a reply.
    recv_again_max = 6;
    wait.tv_sec  = 0;  
    wait.tv_usec = 500000;
    setsockopt (recsd, SOL_SOCKET, SO_RCVTIMEO, (char *)&wait, sizeof(struct timeval));

    memset(&src_sa, 0, sizeof(struct sockaddr_in)); 
    inet_pton(AF_INET, src_ip, (void*)&src_sa.sin_addr);
    src_sa.sin_port = htons(src_port);  
    if(bind(recsd, (struct sockaddr*)&src_sa, sizeof(struct sockaddr_in)) < 0){
        perror("bind() failed ");
        exit(EXIT_FAILURE);
    }
    //ttl
    ttl = 64;

    // LOOP: incrementing TTL each time, exiting when we get our target IP address.
    iphdr = (struct ip *)rec_ip_frame;
    tcphdr = (struct tcphdr *)(rec_ip_frame + IP4_HDRLEN);

    (void) gettimeofday (&ti1, &tz);
    for (;;) {
        // Create probe packet.
        memset (snd_ip_frame, 0, IP_MAXPACKET * sizeof (uint8_t));
        datalen = strlen (tcp_dat);
        memcpy (data, tcp_dat, datalen * sizeof (uint8_t));
        create_ip_frame (snd_ip_frame, src_ip, src_port, dst_ip, dst_port, ttl, syn_seq, data, datalen);
        
        frame_length = IP4_HDRLEN + TCP_HDRLEN + datalen;

        // Send ip frame to socket.
        memset(&dst_sa, 0, sizeof(struct sockaddr_in));         //notice: must init sockaddr_in zero
        inet_pton(AF_INET, dst_ip, (void*)&dst_sa.sin_addr);
        dst_sa.sin_family = AF_INET;
        dst_sa.sin_port = htons(dst_port);

        (void) gettimeofday (&t1, &tz);
        if(set_interval){
            idt = (t1.tv_sec - ti1.tv_sec) * 1000 + (t1.tv_usec - ti1.tv_usec) / 1000;                 
            if(idt < interval)
                usleep((interval - idt) * 1000);
            // Get time again, this is very important
            (void) gettimeofday (&t1, &tz);
            ti1 = t1;
        }
        
        if(pack_cnt >= pack_cnt_max){
            ratio = (float)pack_lost / (float)(pack_cnt_max + pack_cnt_max - pack_lost) * 100.0;
            printf("\n---------tcping statistics---------\n");
            printf("send  : %u\n", pack_cnt_max);
            printf("lost  : %u\n", pack_lost);
            printf("ratio : %2.4f%\n", ratio);
            printf("rtts  : %2.4f\n", rtts);
            printf("wto   : %2.4f\n", wto);
            printf("addr  : %s\n", dst_ip);
            goto end_prog;
        }
        ++pack_cnt; 

        if ((bytes = sendto (sendsd, snd_ip_frame, frame_length, 0, (struct sockaddr*)&dst_sa, sizeof(struct sockaddr_in))) <= 0) {
            perror ("sendto() failed");
            exit (EXIT_FAILURE);
        }
       
        // Start timer.
        // Listen for incoming ip frame from socket sd.
        // RECEIVE LOOP
        again_cnt = 0;
        for (;;) {
            memset (rec_ip_frame, 0, IP_MAXPACKET * sizeof (uint8_t));
            memset (&from, 0, sizeof (from));
            fromlen = sizeof (from);
            if ((bytes = recvfrom (recsd, rec_ip_frame, IP_MAXPACKET, 0, (struct sockaddr *)&from, &fromlen)) < 0) {
                status = errno;
                // Deal with error conditions first.
                if (status == EAGAIN) {  // EAGAIN = 11
                    ++again_cnt;
                    if(again_cnt > recv_again_max){
                        ++pack_lost;
                        if(verbose) {
               		        ratio = (float)pack_lost / (float)(pack_cnt + pack_cnt - pack_lost) * 100.0;
                            printf ("%-u\t %-s\t\t lost\t\t %2.4fms\t %u/%u\t %2.4f\n", pack_cnt, dst_ip, wto, pack_lost, pack_cnt, ratio);
                            syn_seq = syn_seq + syn_seq_timeout_skip;
                        }
                        break;
                    }
                    continue;  // Recv again
                } else if (status == EINTR) {  // EINTR = 4
                    continue;  // Something weird happened, but let's keep listening.
                } else {
                    perror ("recvfrom() failed.\n");
                    exit (EXIT_FAILURE);
                }
            }  // End of error handling conditionals.

            // Stop timer and calculate how long it took to get a reply.
            (void) gettimeofday (&t2, &tz);
            dt = (double)(t2.tv_sec - t1.tv_sec) * 1000.0 + (double) (t2.tv_usec - t1.tv_usec) / 1000.0;
            
            // Check for an IP ethernet frame. If not, ignore and keep listening.
            // Did we reach our destination?
            if ((iphdr->ip_p == IPPROTO_TCP) && (tcphdr->th_flags == 18)) { // (18 = SYN, ACK)
                 // Extract source IP address from received ethernet frame.
                if (inet_ntop (AF_INET, &(iphdr->ip_src.s_addr), rec_ip, INET_ADDRSTRLEN) == NULL) {
                    status = errno;
                    fprintf (stderr, "inet_ntop() failed.\nError message: %s", strerror (status));
                    exit (EXIT_FAILURE);
                }

                // Check rec ip and ack sequence
                if ((strcmp(rec_ip, dst_ip) != 0) || (ntohs(tcphdr->th_dport) != src_port) || (ntohl(tcphdr->th_ack) != syn_ack_seq)) {
                    continue;
                } 
              
                // Recv correct syn+ack packet 
                rtt = dt;
                if(first){
                    rtts = rtt;
                    first = FALSE;
                }
                else{
                    rtts = (1.0-factor)*rtts + factor*rtt;    
                }
                
                if(!set_wto)
                    wto =  multiplex * rtts;

                // Report source IP address and time for reply.
                if(verbose) {
                    ratio = (float)pack_lost / (float)(pack_cnt + pack_cnt - pack_lost) * 100.0;
                    printf ("%-u\t %-s\t\t %2.4fms\t %2.4fms\t %u/%u\t %2.4f\n", pack_cnt, dst_ip, rtts, wto, pack_lost, pack_cnt, ratio);
                }
                syn_seq = syn_seq + 1;
                break;
            }  // End of Reached Destination conditional.

            if(dt > wto){
                ++pack_lost;
                if(verbose) {
                    ratio = (float)pack_lost / (float)(pack_cnt + pack_cnt - pack_lost) * 100.0;
                    printf("%-u\t %-s\t\t lost\t\t %2.4fms\t %u/%u\t %2.4f\n", pack_cnt, dst_ip, wto, pack_lost, pack_cnt, ratio);
                }
                // reset syn init sequence
                syn_seq = syn_seq + syn_seq_timeout_skip;
                break;
            }
        }  // End of Receive loop.
        syn_seq = syn_seq > 655350 ? 0 : syn_seq;
        syn_ack_seq = syn_seq + 1;
    }  // End of Send loop.

end_prog: 
    // Close socket descriptors.
    close (sendsd);
    close (recsd);

    // Free allocated memory.
    free (tcp_dat);
    free (data);
    free (snd_ip_frame);
    free (rec_ip_frame);

    return (EXIT_SUCCESS);
}

// Create a TCP IP frame.
int
create_ip_frame (uint8_t *snd_ip_frame, char *src_ip, uint16_t src_port, char *dst_ip, uint16_t dst_port, int ttl, uint32_t seq, uint8_t *data, int datalen)
{
    int i, status, *ip_flags, *tcp_flags;
    struct ip iphdr;
    struct tcphdr tcphdr;

    // Allocate memory for various arrays.
    ip_flags = allocate_intmem (4);
    tcp_flags = allocate_intmem (8);

    // IPv4 header

    // IPv4 header length (4 bits): Number of 32-bit words in header = 5
    iphdr.ip_hl = IP4_HDRLEN / sizeof (uint32_t);

    // Internet Protocol version (4 bits): IPv4
    iphdr.ip_v = 4;

    // Type of service (8 bits)
    iphdr.ip_tos = 0;

    // Total length of datagram (16 bits): IP header + TCP header + data
    iphdr.ip_len = htons (IP4_HDRLEN + TCP_HDRLEN + datalen);

    // ID sequence number (16 bits): unused, since single datagram
    iphdr.ip_id = htons (0);

    // Flags, and Fragmentation offset (3, 13 bits): 0 since single datagram

    // Zero (1 bit)
    ip_flags[0] = 0;

    // Do not fragment flag (1 bit)
    ip_flags[1] = 0;

    // More fragments following flag (1 bit)
    ip_flags[2] = 0;

    // Fragmentation offset (13 bits)
    ip_flags[3] = 0;

    iphdr.ip_off = htons ((ip_flags[0] << 15)
                        + (ip_flags[1] << 14)
                        + (ip_flags[2] << 13)
                        +  ip_flags[3]);

    // Time-to-Live (8 bits): default to maximum value
    iphdr.ip_ttl = ttl;

    // Transport layer protocol (8 bits): 6 for TCP
    iphdr.ip_p = IPPROTO_TCP;

    // Source IPv4 address (32 bits)
    if ((status = inet_pton (AF_INET, src_ip, &(iphdr.ip_src))) != 1) {
      fprintf (stderr, "inet_pton() failed.\nError message: %s", strerror (status));
      exit (EXIT_FAILURE);
    }

    // Destination IPv4 address (32 bits)
    if ((status = inet_pton (AF_INET, dst_ip, &(iphdr.ip_dst))) != 1) {
      fprintf (stderr, "inet_pton() failed.\nError message: %s", strerror (status));
      exit (EXIT_FAILURE);
    }

    // IPv4 header checksum (16 bits): set to 0 when calculating checksum
    iphdr.ip_sum = 0;
    iphdr.ip_sum = checksum ((uint16_t *) &iphdr, IP4_HDRLEN);

    // TCP header

    // Source port number (16 bits)
    tcphdr.th_sport = htons (src_port);

    // Destination port number (16 bits)
    tcphdr.th_dport = htons (dst_port);

    // Sequence number (32 bits)
    tcphdr.th_seq = htonl (seq);

    // Acknowledgement number (32 bits): 0 in first packet of SYN/ACK process
    tcphdr.th_ack = htonl (seq + 1);

    // Reserved (4 bits): should be 0
    tcphdr.th_x2 = 0;

    // Data offset (4 bits): size of TCP header in 32-bit words
    tcphdr.th_off = TCP_HDRLEN / 4;

    // Flags (8 bits)

    // FIN flag (1 bit)
    tcp_flags[0] = 0;

    // SYN flag (1 bit): set to 1
    tcp_flags[1] = 1;

    // RST flag (1 bit)
    tcp_flags[2] = 0;

    // PSH flag (1 bit)
    tcp_flags[3] = 0;

    // ACK flag (1 bit)
    tcp_flags[4] = 0;

    // URG flag (1 bit)
    tcp_flags[5] = 0;

    // ECE flag (1 bit)
    tcp_flags[6] = 0;

    // CWR flag (1 bit)
    tcp_flags[7] = 0;

    tcphdr.th_flags = 0;
    for (i=0; i<8; i++) {
      tcphdr.th_flags += (tcp_flags[i] << i);
    }

    // Window size (16 bits)
    tcphdr.th_win = htons (65535);

    // Urgent pointer (16 bits): 0 (only valid if URG flag is set)
    tcphdr.th_urp = htons (0);

    // TCP checksum (16 bits)
    tcphdr.th_sum = tcp4_checksum (iphdr, tcphdr, data, datalen);

    // Fill out ethernet frame header.

    // Next is ethernet frame data (IPv4 header + TCP header).
    // IPv4 header
    memcpy (snd_ip_frame, &iphdr, IP4_HDRLEN * sizeof (uint8_t));

    // TCP header
    memcpy (snd_ip_frame + IP4_HDRLEN, &tcphdr, TCP_HDRLEN * sizeof (uint8_t));

    // TCP data
    memcpy (snd_ip_frame + IP4_HDRLEN + TCP_HDRLEN, data, datalen * sizeof (uint8_t));

    // Free allocated memory.
    free (ip_flags);
    free (tcp_flags);

    return (EXIT_SUCCESS);
}

// Checksum function
uint16_t
checksum (uint16_t *addr, int len)
{
    int nleft = len;
    int sum = 0;
    uint16_t *w = addr;
    uint16_t answer = 0;

    while (nleft > 1) {
        sum += *w++;
        nleft -= sizeof (uint16_t);
    }

    if (nleft == 1) {
        *(uint8_t *) (&answer) = *(uint8_t *) w;
        sum += answer;
    }

    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    answer = ~sum;
    return (answer);
}

// Build IPv4 TCP pseudo-header and call checksum function.
uint16_t
tcp4_checksum (struct ip iphdr, struct tcphdr tcphdr, uint8_t *payload, int payloadlen)
{
    uint16_t svalue;
    char buf[IP_MAXPACKET], cvalue;
    char *ptr;
    int chksumlen = 0;
    int i;

    ptr = &buf[0];  // ptr points to beginning of buffer buf

    // Copy source IP address into buf (32 bits)
    memcpy (ptr, &iphdr.ip_src.s_addr, sizeof (iphdr.ip_src.s_addr));
    ptr += sizeof (iphdr.ip_src.s_addr);
    chksumlen += sizeof (iphdr.ip_src.s_addr);

    // Copy destination IP address into buf (32 bits)
    memcpy (ptr, &iphdr.ip_dst.s_addr, sizeof (iphdr.ip_dst.s_addr));
    ptr += sizeof (iphdr.ip_dst.s_addr);
    chksumlen += sizeof (iphdr.ip_dst.s_addr);

    // Copy zero field to buf (8 bits)
    *ptr = 0; ptr++;
    chksumlen += 1;

    // Copy transport layer protocol to buf (8 bits)
    memcpy (ptr, &iphdr.ip_p, sizeof (iphdr.ip_p));
    ptr += sizeof (iphdr.ip_p);
    chksumlen += sizeof (iphdr.ip_p);

    // Copy TCP length to buf (16 bits)
    svalue = htons (sizeof (tcphdr) + payloadlen);
    memcpy (ptr, &svalue, sizeof (svalue));
    ptr += sizeof (svalue);
    chksumlen += sizeof (svalue);

    // Copy TCP source port to buf (16 bits)
    memcpy (ptr, &tcphdr.th_sport, sizeof (tcphdr.th_sport));
    ptr += sizeof (tcphdr.th_sport);
    chksumlen += sizeof (tcphdr.th_sport);

    // Copy TCP destination port to buf (16 bits)
    memcpy (ptr, &tcphdr.th_dport, sizeof (tcphdr.th_dport));
    ptr += sizeof (tcphdr.th_dport);
    chksumlen += sizeof (tcphdr.th_dport);

    // Copy sequence number to buf (32 bits)
    memcpy (ptr, &tcphdr.th_seq, sizeof (tcphdr.th_seq));
    ptr += sizeof (tcphdr.th_seq);
    chksumlen += sizeof (tcphdr.th_seq);

    // Copy acknowledgement number to buf (32 bits)
    memcpy (ptr, &tcphdr.th_ack, sizeof (tcphdr.th_ack));
    ptr += sizeof (tcphdr.th_ack);
    chksumlen += sizeof (tcphdr.th_ack);

    // Copy data offset to buf (4 bits) and
    // copy reserved bits to buf (4 bits)
    cvalue = (tcphdr.th_off << 4) + tcphdr.th_x2;
    memcpy (ptr, &cvalue, sizeof (cvalue));
    ptr += sizeof (cvalue);
    chksumlen += sizeof (cvalue);

    // Copy TCP flags to buf (8 bits)
    memcpy (ptr, &tcphdr.th_flags, sizeof (tcphdr.th_flags));
    ptr += sizeof (tcphdr.th_flags);
    chksumlen += sizeof (tcphdr.th_flags);

    // Copy TCP window size to buf (16 bits)
    memcpy (ptr, &tcphdr.th_win, sizeof (tcphdr.th_win));
    ptr += sizeof (tcphdr.th_win);
    chksumlen += sizeof (tcphdr.th_win);

    // Copy TCP checksum to buf (16 bits)
    // Zero, since we don't know it yet
    *ptr = 0; ptr++;
    *ptr = 0; ptr++;
    chksumlen += 2;

    // Copy urgent pointer to buf (16 bits)
    memcpy (ptr, &tcphdr.th_urp, sizeof (tcphdr.th_urp));
    ptr += sizeof (tcphdr.th_urp);
    chksumlen += sizeof (tcphdr.th_urp);

    // Copy payload to buf
    memcpy (ptr, payload, payloadlen);
    ptr += payloadlen;
    chksumlen += payloadlen;

    // Pad to the next 16-bit boundary
    for (i=0; i<payloadlen%2; i++, ptr++) {
      *ptr = 0;
      ptr++;
      chksumlen++;
    }

    return checksum ((uint16_t *) buf, chksumlen);
}

// Allocate memory for an array of chars.
char *
allocate_strmem (int len)
{
    void *tmp;

    if (len <= 0) {
        fprintf (stderr, "ERROR: Cannot allocate memory because len = %i in allocate_strmem().\n", len);
        exit (EXIT_FAILURE);
    }

    tmp = (char *) malloc (len * sizeof (char));
    if (tmp != NULL) {
        memset (tmp, 0, len * sizeof (char));
        return (tmp);
    } else {
        fprintf (stderr, "ERROR: Cannot allocate memory for array allocate_strmem().\n");
        exit (EXIT_FAILURE);
    }
}

// Allocate memory for an array of unsigned chars.
uint8_t *
allocate_ustrmem (int len)
{
    void *tmp;

    if (len <= 0) {
        fprintf (stderr, "ERROR: Cannot allocate memory because len = %i in allocate_ustrmem().\n", len);
        exit (EXIT_FAILURE);
    }

    tmp = (uint8_t *) malloc (len * sizeof (uint8_t));
    if (tmp != NULL) {
        memset (tmp, 0, len * sizeof (uint8_t));
        return (tmp);
    } else {
        fprintf (stderr, "ERROR: Cannot allocate memory for array allocate_ustrmem().\n");
        exit (EXIT_FAILURE);
    }
}

// Allocate memory for an array of ints.
int *
allocate_intmem (int len)
{
    void *tmp;

    if (len <= 0) {
        fprintf (stderr, "ERROR: Cannot allocate memory because len = %i in allocate_intmem().\n", len);
        exit (EXIT_FAILURE);
    }

    tmp = (int *) malloc (len * sizeof (int));
    if (tmp != NULL) {
        memset (tmp, 0, len * sizeof (int));
        return (tmp);
    } else {
        fprintf (stderr, "ERROR: Cannot allocate memory for array allocate_intmem().\n");
        exit (EXIT_FAILURE);
    }
}

 

 

 

 

 

【运行输出】

 

 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,以下是一个基于socket的Ping程序设计与实现: 1. 客户端向服务器发送一个Ping消息; 2. 服务器收到Ping消息后,立即将其作为回复消息发送回客户端; 3. 客户端收到回复消息后,计算往返时间(RTT)并输出。 具体实现步骤如下: 1. 客户端发送Ping消息: ```python import socket import time # 客户端IP和端口号 client_address = ('127.0.0.1', 8000) # 创建UDP套接字 client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 发送Ping消息 for i in range(10): # 记录发送时间 send_time = time.time() message = f'Ping {i} {send_time}' client_socket.sendto(message.encode(), client_address) time.sleep(1) # 关闭套接字 client_socket.close() ``` 2. 服务器接收Ping消息并回复: ```python import socket # 服务器IP和端口号 server_address = ('127.0.0.1', 8000) # 创建UDP套接字 server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 绑定服务器IP和端口号 server_socket.bind(server_address) # 接收Ping消息并回复 while True: message, client_address = server_socket.recvfrom(1024) server_socket.sendto(message, client_address) # 关闭套接字 server_socket.close() ``` 3. 客户端接收回复消息并计算RTT: ```python import socket import time # 服务器IP和端口号 server_address = ('127.0.0.1', 8000) # 创建UDP套接字 client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 发送和接收Ping消息 for i in range(10): # 记录发送时间 send_time = time.time() message = f'Ping {i} {send_time}' client_socket.sendto(message.encode(), server_address) # 接收回复消息并计算RTT message, server_address = client_socket.recvfrom(1024) receive_time = time.time() rtt = receive_time - send_time print(f'Ping {i} RTT: {rtt}') time.sleep(1) # 关闭套接字 client_socket.close() ``` 这个Ping程序可以在本地网络环境下测试网络延迟。需要注意的是,我们使用的是UDP套接字,因为Ping程序对网络的可靠性要求不高。如果需要更高的可靠性,可以考虑使用TCP套接字。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值