Windows下通过socket进行字符串和文件传输

  今天在windows平台下,通过socket实现了简单的文件传输。通过实现这一功能,了解基本的windows网络编程和相关函数的使用方法。

  在windows平台上进行网络编程,首先都需要调用函数WSAStartup()进行链接库的初始化。如果没有使用该函数进行初始化,则后面会出现10093的错误(可以通过GetLastError()获得错误码)。

  进行初始化后,客户端和服务器进行不同的工作。但是不管是服务器还是客户端,都需要用到两个最基本的结构体,分别是SOCKET和sockaddr_in。其中,SOCKET结构体用于表示一个socket连接。sockaddr_in表示一个地址结构,用它来保存需要连接的主机ip地址及端口号,或者是需要监听的端口号。

  客户端基本流程图如下图:

Windows下通过socket进行字符串和文件传输

  服务器基本流程图如下:

Windows下通过socket进行字符串和文件传输

  这次实现的功能主要是:客户端向服务器发送一个字符串和一个文件(图片文件),服务器以接收到的字符串为文件名,并将接收到的文件保存到本地。

  客户端源代码如下:

//file_transfer.h

/**************************************************
*Author:xiongmao
*
*Date:2016/2/22 17:53
*
*Description:用于网络间的文件传输
*
**************************************************/ #pragma once
#include <string> #ifndef WIN_SOCKET_
#define WIN_SOCKET_
#include <WinSock2.h>
#pragma comment(lib , "ws2_32.lib")
#endif using std::string; class FileTransfer
{
public:
FileTransfer();
~FileTransfer();
/*****************************************************************************
* @name : setIpAndPort
*
* @author : xiongmao
*
* @create date : 2016/2/18 16:04
*
* @function:设置目标ip地址和端口号
*
* @inparam :
* ip:目标ip地址
* port:目标端口号
* @outparam :
*
* @last change : 2016/2/18 16:04
*****************************************************************************/
bool setIpAndPort(string ip,int port);
/*****************************************************************************
* @name : setFilePath
*
* @author : xiongmao
*
* @create date : 2016/2/18 16:06
*
* @function:设置需要发送的文件的路径
*
* @inparam :
* path:文件路径
* @outparam :
*
* @last change : 2016/2/18 16:06
*****************************************************************************/
bool setFilePath(string path);
/*****************************************************************************
* @name : sendFile
*
* @author : xiongmao
*
* @create date : 2016/2/18 16:07
*
* @function:发送简单字符串信息和指定文件给目标主机
*
* @inparam :
* msg:需要发送给目标主机的一些简单信息
* filePath:需要传输的文件的路径
* @outparam :
*
* @last change : 2016/2/21 16:41
*****************************************************************************/
bool sendFile(string msg,string filePath);
private: const static int BUFFER_SIZE=; int m_Port;
string m_IpAddr;
string m_FilePath; WSADATA ws;
SOCKET m_ServerSocket;
sockaddr_in m_ServerAddr; };
//file_transfer.cpp

#include "file_transfer.h"
#include <fstream>
#include <iostream>
using namespace std; FileTransfer::FileTransfer()
{
m_Port=-;
/*
if ( WSAStartup(MAKEWORD(2,2), &ws) != 0 )
{
printf("Init Windows Socket Failed,the error code is : %d \n", GetLastError());
exit(-1);
}
*/
} FileTransfer::~FileTransfer()
{
WSACleanup();
} bool FileTransfer::setIpAndPort(string ip,int port)
{
m_IpAddr=ip;
m_Port=port;
m_ServerAddr.sin_family = AF_INET;
m_ServerAddr.sin_addr.s_addr = inet_addr(m_IpAddr.c_str());
m_ServerAddr.sin_port = htons(m_Port);
return true;
} bool FileTransfer::setFilePath(string path)
{
fstream testFile;
testFile.open(path,ios::in);
if(!testFile)
{
printf("file not exist!\n");
return false;
}
m_FilePath=path;
return true;
} bool FileTransfer::sendFile(string msg,string filePath)
{
m_FilePath=filePath;
m_ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if ( m_ServerSocket == INVALID_SOCKET )
{
printf("Create Socket Failed::%d\n", GetLastError());
return false;
}
if (connect(m_ServerSocket,(LPSOCKADDR)& m_ServerAddr,sizeof(m_ServerAddr))==SOCKET_ERROR)
{
printf("can not connect to server! NO: %d\n",GetLastError());
return false;
}
FILE * fp =fopen(m_FilePath.c_str(),"rb");
if (fp ==NULL)
{
printf("file open error!");
return false;
}
char buffer[BUFFER_SIZE];
//发送文件名(在卡口中用来发送识别出来的车牌号)
memset(buffer,,BUFFER_SIZE);
strncpy(buffer,msg.c_str(),msg.length());
if(send(m_ServerSocket,buffer,msg.length(),)<)
{
printf("seng msg fail!(the error num is : %d )\n",GetLastError());
return false;
}
//发送文件数据
memset(buffer,,sizeof(buffer));
int length = ;
while ((length = fread(buffer, sizeof(char), sizeof(buffer), fp)) > )
{
if (send(m_ServerSocket, buffer, length, )== SOCKET_ERROR)
{
printf("Send File: %s Failed\n", m_FilePath.c_str());
printf("error num : %d\n",GetLastError());
return false;
}
memset(buffer, , sizeof(buffer));
}
fclose(fp);
closesocket(m_ServerSocket);
return true;
}

