C++利用SOCKET传送文件

/*server.h*/

#pragma comment(lib, "WS2_32")

#include <WinSock2.h>
#include <iostream>
//#include <stdio.h>
#include <assert.h>

#ifndef COMMONDEF_H
#define COMMONDEF_H

#define MAX_PACKET_SIZE   10240    // 数据包的最大长度,单位是sizeof(char)
#define MAXFILEDIRLENGTH 256     // 存放文件路径的最大长度
#define PORT     4096    // 端口号
#define SERVER_IP    "127.0.0.1" // server端的IP地址

// 各种消息的宏定义
#define INVALID_MSG      -1   // 无效的消息标识
#define MSG_FILENAME     1   // 文件的名称
#define MSG_FILELENGTH     2   // 传送文件的长度
#define MSG_CLIENT_READY    3   // 客户端准备接收文件
#define MSG_FILE      4   // 传送文件
#define MSG_SENDFILESUCCESS    5   // 传送文件成功
#define MSG_OPENFILE_ERROR    10   // 打开文件失败,可能是文件路径错误找不到文件等原因
#define MSG_FILEALREADYEXIT_ERROR 11   // 要保存的文件已经存在了

class CCSDef
{
public:
#pragma pack(1)      // 使结构体的数据按照1字节来对齐,省空间

// 消息头
struct TMSG_HEADER
{
   char    cMsgID;    // 消息标识

TMSG_HEADER(char MsgID = INVALID_MSG)
    : cMsgID(MsgID)
   {
   }
};

// 请求传送的文件名
// 客户端传给服务器端的是全路径名称
// 服务器传回给客户端的是文件名
struct TMSG_FILENAME : public TMSG_HEADER
{
   char szFileName[256];   // 保存文件名的字符数组

TMSG_FILENAME()
    : TMSG_HEADER(MSG_FILENAME)
   {
   }
};

// 传送文件长度
struct TMSG_FILELENGTH : public TMSG_HEADER
{
   long lLength;

TMSG_FILELENGTH(long length)
    : TMSG_HEADER(MSG_FILELENGTH), lLength(length)
   {

}
};

// Client端已经准备好了,要求Server端开始传送文件
struct TMSG_CLIENT_READY : public TMSG_HEADER
{
   TMSG_CLIENT_READY()
    : TMSG_HEADER(MSG_CLIENT_READY)
   {
   }
};

// 传送文件
struct TMSG_FILE : public TMSG_HEADER
{
   union     // 采用union保证了数据包的大小不大于MAX_PACKET_SIZE * sizeof(char)
   {
    char szBuff[MAX_PACKET_SIZE];
    struct
    {
     int nStart;
     int nSize;
     char szBuff[MAX_PACKET_SIZE - 2 * sizeof(int)];
    }tFile;
   };

TMSG_FILE()
    : TMSG_HEADER(MSG_FILE)
   {

}
};

// 传送文件成功
struct TMSG_SENDFILESUCCESS : public TMSG_HEADER
{
   TMSG_SENDFILESUCCESS()
    : TMSG_HEADER(MSG_SENDFILESUCCESS)
   {
   }
};

// 传送出错信息,包括:
// MSG_OPENFILE_ERROR:打开文件失败
// MSG_FILEALREADYEXIT_ERROR:要保存的文件已经存在了
struct TMSG_ERROR_MSG : public TMSG_HEADER
{
   TMSG_ERROR_MSG(char cErrorMsg)
    : TMSG_HEADER(cErrorMsg)
   {
   }
};

#pragma pack()
};

#endif

/*server.cpp*/

#include "server.h"

char g_szNewFileName[MAXFILEDIRLENGTH];
char g_szBuff[MAX_PACKET_SIZE + 1];
long g_lLength;
char* g_pBuff = NULL;

