Windows下通過socket進行字符串和文件傳輸


  今天在windows平台下,通過socket實現了簡單的文件傳輸。通過實現這一功能,了解基本的windows網絡編程和相關函數的使用方法。

  在windows平台上進行網絡編程,首先都需要調用函數WSAStartup()進行鏈接庫的初始化。如果沒有使用該函數進行初始化,則后面會出現10093的錯誤(可以通過GetLastError()獲得錯誤碼)。

  進行初始化后,客戶端和服務器進行不同的工作。但是不管是服務器還是客戶端,都需要用到兩個最基本的結構體,分別是SOCKET和sockaddr_in。其中,SOCKET結構體用於表示一個socket連接。sockaddr_in表示一個地址結構,用它來保存需要連接的主機ip地址及端口號,或者是需要監聽的端口號。

  客戶端基本流程圖如下圖:

 

  服務器基本流程圖如下:

   這次實現的功能主要是:客戶端向服務器發送一個字符串和一個文件(圖片文件),服務器以接收到的字符串為文件名,並將接收到的文件保存到本地。

  客戶端源代碼如下:

//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函數就可以了嗎?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM