Windows网络编程:Winsock实现客户端与服务器文件传输(TCP/IP)

在《Qt实现客户端与服务器消息发送与文件传输》一文里Jungle用Qt和Qt封装的类实现了客户端与服务器之间的消息发送和文件传输。本文Jungle尝试用Windows编程实现客户端与服务器之间的文件传输。不过本文仅仅简单介绍如何实现socket套接字传输。不足之处请各位CSDNer指出。

1.Winsock2

借用百度百科:“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;

2.客户端程序

#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;  
}  

3.服务端程序

#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;  
}  

4.测试

先运行服务端程序,在运行客户端程序。在客户端控制台输入服务端工程文件夹下的文件名字,回车键,该文件传输到了客户端工程文件夹下。
这里写图片描述
按下回车键后,可以看到,客户端工程文件夹下接收到了服务端传送的文件:
这里写图片描述


欢迎关注知乎专栏:Jungle是一个用Qt的工业Robot
欢迎关注Jungle的微信公众号:Jungle笔记
在这里插入图片描述

  • 9
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冯Jungle

您的支持是对我最大的鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值