赞
踩
在《Qt实现客户端与服务器消息发送与文件传输》一文里Jungle用Qt和Qt封装的类实现了客户端与服务器之间的消息发送和文件传输。本文Jungle尝试用Windows编程实现客户端与服务器之间的文件传输。不过本文仅仅简单介绍如何实现socket套接字传输。不足之处请各位CSDNer指出。
借用百度百科:“WinSock2是连接系统和用户使用的软件之间用于交流的一个接口。Winsock2 SPI(Service Provider Interface)服务提供者接口建立在Windows开放系统架构WOSA(Windows Open System Architecture)之上,是Winsock系统组件提供的面向系统底层的编程接口。Winsock系统组件向上面向用户应用程序提供一个标准的API接口;向下在Winsock组件和Winsock服务提供者(比如TCP/IP协议栈)之间提供一个标准的SPI接口。”
简而言之,这是Windows提供的一个编程接口,用户可通过该接口访问系统底层。WinSock2.h头文件里包含了很多接口的定义,比如本小节要用到的socket:
#if INCL_WINSOCK_API_PROTOTYPES WINSOCK_API_LINKAGE SOCKET WSAAPI socket( IN int af, IN int type, IN int protocol ); #endif /* INCL_WINSOCK_API_PROTOTYPES */ typedef struct WSAData { WORD wVersion;//Windows Sockets DLL期望调用者使用的Windows Sockets规范的版本 WORD wHighVersion;//这个DLL能够支持的Windows Sockets规范的最高版本 #ifdef _WIN64 unsigned short iMaxSockets; unsigned short iMaxUdpDg; char FAR * lpVendorInfo; char szDescription[WSADESCRIPTION_LEN+1];//以null结尾的ASCII字符串,Windows Sockets DLL将对Windows Sockets实现的描述拷贝到这个字符串中,包括制造商标识 char szSystemStatus[WSASYS_STATUS_LEN+1]; #else char szDescription[WSADESCRIPTION_LEN+1]; char szSystemStatus[WSASYS_STATUS_LEN+1];//以null结尾的ASCII字符串,Windows Sockets DLL把有关的状态或配置信息拷贝到该字符串中 unsigned short iMaxSockets;//单个进程能够打开的socket的最大数目 unsigned short iMaxUdpDg;//Windows Sockets应用程序能够发送或接收的最大的用户数据包协议(UDP)的数据包大小,以字节为单位 char FAR * lpVendorInfo;//指向销售商的数据结构的指针 #endif } WSADATA, FAR * LPWSADATA;
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <WinSock2.h> #define PORT 8001 ///端口号 #define SERVER_IPADRESS "127.0.0.1" // 服务器IP地址,这里设为本机地址 #define BUFFER_SIZE 1024 #define FILE_NAME_MAX_SIZE 512 #pragma comment(lib, "WS2_32") #include <stdio.h> #include <stdlib.h> #include <string.h> #include <WinSock2.h> #define PORT 8001 #define SERVER_IPADRESS "127.0.0.1" // 192.168.1.6 #define BUFFER_SIZE 1024 #define FILE_NAME_MAX_SIZE 512 #pragma comment(lib, "WS2_32") int main() { // 初始化socket dll WSADATA wsaData; // MAKEWORD // 原型:#define MAKEWORD(a, b) ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8)) // 作用:将两个byte型的a和b合并为一个word型,高8为是b,低8位是a; // 返回值:一个无符号的16位整形 WORD socketVersion = MAKEWORD(2, 0); if(WSAStartup(socketVersion, &wsaData) != 0) { printf("Init socket dll error!"); exit(1); } // 创建socket // AF_INET: IPv4 网络协议的套接字类型; // SOCK_STREAM:提供面向连接的稳定数据传输,即TCP协议; SOCKET c_Socket = socket(AF_INET, SOCK_STREAM, 0); if (SOCKET_ERROR == c_Socket) { printf("Create Socket Error!"); system("pause"); exit(1); } // 指定服务端的地址 sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.S_un.S_addr = inet_addr(SERVER_IPADRESS); server_addr.sin_port = htons(PORT); // 建立连接 if (SOCKET_ERROR == connect(c_Socket, (LPSOCKADDR)&server_addr, sizeof(server_addr))) { printf("Can Not Connect To Server IP!\n"); system("pause"); exit(1); } // 输入文件名 char file_name[FILE_NAME_MAX_SIZE+1]; memset(file_name, 0, FILE_NAME_MAX_SIZE+1); printf("Please Input File Name On Server: "); scanf("%s", &file_name); char buffer[BUFFER_SIZE]; memset(buffer, 0, BUFFER_SIZE); strncpy(buffer, file_name, strlen(file_name)>BUFFER_SIZE ? BUFFER_SIZE:strlen(file_name)); // 向服务器发送文件名 if(send(c_Socket, buffer, BUFFER_SIZE, 0) < 0) { printf("Send File Name Failed\n"); system("pause"); exit(1); } // 打开文件,准备写入 FILE * fp = fopen(file_name, "wb"); //windows下是"wb",表示打开一个只写的二进制文件 if(NULL == fp) { printf("File: %s Can Not Open To Write\n", file_name); system("pause"); exit(1); } else { // memset函数: // 原型:memset(void *s,int ch,size_t n); // 说明:将s所指向的某一块内存中的后n个字节的内容全部设置为ch指定的ASCII值 memset(buffer, 0, BUFFER_SIZE); int length = 0; while ((length = recv(c_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); } printf("Receive File: %s From Server Successful!\n", file_name); } fclose(fp); closesocket(c_Socket); //释放winsock库 WSACleanup(); system("pause"); return 0; }
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <WinSock2.h> #define PORT 8001 #define SERVER_IPADRESSADRESS "127.0.0.1" #define BUFFER_SIZE 1024 #define FILE_NAME_MAX_SIZE 512 #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(1) { printf("Listening To Client...\n"); 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; } char buffer[BUFFER_SIZE]; memset(buffer, 0, BUFFER_SIZE); if (recv(m_New_Socket, buffer, BUFFER_SIZE, 0) < 0) { printf("Server Receive Data Failed!"); break; } char file_name[FILE_NAME_MAX_SIZE+1]; memset(file_name, 0, FILE_NAME_MAX_SIZE+1); strncpy(file_name, buffer, strlen(buffer)>FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE:strlen(buffer)); printf("%s\n", file_name); FILE * fp = fopen(file_name, "rb"); // windows下是"rb",表示打开一个只读的二进制文件 if (NULL == fp) { printf("File: %s Not Found\n", file_name); } else { memset(buffer, 0, BUFFER_SIZE); int length = 0; while ((length = fread(buffer, sizeof(char), BUFFER_SIZE, fp)) > 0) { if (send(m_New_Socket, buffer, length, 0) < 0) { printf("Send File: %s Failed\n", file_name); break; } memset(buffer, 0, BUFFER_SIZE); } fclose(fp); printf("File: %s Transfer Successful!\n", file_name); } closesocket(m_New_Socket); } closesocket(m_Socket); // 释放winsock库 WSACleanup(); return 0; }
先运行服务端程序,在运行客户端程序。在客户端控制台输入服务端工程文件夹下的文件名字,回车键,该文件传输到了客户端工程文件夹下。
按下回车键后,可以看到,客户端工程文件夹下接收到了服务端传送的文件:
欢迎关注知乎专栏:Jungle是一个用Qt的工业Robot
欢迎关注Jungle的微信公众号:Jungle笔记
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。