开发板与开发主机的文件传输方式

背景

  近来接触到一块可搭载Linux系统的开发板,由于需要对该开发板进行性能评估,故需要将开发主机上交叉编译好的测试工具传输到开发板。此外,公司软件开发在云端完成(云端网络与开发板ping不同,但开发板可ping通电脑本地),且电脑主机不能识别U盘,故常用的网络传输方式和U盘拷贝方式都不能使用,只能本地自建scoket程序,或者通过串口传输。本章将自己编写Socket TCP程序,将本地主机上的文件传输给开发板。



常用传输方式

  1. 网络环境可用(云端可ping通开发板):
  • tftp 传输
  • nfs 传输
  • scp uImage root@192.168.2.8 传输
  1. 云端可识别U盘:
  1. 串口传输:
# 符合 XMODEM, YMODEM, ZMODEM 协议的串口调试软件
# 常用软件用 SecureCRT 和 MobaXterm
# SecureCRT 对该协议的支持较好 ,但毕竟是串口传输,速率较慢,且不稳定,不适合较大文件的传输
 rx  uImage					 # 开发板上执行,再通过串口软件点击发送

# 常用命令有: rx 	rb	rz


解决方案

  编写socket软件,以开发板为服务端,本地电脑为客服端,通过网线直连,进行传输。开发板上的程序需通过交叉编译,编写语言为C++。 也可windows搭建nfs、tftp64服务器(特定软件)、MobaXtermt 使用ftp传输。

Linux下socket编程代码:
server.h

#ifndef _SERVER_H
#define _SERVER_H

#include <iostream>
#include <fstream>
#include <cstring>
#include <string.h>
#include <chrono>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUFSIZE 1024

class Server {
 private:
  int    sockfd, s_socket;    // 描述服务器的套接字
  struct sockaddr_in server;  // 描述服务器信息的结构体
  struct sockaddr_in tcpAddr; // 描述客户端的结构体

  char buf[BUFSIZE];  // 缓冲数组
  int rval = 0;       // 接受反馈

  std::ofstream ofs;  // 文件流指针,用于打开文件

 public:
  char file[BUFSIZE];  // 存放文件名
  char clientIp[128];  // 存放客户端IP
  int clientPort;     // 客户端端口

 public:
  size_t fileSize;    // 描述文件大小
  size_t recvSize;    // 描述已接收的字节
  double time;    // 接受文件所用时间
  double Delay;   // cilent与server之间的延时

 public:
  Server(const int port);
  ~Server();

  // TCP连接建立以及相关信息传输
  bool work();
  double measureDelay();

 private:
  // 通过套接字接受文件
  bool recvFile(const char filename[], double &time);

};

#endif


server.cpp

#include "server.h"

// 构造函数,初始化socket
Server::Server(const int port) {
  // 建立描述服务器的套接字
  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    std::cout << "Can not create socket!" << std::endl;
    exit(0);
  }

  // 将服务器信息中的每个字节用0填充
  memset(&server, 0, sizeof(server));
  // 设置地址簇为Internet协议族——IPV4
  server.sin_family = AF_INET;
  // 设置端口
  server.sin_port = htons(port);
  // server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
  // 设置IP地址任何地址
  server.sin_addr.s_addr = htonl(INADDR_ANY);

  // 将本机IP和端口与sockfd绑定
  rval = bind(sockfd, (struct sockaddr *) &server, sizeof(server));
  if (rval == -1) {
    // 异常处理
    std::cout << "Can not create connect!" << std::endl;
    exit(0);
  }

  // 监听sockfd套接字,限定最大等待队列数为5
  listen(sockfd, 5);
}

// 析构函数,如果套接字存在,就关闭
Server::~Server() {
  if (s_socket >= 0) {
    close(s_socket);
  }
  close(sockfd);
}

