简介
此项目在局域网下的两台主机间完成,需要一台主机向另一台主机连续发送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数据包(包括最后一个数据包表示传送的结束)
接收端在100个包中成功接收了71个包,丢失了29个包。
总结
程序仍然有一点点小缺陷,如果作为结束信息的包本身发生丢包,那么接收端会一直等待下去直到接收到结束信息,不过不影响大局,(大不了重来一次),我就不改了。