简介

此项目在局域网下的两台主机间完成,需要一台主机向另一台主机连续发送100个包,并统计另一台主机实际收到的包的个数。

程序设计

1.配置环境
实验选择在Dev C++中开发,由于使用了windows网络编程的相关API,需要包含<winsock2.h>头文件以及在项目中添加静态链接文件wsock32.lib。
添加静态链接文件的方法:项目->项目属性->参数->链接->加入库或者对象,找到wsock32.lib并添加到项目中。
选择同一个局域网下的两台主机,分别作为发送端和接收端,ip分别为192.168.1.109、192.168.1.108。

2.程序设计
在发送端主机上新建一个项目,引入所需的头文件和静态链接文件,然后编写发送端程序;
在接收端主机上新建一个项目,引入所需的头文件和静态链接文件,然后编写接收端程序。
发送端程序大致分别以下几部分:
使用socket()创建套接字serverscok、使用bind()为套接字绑定本主机的ip和一个端口、创建一个struct sockaddr_in对象并将接收端的ip和端口号赋给它、在一个循环内使用sendto()将100个UDP数据包从serversock发送到接收端、使用closesocket()释放分配给套接字的资源。
接收端程序大致分为以下几部分:
使用socket()创建套接字clientsock、使用bind()为套接字绑定本主机的ip和一个端口、在一个循环内使用recvfrom()接收发送端发过来的数据包、统计丢包数、使用closesocket()释放分配给套接字的资源。

程序实现

发送端

//server which send messages to client

#include<winsock2.h>
#include<stdio.h>
#include<stdlib.h>
#define WSVERS MAKEWORD(2, 0)
void errexit(const char*, ...);//错误信息打印函数

int main(int argc, char* argv[]) {
    struct sockaddr_in sock_address, to_address;
    SOCKET serversock;
    WSADATA wsadata;
    
    if (WSAStartup(WSVERS, &wsadata) != 0) {
        errexit("WSAStartup failed\n");
    }
    //create and bind serversock
    serversock = socket(AF_INET, SOCK_DGRAM, 0); //获得serversock
    sock_address.sin_family = AF_INET; //设置地址家族
    sock_address.sin_port = htons(12345); //设置端口
    sock_address.sin_addr.s_addr = inet_addr("192.168.1.109"); //设置地址
    int flag = bind(serversock, (struct sockaddr*) &sock_address, sizeof(struct sockaddr));
    if (flag != 0) errexit("bind falied.\n");

    char buf[4096];
    
    //initiate to_address with client's address
    to_address.sin_family = AF_INET;
    to_address.sin_port = htons(12345); //设置端口
    const char * addr = "192.168.1.108";
    to_address.sin_addr.s_addr = inet_addr(addr); //设置地址
     
    int count = 0;
    while (++count <= 101) {
        char buf[4096];
        if (count <= 100)
            sprintf(buf, "Hello, I'm number %d of packet sent from %s", count, "192.168.1.109");
        else 
            sprintf(buf, "end of messages");
        flag = sendto(serversock, buf, 4096, 0, (struct sockaddr*) &to_address, sizeof(struct sockaddr));
        if (flag == -1) {
            errexit("send falied, errno = %d\n", GetLastError());
        }
        printf("send message '%s' to %s\n", buf, addr);
    }
    
    (void)closesocket(serversock);

    return 0;
    
}


void errexit(const char *format, ...) {
    va_list args;

    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    WSACleanup();
    exit(1);

}

接收端

//client which receive messages from server

#include<winsock2.h>
#include<stdio.h>
#include<stdlib.h>
#define WSVERS MAKEWORD(2, 0)
void errexit(const char*, ...);//错误信息打印函数

int main(int argc, char* argv[]) {
    struct sockaddr_in sock_address, from_address;
    SOCKET clientsock;
    WSADATA wsadata;
    
    if (WSAStartup(WSVERS, &wsadata) != 0) {
        errexit("WSAStartup failed\n");
    }
    //create and bind serversock
    clientsock = socket(AF_INET, SOCK_DGRAM, 0); //获得serversock
    sock_address.sin_family = AF_INET; //设置地址家族
    sock_address.sin_port = htons(12345); //设置端口
    sock_address.sin_addr.s_addr = inet_addr("192.168.1.108"); //设置地址
    int flag = bind(clientsock, (struct sockaddr*) &sock_address, sizeof(struct sockaddr));
    if (flag != 0) errexit("bind falied.\n");

    
     
    char buf[4096];
    
    int count = 0;
    int fromlen;
    const char* endquotes = "end of messages";
    while (true) {
        flag = recvfrom(clientsock, buf, 4096, 0, NULL, NULL);
        if (flag == -1) {
            errexit("receive falied, errno = %d\n", GetLastError());
        }
        printf("%s\n", buf);
        if (strcmp(endquotes, buf) == 0) break;
        ++count;
        printf("count: %d\n", count);
    }
    printf("\n\ntotal 100, actually receive %d, lost %d\n", count, 100 - count);
    (void)closesocket(clientsock);

    return 0;
    
}

void errexit(const char *format, ...) {
    va_list args;

    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    WSACleanup();
    exit(1);

}

运行结果

先运行接收端程序,使其处于阻塞状态,然后运行发送端程序。

结果显示,发送端成功地发送出101个UDP数据包(包括最后一个数据包表示传送的结束)

Wireshark中如何判断udp是否丢包_接收端


接收端在100个包中成功接收了71个包,丢失了29个包。

Wireshark中如何判断udp是否丢包_网络_02


Wireshark中如何判断udp是否丢包_接收端_03

总结

程序仍然有一点点小缺陷,如果作为结束信息的包本身发生丢包,那么接收端会一直等待下去直到接收到结束信息,不过不影响大局,(大不了重来一次),我就不改了。