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

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

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

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

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

Windows下通过socket进行字符串和文件传输_第1张图片

 

  服务器基本流程图如下:

Windows下通过socket进行字符串和文件传输_第2张图片

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

  客户端源代码如下:

//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=1024;

    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=-1;
    /*
    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,0,BUFFER_SIZE);
    strncpy(buffer,msg.c_str(),msg.length());
    if(send(m_ServerSocket,buffer,msg.length(),0)<0)
    {
        printf("seng msg fail!(the error num is : %d )\n",GetLastError());
        return false;
    }
    //发送文件数据
    memset(buffer,0,sizeof(buffer));
    int length = 0; 
    while ((length = fread(buffer, sizeof(char), sizeof(buffer), fp)) > 0) 
    { 
        if (send(m_ServerSocket, buffer, length, 0)== SOCKET_ERROR) 
        { 
            printf("Send File: %s Failed\n", m_FilePath.c_str()); 
            printf("error num : %d\n",GetLastError());
            return false;
        } 
        memset(buffer, 0, 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(1)
    {
        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 0; 
} 

 

 

服务器测试代码

//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(2, 0); 
    if(WSAStartup(socketVersion, &wsaData) != 0) 
    { 
        printf("Init socket dll error!"); 
        exit(1); 
    } 

    // 创建socket 
    SOCKET m_Socket = socket(AF_INET, SOCK_STREAM, 0); 
    if (SOCKET_ERROR == m_Socket) 
    { 
        printf("Create Socket Error!"); 
        exit(1); 
    } 

    //绑定socket和服务端(本地)地址 
    if (SOCKET_ERROR == bind(m_Socket, (LPSOCKADDR)&server_addr, sizeof(server_addr))) 
    { 
        printf("Server Bind Failed: %d", WSAGetLastError()); 
        exit(1); 
    } 

    //监听 
    if (SOCKET_ERROR == listen(m_Socket, 10)) 
    { 
        printf("Server Listen Failed: %d", WSAGetLastError()); 
        exit(1); 
    } 
    
    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,0,sizeof(buffer));
        memset(file_name,0,sizeof(file_name));
        if (recv(m_New_Socket,buffer,sizeof(buffer),0)<0)
        {
            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, 0, BUFFER_SIZE); 
        int length = 0; 
        while ((length = recv(m_New_Socket, buffer, BUFFER_SIZE, 0)) > 0) 
        { 
            if (fwrite(buffer, sizeof(char), length, fp) < length) 
            { 
                printf("File: %s Write Failed\n", file_name); 
                break; 
            } 
            memset(buffer, 0, BUFFER_SIZE); 
        }
        fclose(fp);
        printf("file transfer success!\n"); 
    }
    system("pause");
    return 0; 
} 

 

 

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

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

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

 

你可能感兴趣的:(Windows下通过socket进行字符串和文件传输)