客户端测试主函数

//main.cpp

/*************************************************************************
> File Name: Win_Server.c
> Author: SongLee
************************************************************************/ #include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "file_transfer.h"
using namespace std; #define PORT 8087
#define SERVER_IP "127.0.0.1"
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
#pragma comment(lib, "WS2_32") int main()
{
while()
{
bool flag;
string filename;
printf("input file name:");
cin>>filename;
FileTransfer ft;
ft.setIpAndPort(SERVER_IP,PORT);
ft.setFilePath(filename);
flag=ft.sendFile(filename,filename);
if (flag)
{
printf("send file %s success \n",filename.c_str());
}
else
{
printf("send file %d fail.The error code is : %d \n",GetLastError());
}
}
system("pause");
return ;
}

服务器测试代码

//main.cpp

/*************************************************************************
> 服务器测试代码
> 先从客户端接收一个字符串,作为文件的文件名,接着接收客户端发送过来的文
> 件并保存至本地
************************************************************************/ #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h> #define PORT 8087
#define SERVER_IP "127.0.0.1"
#define BUFFER_SIZE 1024
#pragma comment(lib, "WS2_32") int main()
{
// 声明并初始化一个服务端(本地)的地址结构
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT); // 初始化socket dll
WSADATA wsaData;
WORD socketVersion = MAKEWORD(, );
if(WSAStartup(socketVersion, &wsaData) != )
{
printf("Init socket dll error!");
exit();
} // 创建socket
SOCKET m_Socket = socket(AF_INET, SOCK_STREAM, );
if (SOCKET_ERROR == m_Socket)
{
printf("Create Socket Error!");
exit();
} //绑定socket和服务端(本地)地址
if (SOCKET_ERROR == bind(m_Socket, (LPSOCKADDR)&server_addr, sizeof(server_addr)))
{
printf("Server Bind Failed: %d", WSAGetLastError());
exit();
} //监听
if (SOCKET_ERROR == listen(m_Socket, ))
{
printf("Server Listen Failed: %d", WSAGetLastError());
exit();
} while (true)
{
printf("wait for file transfer...\n");
char file_name[BUFFER_SIZE];
char buffer[BUFFER_SIZE]; sockaddr_in client_addr;
int client_addr_len = sizeof(client_addr);
//首先接收发送过来的字符串
SOCKET m_New_Socket = accept( m_Socket, (sockaddr *)&client_addr, &client_addr_len);
if (SOCKET_ERROR == m_New_Socket)
{
printf("Server Accept Failed: %d", WSAGetLastError());
break;
}
memset(buffer,,sizeof(buffer));
memset(file_name,,sizeof(file_name));
if (recv(m_New_Socket,buffer,sizeof(buffer),)<)
{
printf("recv file name fail!\n");
continue;
}
strncpy(file_name,buffer,strlen(buffer));
printf("recv file name : %s \n",file_name);
FILE * fp = fopen(file_name,"wb");
if (fp==NULL)
{
printf("open file error\n");
continue;
}
//获取字符串后继续获取文件数据
memset(buffer, , BUFFER_SIZE);
int length = ;
while ((length = recv(m_New_Socket, buffer, BUFFER_SIZE, )) > )
{
if (fwrite(buffer, sizeof(char), length, fp) < length)
{
printf("File: %s Write Failed\n", file_name);
break;
}
memset(buffer, , BUFFER_SIZE);
}
fclose(fp);
printf("file transfer success!\n");
}
system("pause");
return ;
}

  在进行编程的过程中,自己有以下几个问题没弄懂:

  (1)connect函数函数是怎么知道连接请求是否产生错误了?

  (2)当一次connect()连接成功后,如何主动关闭这个socket连接,直接调用closesocket函数就可以了吗?

上一篇:软件工程:为数十亿用户设计架构


下一篇:http协议中302和303的区别