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:
server:
文件传输:
client:
server:
多线程演示:
分析
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;
}