UDP传输数据及文件

时间:2022-10-16 19:47:23

UDP传输数据及文件

一、实验内容

内容1:

  • 利用数据报套接字实现数据传输
  • 客户端创建UDP套接字,向指定的服务端发送数据;
  • 服务端接收到新数据,显示是谁发送过来的,并把该数据 回传给发送方;
  • 服务端使用多线程来实现并发。
  • 观察在服务端启动和不启动时,客户端的运行情况。

内容2:

  • 使用UDP套接字实现文件传输
  • 客户端向服务端发送文件名;
  • 客户端向服务端传输文件内容;
  • 双方关闭套接字。
  • 注意收发速度对文件传输结果的影响

二、实验要求

  提交源码(源码编写要规范)、可执行程序、实验报告(要有程序运行截图)。

三、实验分析

server

绑定端口

暴力遍历,绑定失败则端口+1

SOCKET ClientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
sockaddr_in remoteAddr;
remoteAddr = *((sockaddr_in *)lpParameter);

int nAddrLen = sizeof(remoteAddr);
//服务端端口绑定
u_short newport = 8888;
sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(newport);
serAddr.sin_addr.S_un.S_addr = INADDR_ANY;

//尝试绑定新的端口
while (bind(ClientSocket, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
{
newport++;
serAddr.sin_port = htons(newport);
}
接收数据
char message[BUF_MAXLEN];
memset(message,0x00,sizeof(message));
ret = recvfrom(ClientSocket, message, BUF_MAXLEN, 0, (sockaddr *)&remoteAddr, &nAddrLen);//接收数据
cout<<"-----------------------------RECEIVING...-----------------------------"<<endl;
cout<<"Receive Messages:"<<message<<endl;
sendto(ClientSocket,message, strlen(message), 0, (sockaddr *)&remoteAddr, nAddrLen);//回显数据
//表明传输时间对象
SYSTEMTIME st;
GetLocalTime(&st);
cout<<"Linking Time:"<<st.wYear<<"/"<<st.wMonth<<"/"<<st.wDay<<" "<<st.wHour<<":"<<st.wMinute<<endl;
cout<<"Client "<<inet_ntoa(remoteAddr.sin_addr)<<":"<<ntohs(remoteAddr.sin_port)<< " Connection!" << endl;
cout<<"----------------------------------------------------------------------"<<endl;
cout<<endl;
接收文件

进入新的线程之后,还是之前的方法获取文件的名字长度,之后进行循环的接收,直到接收完毕

while(true)
{
char recvData[BUF_MAXLEN];
char file_name[MAX_NAME];
//接收文件名
ret = recvfrom(ClientSocket, file_name, BUF_MAXLEN, 0, (sockaddr *)&remoteAddr, &nAddrLen);
file_name[ret] = '\0';
cout<<"-----------------------------RECEIVING...-----------------------------"<<endl;
//打开文件
FILE * fp;
if (!(fp = fopen(file_name, "wb")))
{
cout<<"File "<<file_name<<" can't open"<<endl;
return -1;
}
memset(recvData, 0, BUF_MAXLEN);
int file_len = 0;//接收文件
while ((file_len = recvfrom(ClientSocket, recvData, BUF_MAXLEN, 0, (sockaddr *)&remoteAddr, &nAddrLen)))
{
//cout << "file_len = "<< file_len << endl;
if (file_len == INVALID_SOCKET || file_len == 0)
{
cout << "Receive Error" << endl;
return -1;
}
ret = fwrite(recvData, sizeof(char), file_len, fp);//写文件
//fflush(fp);//磁盘刷新
if (ret < file_len)
{
cout<<file_name<<" Failed"<<endl;
break;
}
memset(recvData, 0, BUF_MAXLEN);
}
cout<<"Receive "<<file_name<<" Successful!"<<endl;
//表明传输时间,对象
SYSTEMTIME st;
GetLocalTime(&st);
cout<<"Linking Time:"<<st.wYear<<"/"<<st.wMonth<<"/"<<st.wDay<<" "<<st.wHour<<":"<<st.wMinute<<endl;
cout<<"Client "<<inet_ntoa(remoteAddr.sin_addr)<<":"<<ntohs(remoteAddr.sin_port)<< " Connection!" << endl;
cout<<"----------------------------------------------------------------------"<<endl;
cout<<endl;
}

client

发送数据
cout<<"------------------------------SENDING...------------------------------"<<endl;
cout<<"Send Message:";
memset(sendData,0x00,sizeof(sendData));
cin>>sendData;
getchar();
sendto(sclient, sendData, strlen(sendData), 0, (sockaddr *)&sin, len);//发送数据
cout<<"Client OK!"<<endl;
SYSTEMTIME st;
GetLocalTime(&st);
cout<<"Linking Time:"<<st.wYear<<"/"<<st.wMonth<<"/"<<st.wDay<<" "<<st.wHour<<":"<<st.wMinute<<endl;
cout << "Server:" <<inet_ntoa(sin.sin_addr)<< ":" <<ntohs(sin.sin_port)<< " Connection!" << endl;

memset(recvData,0x00,sizeof(recvData));
ret = recvfrom(sclient, recvData, BUF_MAXLEN, 0, (sockaddr *)&sin, &len);//回显数据
if(ret > 0)
{
cout<<"Receive From Server: "<<recvData<<endl;
cout<<"----------------------------------------------------------------------"<<endl;
cout<<endl;
memset(recvData,0x00,sizeof(recvData));
}
发送文件
cout<<"------------------------------SENDING...------------------------------"<<endl;
cout<<"Please input the filename:"<<endl;
cin>>file_name;
//传送文件名
sendto(sclient, file_name, strlen(file_name), 0, (sockaddr *)&sin, len);
if( !(fp = fopen(file_name,"rb")) )
{
cout<<"File "<<file_name<<" can't open"<<endl;
continue;
}

memset(sendData,0x00,sizeof(sendData));
//传送文件
int length;
while ((length = fread(sendData, sizeof(char), sizeof(sendData), fp)) > 0)
{
//cout << "length=" << length << endl;
//cout << sendData << endl;
ret = sendto(sclient, sendData, length, 0, (sockaddr *)&sin, len);
if (ret == SOCKET_ERROR || ret == 0)
{
cout<<"wrong"<<endl;
return -1;
}
memset(sendData, 0, sizeof(sendData));
}
ret = sendto(sclient, sendData, length, 0, (sockaddr *)&sin, len);
if (ret == SOCKET_ERROR)
{
cout<<"wrong"<<endl;
return -1;
}
else
{
cout<<"send successful!"<<endl;
}
fclose(fp);

截图

数据传输:

client:
UDP传输数据及文件
server:
UDP传输数据及文件

文件传输:

client:
UDP传输数据及文件
server:
UDP传输数据及文件

多线程演示:

UDP传输数据及文件

分析

UDP主要丢包原因:

  • 接收端处理时间过长导致丢包:调用 recvfrom方法接收端收到数据后,处理数据花了一些时间,处理完后再次调用 recvfrom方法,在这二次调用间隔里,发过来的包可能丢失。对于这种情况可以修改接收端,将包接收后存入一个缓冲区,然后迅速返回继续 recvfrom。

  • 发送的包巨大丢包:这种情况需要切割成小包再逐个send。

  • 发送的包频率太快:虽然每个包的大小都小于mtu size 但是频率太快,例如40多个mut size的包连续发送中间不sleep,也有可能导致丢包。

源码

client

//
// client.cpp
//
#include <stdio.h>
#include <iostream>
#include <winsock2.h>

using namespace std;

#pragma comment(lib, "ws2_32.lib")

#define BUF_MAXLEN 1024

int main(int argc, char* argv[])
{
WORD socketVersion = MAKEWORD(2,2);
WSADATA wsaData;
if(WSAStartup(socketVersion, &wsaData) != 0)
{
cout<<"Init Windows Socket Failed::"<<GetLastError()<<endl;
return 0;
}
SOCKET sclient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int len = sizeof(sin);

char choose[10];
cout<<"Do you want to 1. Transfer message OR 2. Send the file (choose 1# OR 2#)"<<endl;
cin>>choose;
getchar();

char sendData[BUF_MAXLEN];
char recvData[BUF_MAXLEN];
if(!strncmp(choose,"1#",2))
{
sendto(sclient, choose, strlen(choose), 0, (sockaddr *)&sin, len);
char * sendData_ = "HELLO!";
sendto(sclient, sendData_, strlen(sendData_), 0, (sockaddr *)&sin, len);//发送hello
cout<<"Client OK!"<<endl;

memset(recvData,0x00,sizeof(recvData));
int ret = recvfrom(sclient, recvData, BUF_MAXLEN, 0, (sockaddr *)&sin, &len);//接收hello
cout<<"--------------------------------"<<recvData<<"--------------------------------"<<endl;
sendto(sclient, choose, strlen(choose), 0, (sockaddr *)&sin, len);//再次传输选择,使线程选择模式
while( true )
{
char info[100];

cout<<"Input '#!#' to quit OR Press any key to continue. "<<endl;
cin>>info;//选择
getchar();//吃掉回车
if(!strncmp(info,"#!#",3))
{
break;
}

cout<<"------------------------------SENDING...------------------------------"<<endl;
cout<<"Send Message:";
memset(sendData,0x00,sizeof(sendData));
cin>>sendData;
getchar();
sendto(sclient, sendData, strlen(sendData), 0, (sockaddr *)&sin, len);//发送数据
cout<<"Client OK!"<<endl;
SYSTEMTIME st;
GetLocalTime(&st);
cout<<"Linking Time:"<<st.wYear<<"/"<<st.wMonth<<"/"<<st.wDay<<" "<<st.wHour<<":"<<st.wMinute<<endl;
cout << "Server:" <<inet_ntoa(sin.sin_addr)<< ":" <<ntohs(sin.sin_port)<< " Connection!" << endl;

memset(recvData,0x00,sizeof(recvData));
ret = recvfrom(sclient, recvData, BUF_MAXLEN, 0, (sockaddr *)&sin, &len);//回显数据
if(ret > 0)
{
cout<<"Receive From Server: "<<recvData<<endl;
cout<<"----------------------------------------------------------------------"<<endl;
cout<<endl;
memset(recvData,0x00,sizeof(recvData));
}
}
}
else if(!strncmp(choose,"2#",2))
{
sendto(sclient, choose, strlen(choose), 0, (sockaddr *)&sin, len);
char * sendData_ = "HELLO!";
sendto(sclient, sendData_, strlen(sendData_), 0, (sockaddr *)&sin, len);//发送hello
cout<<"Client OK!"<<endl;

memset(recvData,0x00,sizeof(recvData));
int ret = recvfrom(sclient, recvData, BUF_MAXLEN, 0, (sockaddr *)&sin, &len);//接收hello
cout<<"--------------------------------"<<recvData<<"--------------------------------"<<endl;
sendto(sclient, choose, strlen(choose), 0, (sockaddr *)&sin, len);//再次传输选择,使线程选择模式
while( true )
{
char info[100];
cout<<"Input '#!#' to quit OR Press any key to continue. "<<endl;
cin>>info;
getchar();
if(!strncmp(info,"#!#",3))
{
break;
}
FILE *fp;
char file_name[MAX_PATH];

cout<<"------------------------------SENDING...------------------------------"<<endl;
cout<<"Please input the filename:"<<endl;
cin>>file_name;
//传送文件名
sendto(sclient, file_name, strlen(file_name), 0, (sockaddr *)&sin, len);
if( !(fp = fopen(file_name,"rb")) )
{
cout<<"File "<<file_name<<" can't open"<<endl;
continue;
}

memset(sendData,0x00,sizeof(sendData));
//传送文件
int length;
while ((length = fread(sendData, sizeof(char), sizeof(sendData), fp)) > 0)
{
//cout << "length=" << length << endl;
//cout << sendData << endl;
ret = sendto(sclient, sendData, length, 0, (sockaddr *)&sin, len);
if (ret == SOCKET_ERROR || ret == 0)
{
cout<<"wrong"<<endl;
return -1;
}
memset(sendData, 0, sizeof(sendData));
}
ret = sendto(sclient, sendData, length, 0, (sockaddr *)&sin, len);
if (ret == SOCKET_ERROR)
{
cout<<"wrong"<<endl;
return -1;
}
else
{
cout<<"send successful!"<<endl;
}
fclose(fp);
//表明传输时间,对象
SYSTEMTIME st;
GetLocalTime(&st);
cout<<"Linking Time:"<<st.wYear<<"/"<<st.wMonth<<"/"<<st.wDay<<" "<<st.wHour<<":"<<st.wMinute<<endl;
cout <<"Server:" <<inet_ntoa(sin.sin_addr)<< ":" <<ntohs(sin.sin_port)<< " Connection!" << endl;
cout<<"----------------------------------------------------------------------"<<endl;
}
}

closesocket(sclient);
WSACleanup();
return 0;
}

server

//
// server.cpp
//
#include <stdio.h>
#include <iostream>
#include <winsock2.h>

using namespace std;
#pragma comment(lib, "ws2_32.lib")

#define BUF_MAXLEN 1024
#define MAX_NAME 1024

DWORD WINAPI ClientThread(LPVOID lpParameter)
{
//服务端UDP套接字
SOCKET ClientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
sockaddr_in remoteAddr;
remoteAddr = *((sockaddr_in *)lpParameter);

int nAddrLen = sizeof(remoteAddr);
//服务端端口绑定
u_short newport = 8888;
sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(newport);
serAddr.sin_addr.S_un.S_addr = INADDR_ANY;

//尝试绑定新的端口
while (bind(ClientSocket, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
{
newport++;
serAddr.sin_port = htons(newport);
}

char * sendData = "HELLO!";
sendto(ClientSocket,sendData, strlen(sendData), 0, (sockaddr *)&remoteAddr, nAddrLen);//发送hello

char choose[100];
memset(choose,0,sizeof(choose));
recvfrom(ClientSocket, choose, BUF_MAXLEN, 0, (sockaddr *)&remoteAddr, &nAddrLen);//接收选择

int ret;
if(!strncmp(choose,"1#",2))
{
while(true)
{
//memset(sendData,0x00,sizeof(sendData));
char message[BUF_MAXLEN];
memset(message,0x00,sizeof(message));
ret = recvfrom(ClientSocket, message, BUF_MAXLEN, 0, (sockaddr *)&remoteAddr, &nAddrLen);//接收数据
cout<<"-----------------------------RECEIVING...-----------------------------"<<endl;
cout<<"Receive Messages:"<<message<<endl;
sendto(ClientSocket,message, strlen(message), 0, (sockaddr *)&remoteAddr, nAddrLen);//回显数据
//表明传输时间对象
SYSTEMTIME st;
GetLocalTime(&st);
cout<<"Linking Time:"<<st.wYear<<"/"<<st.wMonth<<"/"<<st.wDay<<" "<<st.wHour<<":"<<st.wMinute<<endl;
cout<<"Client "<<inet_ntoa(remoteAddr.sin_addr)<<":"<<ntohs(remoteAddr.sin_port)<< " Connection!" << endl;
cout<<"----------------------------------------------------------------------"<<endl;
cout<<endl;
}
}
else if(!strncmp(choose,"2#",2))
{
while(true)
{
char recvData[BUF_MAXLEN];
char file_name[MAX_NAME];
//接收文件名
ret = recvfrom(ClientSocket, file_name, BUF_MAXLEN, 0, (sockaddr *)&remoteAddr, &nAddrLen);
file_name[ret] = '\0';
cout<<"-----------------------------RECEIVING...-----------------------------"<<endl;
//打开文件
FILE * fp;
if (!(fp = fopen(file_name, "wb")))
{
cout<<"File "<<file_name<<" can't open"<<endl;
return -1;
}
memset(recvData, 0, BUF_MAXLEN);
int file_len = 0;//接收文件
while ((file_len = recvfrom(ClientSocket, recvData, BUF_MAXLEN, 0, (sockaddr *)&remoteAddr, &nAddrLen)))
{
//cout << "file_len = "<< file_len << endl;
if (file_len == INVALID_SOCKET || file_len == 0)
{
cout << "Receive Error" << endl;
return -1;
}
ret = fwrite(recvData, sizeof(char), file_len, fp);//写文件
//fflush(fp);//磁盘刷新
if (ret < file_len)
{
cout<<file_name<<" Failed"<<endl;
break;
}
memset(recvData, 0, BUF_MAXLEN);
}
cout<<"Receive "<<file_name<<" Successful!"<<endl;
//表明传输时间,对象
SYSTEMTIME st;
GetLocalTime(&st);
cout<<"Linking Time:"<<st.wYear<<"/"<<st.wMonth<<"/"<<st.wDay<<" "<<st.wHour<<":"<<st.wMinute<<endl;
cout<<"Client "<<inet_ntoa(remoteAddr.sin_addr)<<":"<<ntohs(remoteAddr.sin_port)<< " Connection!" << endl;
cout<<"----------------------------------------------------------------------"<<endl;
cout<<endl;
}
}
return 0;
}

int main(int argc, char* argv[])
{
WSADATA wsaData;
SOCKET ServerSocket,ClientSocket;

WORD sockVersion = MAKEWORD(2,2);
if ( WSAStartup(sockVersion, &wsaData) != 0 )
{
cout<<"Init Windows Socket Failed:"<<GetLastError()<<endl;
closesocket(ClientSocket);
return -1;
}

ServerSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if ( ServerSocket == INVALID_SOCKET )
{
cout<<"Create Socket Failed:"<<GetLastError()<<endl;
closesocket(ServerSocket);
WSACleanup();
return -1;
}

sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(8888);
serAddr.sin_addr.S_un.S_addr = INADDR_ANY;

if(bind(ServerSocket, (sockaddr*)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
{
cout<<"bind error !"<<endl;
return 0;
}
else
{
cout<<"Server is started!"<<endl;
}

sockaddr_in remoteAddr;
int nAddrLen = sizeof(remoteAddr);

while (true)
{
char choose[100];
memset(choose,0,sizeof(choose));
recvfrom(ServerSocket, choose, BUF_MAXLEN, 0, (sockaddr *)&remoteAddr, &nAddrLen);//接收选择
if(!strncmp(choose,"1#",2))
{
cout<<"Message"<<endl;
char recvData[BUF_MAXLEN];
memset(recvData,0x00,sizeof(recvData));
int ret = recvfrom(ServerSocket, recvData, BUF_MAXLEN, 0, (sockaddr *)&remoteAddr, &nAddrLen);//接收hello
if (!ret)
{
cout<<"Receive Failed:"<<GetLastError()<<endl;
}
cout<<"Receive the Connectiong:"<<inet_ntoa(remoteAddr.sin_addr)<<":"<<ntohs(remoteAddr.sin_port)<<endl;
cout<<"--------------------------------"<<recvData<<"--------------------------------"<<endl;
cout<<"Client OK!"<<endl;

HANDLE hThread = CreateThread(NULL, 0, ClientThread, &remoteAddr, 0, NULL);
CloseHandle(hThread);
}
else if(!strncmp(choose,"2#",2))
{
cout<<"File"<<endl;
char recvData[BUF_MAXLEN];
memset(recvData,0x00,sizeof(recvData));
int ret = recvfrom(ServerSocket, recvData, BUF_MAXLEN, 0, (sockaddr *)&remoteAddr, &nAddrLen);//接收hello
if (!ret)
{
cout<<"Receive Failed:"<<GetLastError()<<endl;
}
cout<<"Receive the Connectiong:"<<inet_ntoa(remoteAddr.sin_addr)<<":"<<ntohs(remoteAddr.sin_port)<<endl;
cout<<"--------------------------------"<<recvData<<"--------------------------------"<<endl;
cout<<"Client OK!"<<endl;

HANDLE hThread = CreateThread(NULL, 0, ClientThread, &remoteAddr, 0, NULL);
CloseHandle(hThread);
}
}

WSACleanup();
return 0;
}