bool Server::work() {
  char filename[BUFSIZE] = {0};
  socklen_t tcpAddrLenth = sizeof(tcpAddr);

  // s_socket接受sockfd返回的socket描述符,tcpaddr存放客户端的信息
  if ((s_socket = accept(sockfd, (struct sockaddr *) &tcpAddr, &tcpAddrLenth)) < 0) {
    // 异常处理
    std::cout << "Accept exception";
    exit(1);
  }

  // 转换IP地址
  strcpy(clientIp, inet_ntoa(tcpAddr.sin_addr));
  clientPort = ntohs(tcpAddr.sin_port);

  // 测量延时
  //measureDelay();
  memset(buf, 0, BUFSIZE);

  // 接收文件名
  read(s_socket, filename, BUFSIZE-1);
  strcpy(file, filename);

  // 发送确认信息
  strcpy(buf, "recv");
  write(s_socket, buf, sizeof(buf));

  // 接受文件
  if (!recvFile(filename, time)) {
    return false;
  }
  // 接收成功后就关闭套接字
  close(s_socket);

  return true;
}

bool Server::recvFile(const char filename[], double &time) {
  // 获取当前系统时间
  auto start = std::chrono::system_clock::now();

  // readLen标识从文件流中读取到buf缓冲数组中的字节数
  int readLen = 0;
  recvSize = 0;

  // 创建接受的文件流
  ofs.open(filename, std::ios::out | std::ios::binary);
  if (!ofs.is_open()) {
    std::cout << "Can not open " << filename << std::endl;
    exit(0);
  }

  // 初始化buf缓冲数组为0
  memset(buf, 0, sizeof(buf));
  do {
    readLen = read(s_socket, buf, BUFSIZE);
    if (readLen == -1) {
      // 异常处理
      ofs.close();
      return false;
    } else if (readLen == 0) {
      // readLen为0说明远端已经传输完成并且断开了TCP连接
      break;
    }
    recvSize += readLen;

    // 将buf缓冲数组的内容写入到文件流
    ofs.write(buf, readLen);
    // 初始化buf缓冲数组为0
    memset(buf, 0, sizeof(buf));
  } while (true);

  // 接收成功后就关闭套接字
  ofs.close();

  fileSize = recvSize;

  // 获取当前系统时间,与开始时间相减得到接收文件所用时间
  std::chrono::duration<double> diff = std::chrono::system_clock::now() - start;
  time = diff.count();

  return true;
}

double Server::measureDelay() {
  // 包数量
  int packetNum = 0;

  // 接收到的包数量
  int PacketCount = 0;

  // 接收client发送包的数量
  read(s_socket, (char *) &packetNum, sizeof(packetNum));

  write(s_socket, "recvDone", 9);

  // 开始计时,获取当前系统时间
  auto start = std::chrono::system_clock::now();

  while (true) {
    memset(buf, 0, BUFSIZE);
    if (read(s_socket, buf, BUFSIZE) == -1) {
      std::cout << "Recv packet wrong" << std::endl;
    } else if (strcmp(buf, "sendDone") == 0) {
      // 接收到sendDone信号,说明client发送完毕
      // 获取当前系统时间,得到所用时间,并转换为毫秒
      std::chrono::duration<double> diff = std::chrono::system_clock::now() - start;
      Delay = diff.count();
      break;
    } else {
      ++PacketCount;
    }
  }

  // 发送延时
  write(s_socket, (char *) &Delay, sizeof(Delay));

  return Delay;
}

ServerMain.cpp

#include <iostream>
#include <iomanip>
#include <chrono>

#include "server.h"

using namespace std;

/*
template <class T>
void measure(T&& func)
{
    auto start = chrono::system_clock::now();
    func();
    chrono::duration<double> diff = chrono::system_clock::now() - start;
    cout << diff << endl;
}
*/

// 帮助函数,传入argv第一个字符指针
void help(const char *name) {
  // 裁切掉文件路径,保留文件名
  std::string progname = name;
  size_t lastPos = progname.find_last_of("/\\");
  progname = progname.substr(lastPos + 1);

  cout << ".\\" << progname << " [Options]" << " [Port]" << "[FilePath_1] [FilePath_2] ..." << endl;
  cout << left << setw(10) << "Options :" << endl;
  cout << right << setw(12) << "-r" << "    " << "server receive file from client" << endl;
  cout << right << setw(12) << "-s" << "    " << "server send file to client" << endl << endl;
  cout << left << setw(14) << "Port :" << endl;
  cout << right << setw(12) << "0~1024" << "    " << "Well Known Ports" << endl;  
  cout << right << setw(12) << "1025~49151" << "    " << "Registered Ports" << endl;  
  cout << right << setw(12) << "49152~65535" << "    " << "Dynamic and/or Private Ports" << endl;  
}

int main(int argc, char **argv) {
  if (argc == 1 || (argc == 2 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0)) ) {
    help(argv[0]);
    return 0;
  }

  if (strcmp(argv[1], "-r") != 0 && strcmp(argv[1], "-s") != 0) {
    cout << "please look up by : --help" << endl; 
    return 0;
  }

  // 绑定的端口号
  int port = atoi(argv[2]);
  bool flag = false;

  // 新建一个Server对象
  Server server(port);

  if (strcmp(argv[1], "-r") == 0 ) {
        while (true) {
      // 开始文件接收
      flag = server.work();
      // 如果文件接收成功,打印对应信息
      if (flag) {
        cout << left << setw(18) << server.clientIp;
        cout << left << setw(10) << "Successd:" << left << setw(20) << server.file;
        cout << " Size: " << left << setw(10) << std::to_string(server.fileSize / 1024 / 1024) + "MB";
        cout << "  [" + std::to_string(server.fileSize * 8 / 1024 / 1024 / server.time) + "]Mbps " << endl;
      }
        // 如果文件接收失败,打印对应信息
      else {
        cout << left << setw(18) << server.clientIp
            << left << setw(10) << "Failed:" << server.file << endl;
      }
    }
  } else {
    //     while (true) {
    //   // 开始发送接收
    //   flag = server.work();
    //   // 如果文件接收成功,打印对应信息
    //   if (flag) {
    //     cout << left << setw(18) << server.clientIp;
    //     cout << left << setw(10) << "Successd:" << left << setw(20) << server.file;
    //     cout << " Size: " << left << setw(10) << std::to_string(server.fileSize / 1024 / 1024) + "MB";
    //     cout << "  [" + std::to_string(server.fileSize * 8 / 1024 / 1024 / server.time) + "Mbps] " << endl;
    //   }
    //     // 如果文件接收失败,打印对应信息
    //   else {
    //     cout << left << setw(18) << server.clientIp
    //         << left << setw(10) << "Failed:" << server.file << endl;
    //   }
    // }
  }

  return 0;
}

// g++ --static ServerMain.cpp server.cpp -l ws2_32 -o server

Makefile

CROSS_COMPILE ?=						# 没有定义交叉编译器时,直接用gcc
CC := $(CROSS_COMPILE)g++			    # 编译器	
EXECUTABLE := server     				# 可执行文件名

INCLUDES   := $(wildcard *.h)           # 获取当前目录下的所有头文件
SRC := $(wildcard *.cpp)                # 获取当前目录下的所有.cpp文件
OBJS := $(patsubst %.cpp, %.o, $(SRC))  # 将所有的.cpp文件换成.o文件 server.o ServerMain.o
CPPFLAGS := --std=c++11

RM-F := rm -rf 
.PHONY : clean

$(EXECUTABLE) : $(OBJS)
	@echo	"输出变量OBJS的值,方便调试"
	@echo	$(OBJS)
	$(CC) -o $(EXECUTABLE) $(OBJS) $(CPPFLAGS)

%.o : %.cpp $(INCLUDES)
	$(CC) -c $< -o $@ $(CPPFLAGS)

clean:
	$(RM-F) $(EXECUTABLE) $(OBJS)

windows下socket编程源代码下载地址



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值