// 初始化socket库
bool InitSocket();
// 关闭socket库
bool CloseSocket();
// 解析消息进行相应的处理
bool ProcessMsg(SOCKET sClient);
// 监听Client的消息
void ListenToClient();
// 打开文件
bool OpenFile(CCSDef::TMSG_HEADER* pMsgHeader, SOCKET sClient);
// 传送文件
bool SendFile(SOCKET sClient);
// 读取文件进入缓冲区
bool ReadFile(SOCKET sClient);

int main()
{
InitSocket();
ListenToClient();
CloseSocket();

return 0;
}

void ListenToClient()
{
// 创建socket套接字
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (SOCKET_ERROR == sListen)
{
   printf("Init Socket Error!\n");
   return;
}

// 绑定socket到一个本地地址
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if (::bind(sListen, (LPSOCKADDR)&sin, sizeof(sockaddr_in)) == SOCKET_ERROR)
{
   printf("Bind Error!\n");
   return;
}

// 设置socket进入监听状态
if (::listen(sListen, 10) == SOCKET_ERROR)
{
   printf("Listen Error!\n");
   return;
}

printf("Listening To Client...\n");

// 循环接收client端的连接请求
sockaddr_in ClientAddr;
int nAddrLen = sizeof(sockaddr_in);
SOCKET sClient;

while (INVALID_SOCKET == (sClient = ::accept(sListen, (sockaddr*)&ClientAddr, &nAddrLen)))
{
}

while (true == ProcessMsg(sClient))
{
}

// 关闭同客户端的连接
::closesocket(sClient);

::closesocket(sListen);
}

bool InitSocket()
{
// 初始化socket dll
WSADATA wsaData;
WORD socketVersion = MAKEWORD(2, 2);
if (::WSAStartup(socketVersion, &wsaData) != 0)
{
   printf("Init socket dll error\n");
   return false;
}

return true;
}

bool CloseSocket()
{
// 释放winsock库
::WSACleanup();

if (NULL != g_pBuff)
{
   delete [] g_pBuff;
   g_pBuff = NULL;
}

return true;
}

bool ProcessMsg(SOCKET sClient)
{
int nRecv = ::recv(sClient, g_szBuff, MAX_PACKET_SIZE + 1, 0);
if (nRecv > 0)
{
   g_szBuff[nRecv] = '\0';
}

// 解析命令
CCSDef::TMSG_HEADER* pMsgHeader = (CCSDef::TMSG_HEADER*)g_szBuff;
switch (pMsgHeader->cMsgID)
{
case MSG_FILENAME:    // 文件名
   {
    OpenFile(pMsgHeader, sClient);
   }
   break;
case MSG_CLIENT_READY:   // 客户端准备好了,开始传送文件
   {
    SendFile(sClient);
   }
   break;
case MSG_SENDFILESUCCESS: // 传送文件成功
   {
    printf("Send File Success!\n");
    return false;
   }
   break;
case MSG_FILEALREADYEXIT_ERROR: // 要保存的文件已经存在了
   {
    printf("The file reay to send already exit!\n");
    return false;
   }
   break;
}

return true;
}

