Linux c++ udp按包发送接收文件

我最近写一个项目的时候需要涉及到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发送,收端接收到包存储即可.

  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值