我最近写一个项目的时候需要涉及到udp传输文件.网上找了找发现没什么合适的,那么我就写了一个自己的供参考.
文件分成两类:第一类是文本文件,第二类是二进制文件.他们的文件读写是不一样的.所以我提供了两种文件的读写,传输方式.
首先看源端DataSource的main.cpp:
#include "UDPSocket.h"
#include "Package.h"
#include <unordered_map>
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <fstream>
using namespace std;
//传输二进制文件的函数
void sendBinFile(){
string dstip = "162.105.85.198";
unsigned short dstport = 51002;
UDPSocket udpsocket;
udpsocket.create(20022);
string file1;
cout << "Please input file name: " << endl;
cin >> file1;
ifstream in(file1, ios::binary);
if(!in){
cout << "File open error" << endl;
return;
}
int count = 1;
//需要一直读取到文件结束,注意这里不是按行读取,是按长度读取
while (!in.eof())
{
DataPackage datapackage;
string contentName = "pku/eecs/file/" + file1 + "/segment" + to_string(count);
strcpy(datapackage.contentName, contentName.c_str());
in.read(datapackage.data, 1400);
//这句话是核心,表示读取的长度.正常情况是1400,但是最后一次可能小于1400
datapackage.datasize = in.gcount();
cout << datapackage.datasize << endl;
datapackage.segmentNum = count;
if(datapackage.datasize != 1400){
datapackage.end = 1;
}
else{
datapackage.end = 0;
}
char sendbuffer[1500];
memcpy(sendbuffer, &datapackage, sizeof(sendbuffer));
udpsocket.sendbuf(sendbuffer, sizeof(sendbuffer), dstip, dstport);
if(datapackage.end == 1){
break;
}
count++;
}
udpsocket.Close();
}
//传输文本文件的函数
void sendTextFile(){
string dstip = "162.105.85.198";
unsigned short dstport = 51002;
UDPSocket udpsocket;
udpsocket.create(20021);
string file1;
cout << "Please input file name: " << endl;
cin >> file1;
ifstream in(file1);
string filename;
string line;
int count = 1;
//按行读取
while (getline (in, line))
{
DataPackage datapackage;
int i;
for(i = 0; i < line.size(); i++){
datapackage.data[i] = line[i];
}
datapackage.data[i] = '\0';
string contentName = "pku/eecs/file/" + file1 + "/segment" + to_string(count);
strcpy(datapackage.contentName, contentName.c_str());
datapackage.datasize = i + 2;
datapackage.segmentNum = count;
//如果已经读到了最后一行,那么加上标志位
if(in.eof()){
datapackage.end = 1;
}
else{
datapackage.end = 0;
}
char sendbuffer[1500];
memcpy(sendbuffer, &datapackage, sizeof(sendbuffer));
udpsocket.sendbuf(sendbuffer, sizeof(sendbuffer), dstip, dstport);
count++;
}
udpsocket.Close();
}
int main(){
//按照你向测试的函数调用即可
//sendTextFile();
//sendBinFile();
return 0;
}
接收端是对应的:
#include "UDPSocket.h"
#include "Package.h"
#include <unordered_map>
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <fstream>
using namespace std;
//接收二进制文件
void receiveBinFile(char* filename){
ofstream outfile(filename, ios::binary);
UDPSocket udpsocket;
udpsocket.create(51002);
char recvBuf[1500];
string srcip_;
unsigned short sport_;
int lenrecv;
while (true)
{
lenrecv = udpsocket.recvbuf(recvBuf, 1500, srcip_, sport_);
if(lenrecv < 0){
cout << "[Error] udpSocket recv error" << endl;
break;
}
DataPackage datapackage;
memcpy(&datapackage, recvBuf, sizeof(DataPackage));
outfile.write(datapackage.data, datapackage.datasize);
//判断是不是最后一个
if(datapackage.end == 1) break;
}
udpsocket.Close();
outfile.close();
}
//接收文本文件
void receiveTextFile(char* filename){
ofstream outfile(filename);
UDPSocket udpsocket;
udpsocket.create(51002);
char recvBuf[1500];
string srcip_;
unsigned short sport_;
int lenrecv;
while (true)
{
lenrecv = udpsocket.recvbuf(recvBuf, 1500, srcip_, sport_);
if(lenrecv < 0){
cout << "[Error] udpSocket recv error" << endl;
break;
}
DataPackage datapackage;
memcpy(&datapackage, recvBuf, sizeof(DataPackage));
outfile << datapackage.data << endl;
//判断是不是最后一行
if(datapackage.end == 1) break;
}
udpsocket.Close();
outfile.close();
}
int main(){
//按需调用,注意传参数为想要保存的文件名称和地址
//receiveTextFile("test2.txt");
receiveBinFile("11583.mp3");
return 0;
}
除了上面两个收发核心代码之外,我定义了传输数据包的格式,以及自己封装了udp socket供调用
Package.h
#ifndef __PACKAGE_H__
#define __PACKAGE_H__
#include <string.h>
#include <iostream>
using namespace std;
struct DataPackage{
/**
* 内容名字 忽略即可
*/
char contentName[50];
/**
* 具体数据,我只是初步定于1400字节,因为考虑到IP的MTU是1500字节
*/
char data[1400];
/**
* 数据长度
*/
int datasize;
/**
* 包序号,排序用的
*/
int segmentNum;
/**
* 是否为最后一个包
* 不是: end = 0
* 是: end = 1
*/
int end;
DataPackage(const char* _contentName, const char* _data, int _size, int _segmentNum, int _end){
strcpy(contentName, _contentName);
strcpy(data, _data);
datasize = _size;
segmentNum = _segmentNum;
end = _end;
}
DataPackage(){}
bool operator==(const DataPackage rhs) const{
//return (this->segmentNum == rhs.segmentNum && this->datasize == rhs.datasize && strlen(this->contentName) == strlen(rhs.contentName) && strncmp(this->contentName, rhs.contentName, sizeof(this->contentName)));
return (strlen(this->contentName) == strlen(rhs.contentName) && strncmp(this->contentName, rhs.contentName, sizeof(this->contentName)) == 0);
}
bool operator < (const DataPackage rhs) const{
string contentstr1 = this->contentName;
string contentstr2 = rhs.contentName;
return contentstr1 < contentstr2;
}
};
#endif
以及封装的udp socket
UDPSocket.h
#pragma once
#ifndef UDPSOCKET_H
#define UDPSOCKET_H
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include<time.h>
#include<string>
#include<unistd.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<iostream>
using std::string;
class UDPSocket
{
public:
UDPSocket();
~UDPSocket();
int create(unsigned short port);//绑定端口
int create();
int create(string mcastip,int mcastport);
int sendbuf(char *buf,int buflen,string destip,unsigned short destport);
int recvbuf(char *buf,int buflen,string&srcip,unsigned short &srcport);
int Close();
int sock;
int m_sock;
};
#endif
UDPSocket.cpp
#include"UDPSocket.h"
#include<iostream>
//#include<WinSock2.h>
UDPSocket::UDPSocket()
{
}
UDPSocket::~UDPSocket()
{
close(sock);
}
int UDPSocket::create()
{
sock=socket(AF_INET,SOCK_DGRAM,0);
if(sock<0)
{
std::cout<<"socket initialize error"<<std::endl;
close(sock);
return -1;
}
return sock;
}
int UDPSocket::create(unsigned short port)
{
//struct sockaddr_in localaddr;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
{
std::cout << "socket initialize error" << std::endl;
close(sock);
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
//int addr_len = sizeof(addr);
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, NULL, 0);
int ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
if (ret == -1)
{
std::cout << "socket bind error" << std::endl;
close(sock);
return -1;
}
return sock;
}
int UDPSocket::create(string mcastip,int mcastport)
{
struct sockaddr_in addr;
struct ip_mreq mreq;
u_int yes = 1;
if ((m_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
std::cout << "mcastsocket error" << std::endl;
return -1;
}
if (setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, &yes,sizeof(yes)) < 0)
{
std::cout << "setsockopt error" << std::endl;
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(mcastport);
if (bind(m_sock, (struct sockaddr*)&addr, sizeof(addr))!=0)
{
std::cout << "msockt bind error" << std::endl;
return -1;
}
mreq.imr_multiaddr.s_addr = inet_addr(mcastip.c_str());
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(m_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
{
std::cout << "msockt bind error" << std::endl;
return -1;
}
return m_sock;
}
int UDPSocket::sendbuf(char *buf,int buflen,string destip,unsigned short destport)
{
struct sockaddr_in dstaddr;
memset(&dstaddr, 0, sizeof(dstaddr));
dstaddr.sin_family = AF_INET;
dstaddr.sin_addr.s_addr = inet_addr(destip.c_str());
dstaddr.sin_port = htons(destport);
return sendto(sock, buf, buflen, 0, (struct sockaddr *)&dstaddr, sizeof(dstaddr));
}
int UDPSocket::recvbuf(char *buf, int buflen, string &srcip, unsigned short &srcport)
{
struct sockaddr_in srcaddr;
int from_addr_len = sizeof(struct sockaddr);
int recvsize;
recvsize = recvfrom(sock,buf,buflen,0,(struct sockaddr *)&srcaddr,(socklen_t *)&from_addr_len);
if (recvsize > 0)
{
srcip = inet_ntoa(srcaddr.sin_addr);
srcport = ntohs(srcaddr.sin_port);
}
return recvsize;
}
int UDPSocket::Close()
{
close(sock);
return 0;
}
这样写了Makefile之后,收发两端都把datapackage.h UDPSocket.h UDPSocket.cpp 包括进去,编译运行即可,即可实现发端读取文件并按照包的形式UDP发送,收端接收到包存储即可.