bool ReadFile(SOCKET sClient)
{
if (NULL != g_pBuff)
{
   return true;
}

// 打开文件
FILE *pFile;
if (NULL == (pFile = fopen(g_szNewFileName, "rb")))   // 打开文件失败
{
   printf("Cannot find the file, request the client input file name again\n");
   CCSDef::TMSG_ERROR_MSG tMsgErrorMsg(MSG_OPENFILE_ERROR);
   ::send(sClient, (char*)(&tMsgErrorMsg), sizeof(CCSDef::TMSG_ERROR_MSG), 0);
   return false;
}

// 把文件的长度传回到client去
fseek(pFile, 0, SEEK_END);
g_lLength = ftell(pFile);
printf("File Length = %d\n", g_lLength);
CCSDef::TMSG_FILELENGTH tMsgFileLength(g_lLength);
::send(sClient, (char*)(&tMsgFileLength), sizeof(CCSDef::TMSG_FILELENGTH), 0);

// 处理文件全路径名,把文件名分解出来
char szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szFname[_MAX_FNAME], szExt[_MAX_EXT];
_splitpath(g_szNewFileName, szDrive, szDir, szFname, szExt);
strcat(szFname,szExt);
CCSDef::TMSG_FILENAME tMsgFileName;
strcpy(tMsgFileName.szFileName, szFname);
printf("Send File Name: %s\n", tMsgFileName.szFileName);
::send(sClient, (char*)(&tMsgFileName), sizeof(CCSDef::TMSG_FILENAME), 0);

// 分配缓冲区读取文件内容
g_pBuff = new char[g_lLength + 1];
if (NULL == g_pBuff)
{
   return false;
}

fseek(pFile, 0, SEEK_SET);
fread(g_pBuff, sizeof(char), g_lLength, pFile);
g_pBuff[g_lLength] = '\0';
fclose(pFile);

return true;
}

// 打开文件
bool OpenFile(CCSDef::TMSG_HEADER* pMsgHeader, SOCKET sClient)
{
CCSDef::TMSG_FILENAME* pRequestFilenameMsg = (CCSDef::TMSG_FILENAME*)pMsgHeader;

// 对文件路径名进行一些处理
char *p1, *p2;
for (p1 = pRequestFilenameMsg->szFileName, p2 = g_szNewFileName;
   '\0' != *p1;
   ++p1, ++p2)
{
   if ('\n' != *p1)
   {
    *p2 = *p1;
   }
   if ('\\' == *p2)
   {
    *(++p2) = '\\';
   }
}
*p2 = '\0';

ReadFile(sClient);

return true;
}

// 传送文件
bool SendFile(SOCKET sClient)
{
if (NULL == g_pBuff)
{
   ReadFile(sClient);
}

int nPacketBufferSize = MAX_PACKET_SIZE - 2 * sizeof(int); // 每个数据包存放文件的buffer大小
// 如果文件的长度大于每个数据包所能传送的buffer长度那么就分块传送
for (int i = 0; i < g_lLength; i += nPacketBufferSize)
{  
   CCSDef::TMSG_FILE tMsgFile;
   tMsgFile.tFile.nStart = i;
   if (i + nPacketBufferSize + 1> g_lLength)
   {
    tMsgFile.tFile.nSize = g_lLength - i;
   }
   else
   {
    tMsgFile.tFile.nSize = nPacketBufferSize;
   }
   //printf("start = %d, size = %d\n", tMsgFile.tFile.nStart, tMsgFile.tFile.nSize);
   memcpy(tMsgFile.tFile.szBuff, g_pBuff + tMsgFile.tFile.nStart, tMsgFile.tFile.nSize);
   ::send(sClient, (char*)(&tMsgFile), sizeof(CCSDef::TMSG_FILE), 0);
   Sleep(0.5);
}

delete [] g_pBuff;
g_pBuff = NULL;

return true;
}

/*client.h同server.h*/

/*client.cpp*/

#include "client.h"

long g_lLength = 0;
char* g_pBuff = NULL;
char g_szFileName[MAXFILEDIRLENGTH];
char g_szBuff[MAX_PACKET_SIZE + 1];
SOCKET g_sClient;

// 初始化socket库
bool InitSocket();
// 关闭socket库
bool CloseSocket();
// 把用户输入的文件路径传送到server端
bool SendFileNameToServer();
// 与server端连接
bool ConectToServer();
// 打开文件失败
bool OpenFileError(CCSDef::TMSG_HEADER *pMsgHeader);
// 分配空间以便写入文件
bool AllocateMemoryForFile(CCSDef::TMSG_HEADER *pMsgHeader);
// 写入文件
bool WriteToFile(CCSDef::TMSG_HEADER *pMsgHeader);
// 处理server端传送过来的消息
bool ProcessMsg();

