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