当前位置:   article > 正文

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

windows 套接字发送文件

在《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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

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;  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117

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;  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109

4.测试

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


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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/147053
推荐阅读
相关标签
  

闽ICP备14008679号