int main()
{
InitSocket();
ConectToServer();
CloseSocket();
return 0;
}

// 初始化socket库
bool InitSocket()
{
// 初始化socket dll
WSADATA wsaData;
WORD socketVersion = MAKEWORD(2, 2);
if (::WSAStartup(socketVersion, &wsaData) != 0)
{
   printf("Init socket dll error\n");
   exit(-1);
}

return true;
}

// 关闭socket库
bool CloseSocket()
{
// 关闭套接字
::closesocket(g_sClient);
// 释放winsock库
::WSACleanup();

return true;
}

// 与server端连接进行文件的传输
bool ConectToServer()
{
// 初始化socket套接字
if (SOCKET_ERROR == (g_sClient = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)))
{
   printf("Init Socket Error!\n");
   exit(-1);
}

sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(PORT);
servAddr.sin_addr.S_un.S_addr = ::inet_addr(SERVER_IP);
if (INVALID_SOCKET == (::connect(g_sClient, (sockaddr*)&servAddr, sizeof(sockaddr_in))))
{
   printf("Connect to Server Error!\n");
   exit(-1);
}

// 输入文件路径传输到server端
SendFileNameToServer();

// 接收server端传过来的信息,直到保存文件成功为止
while (true == ProcessMsg())
{
}

return true;
}

// 把用户输入的文件路径传送到server端
bool SendFileNameToServer()
{
char szFileName[MAXFILEDIRLENGTH];
printf("Input the File Directory: ");

fgets(szFileName, MAXFILEDIRLENGTH, stdin);

// 把文件路径发到server端
CCSDef::TMSG_FILENAME tMsgRequestFileName;
strcpy(tMsgRequestFileName.szFileName, szFileName);
if (SOCKET_ERROR == ::send(g_sClient, (char*)(&tMsgRequestFileName), sizeof(CCSDef::TMSG_FILENAME), 0))
{
   printf("Send File Name Error!\n");
   exit(-1);
}

return true;
}

// 处理server端传送过来的消息
bool ProcessMsg()
{
CCSDef::TMSG_HEADER *pMsgHeader;
int nRecv = ::recv(g_sClient, g_szBuff, MAX_PACKET_SIZE + 1, 0);

pMsgHeader = (CCSDef::TMSG_HEADER*)g_szBuff;

switch (pMsgHeader->cMsgID)
{
case MSG_OPENFILE_ERROR:   // 打开文件错误
   {
    OpenFileError(pMsgHeader);
   }
   break;
case MSG_FILELENGTH:    // 文件的长度
   {
    if (0 == g_lLength)
    {
     g_lLength = ((CCSDef::TMSG_FILELENGTH*)pMsgHeader)->lLength;
     printf("File Length: %d\n", g_lLength);
    }
   }
   break;
case MSG_FILENAME:     // 文件名
   {
    return AllocateMemoryForFile(pMsgHeader);
   }
   break;
case MSG_FILE:      // 传送文件,写入文件成功之后退出这个函数
   {
    if (WriteToFile(pMsgHeader))
    {
     return false;
    }
   }
   break;
}

return true;
}

// 打开文件失败
bool OpenFileError(CCSDef::TMSG_HEADER *pMsgHeader)
{
if (NULL != g_pBuff)
   return true;
assert(NULL != pMsgHeader);

printf("Cannot find file!Please input again!\n");

// 重新输入文件名称
SendFileNameToServer();

return true;
}

// 查找是否已经存在了要保存的文件,同时分配缓冲区保存文件
bool AllocateMemoryForFile(CCSDef::TMSG_HEADER *pMsgHeader)
{
assert(NULL != pMsgHeader);

if (NULL != g_pBuff)
{
   return true;
}

CCSDef::TMSG_FILENAME* pRequestFilenameMsg = (CCSDef::TMSG_FILENAME*)pMsgHeader;
printf("File Name: %s\n", pRequestFilenameMsg->szFileName);

// 把文件的路径设置为C盘根目录下
strcpy(g_szFileName, "c:\\");
strcat(g_szFileName, pRequestFilenameMsg->szFileName);

// 查找相同文件名的文件是否已经存在,如果存在报错退出
FILE* pFile;
if (NULL != (pFile = fopen(g_szFileName, "r")))
{
   // 文件已经存在,要求重新输入一个文件
   printf("The file already exist!\n");
   CCSDef::TMSG_ERROR_MSG tMsgErrorMsg(MSG_FILEALREADYEXIT_ERROR);
   ::send(g_sClient, (char*)(&tMsgErrorMsg), sizeof(CCSDef::TMSG_ERROR_MSG), 0);

fclose(pFile);
   return false;
}

// 分配缓冲区开始接收文件,如果分配成功就给server端发送开始传送文件的要求
g_pBuff = new char[g_lLength + 1];
if (NULL != g_pBuff)
{
   memset(g_pBuff, '\0', g_lLength + 1);
   printf("Now ready to get the file %s!\n", pRequestFilenameMsg->szFileName);
   CCSDef::TMSG_CLIENT_READY tMsgClientReady;

if (SOCKET_ERROR == ::send(g_sClient, (char*)(&tMsgClientReady), sizeof(CCSDef::TMSG_CLIENT_READY), 0))
   {
    printf("Send Error!\n");
    exit(-1);
   }
}
else
{
   printf("Alloc memory for file error!\n");
   exit(-1);
}

return true;
}

// 写入文件
bool WriteToFile(CCSDef::TMSG_HEADER *pMsgHeader)
{
assert(NULL != pMsgHeader);

CCSDef::TMSG_FILE* pMsgFile = (CCSDef::TMSG_FILE*)pMsgHeader;

int nStart = pMsgFile->tFile.nStart;
int nSize = pMsgFile->tFile.nSize;
memcpy(g_pBuff + nStart, pMsgFile->tFile.szBuff, nSize);
if (0 == nStart)
{
   printf("Saving file into buffer...\n");
}

memcpy(g_pBuff + nStart, pMsgFile->tFile.szBuff, nSize);
//printf("start = %d, size = %d\n", nStart, nSize);

// 如果已经保存到缓冲区完毕就写入文件
if (nStart + nSize >= g_lLength)
{
   printf("Writing to disk....\n");
   // 写入文件
   FILE* pFile;
   pFile = fopen(g_szFileName, "w+b");
   fwrite(g_pBuff, sizeof(char), g_lLength, pFile);

delete [] g_pBuff;
   g_pBuff = NULL;
   fclose(pFile);

// 保存文件成功传送消息给server退出server
   CCSDef::TMSG_SENDFILESUCCESS tMsgSendFileSuccess;
   while (SOCKET_ERROR == ::send(g_sClient, (char*)(&tMsgSendFileSuccess), sizeof(CCSDef::TMSG_SENDFILESUCCESS), 0))
   {
   }

printf("Save the file %s success!\n", g_szFileName);

return true;
}
else
{
   return false;
}
}

