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