C++利用SOCKET传送文件相关推荐

  1. 自己动手写简单的web应用服务器(4)—利用socket实现文件的下载

    直接上源码: 服务器: 1 package download; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOut ...

  2. linux备份文件到ftp上,Linux如何利用ssh传送文件至FTP空间进行备份

    在Linux系统中,可通过ssh将文件传送到FPT空间中,将一些重要的数据进行备份,那么要如何实现文件的传送呢?小编给大家具体介绍下Linux利用ssh传送文件到FTP空间的方法. 步骤: 假设要备份 ...

  3. Linux 利用socket实现文件传输

    文章目录 一.要求 二.实现提示: 三.程序 四.现象 一.要求   利用Linux Socket进行文件传输,本次只支持client端向sever端上传文件 二.实现提示: client.c cli ...

  4. win7系统两台电脑之间利用Socket实现文件传输---C++实现

    参考:http://www.jb51.net/article/53732.htm 本博客主要实现了简单win7系统下,两台电脑之间(客户机与服务器)的文件传输,主要是基于Windows网络编程中win ...

  5. 利用ansible传送文件方式

    Ansible 作为一款配置管理和应用部署的软件,日常使用的场景很多,我自己也是重度用户.最近整理下 Ansible 传输文件的几种方式 一.copy模块 src参数 :用于指定需要copy的源文件或 ...

  6. html大文件传输,socket大文件传输

    遇到了在c#中socket如何实现大文件传输 接收端 private void Receive(Socket socket) { NetworkStream ns = new NetworkStrea ...

  7. 利用计算机网络共享传文件,利用windows7系统家庭组让多台电脑(传送文件)资源共享...

    大部分网民家中大概都不止一台电脑,如果需要在家中的几台电脑之间传送文件,你会选择什么方法?用QQ传.用U盘拷?这两种方法最常见,操作起来也不难,但多少还是有点麻烦,尤其是在没有QQ或找不到U盘的情况下 ...

  8. Windows 和Linux利用tftp协议传送文件

    Windows 和Ubuntu利用tftp协议传送文件 这几天公司需要将一个文件从Windows7传到装在虚拟机中的Linux系统中,由于Linux系统只开了tftp协议的69端口,只能通过tftp协 ...

  9. 基于Socket的TCP协议Linux聊天系统(即时聊天、传送文件)

    基于Socket的TCP协议Linux聊天系统(即时聊天.传送文件)有源码!!!! 4.1 需求分析 4.1.1 系统目的 4.1.2 系统结构图 4.1.3 系统功能 4.1.4 系统数据流图 4. ...

最新文章

  1. Android之打造属于自己的侧边栏(SlidingMenu)
  2. 深入了解什么是Docker
  3. jquery.validate.js插件的使用方法
  4. 内存对齐还是需要重视的——XMMATRIX 为例
  5. Linux开发商计划停止开发32位版本
  6. 4.3 Siamese 网络
  7. LeetCode刷题(22)
  8. 吴恩达对话LeCun:神经网络跌宕四十年
  9. python3--列表生成式
  10. python入门经典.pdf
  11. 【操作系统】Nachos 多道程序设计
  12. 更改Wamp下网站地址栏图标的显示
  13. ftp 可以连接 feat 接收服务器响应时超时,[软件工具]CUTFTP上传出现:接收服务器响应时发生超时(60000 毫秒),建立数据 socket 失...
  14. 规则引擎Drools简述
  15. 如何使用MISRA改进嵌入式编程
  16. 如何让html页面缩小时不打乱,如何让表格快速打乱
  17. 笔记本跑python 90多度会坏吗_cpu温度多高才安全?90度会烧坏吗??
  18. kali linux 最新下载,kali新版本下载,Kali Linux 2019.4 开放下载
  19. C++ Combo Box控件用法
  20. cad线段总和lisp_求一个线段长度总和与生成文本 - AutoLISP/Visual LISP 编程技术 - CAD论坛 - 明经CAD社区 - Powered by Discuz!...

热门文章

  1. P3375 【模板】KMP字符串匹配
  2. HDU 1028 Ignatius and the Princess III
  3. hdu 4442 Physical Examination (2012年金华赛区现场赛A题)
  4. Ubuntu下安装FTP服务及使用(VSFTPD详细设置)(二)
  5. 理解Javascript_12_执行模型浅析
  6. 创建非矩形的Windows 窗体
  7. 为什么这么多烂代码?
  8. Python学习笔记_1_基础_2:数据运算、bytes数据类型、.pyc文件(什么鬼)
  9. TCP的几个状态(SYN/FIN/ACK/PSH/RST)
  10. ASP.NET MVC 实现二级域名(泛域名)