栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

关于如何用C++ Socket替代FTP工具这回小事

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

关于如何用C++ Socket替代FTP工具这回小事

C++ Socket返回文件内容

兄弟萌用虚拟机装上新系统或者租用云服务器之后,总想要能在PC和服务器之间传些东西。


一般说来,FilezillaFTP工具就很好用了(甚至支持拖拽式上传下载)。但是,能写写代码当然更有意思。本文介绍Windows环境下的实现方式(Linux环境代码上的区别很小,文中会指出),提供全部的代码。

一、概览菜单

socket这玩意儿,就是服务端监听某个端口,根据客户端的请求返回对应的内容。这样说来,服务端需要解析客户端的请求,那就涉及到请求格式和格式解析,很麻烦嘛。不过如果我们固定返回某句话,或者某个文件,这样就简单了。而且有了完整的应答接口,后续再加上解析也是可以的。

经过简化,这个看似工程量很大的计划,就转化成实现下面三个函数啦。


1 SOCKET Socket_SetupSever(const std::string& address, const unsigned short& port);
2 std::string Socket_ClientGet(const std::string& address, const unsigned short& port);
3 std::string Socket_Socket_ReadInfo(const std::string& infopath, char mod);

三个函数按顺序依次是1 在服务器建立服务、2在客户端向服务端发起请求、3获取准备向客户端返回的文件内容。


那服务端就可以用下面的代码建立服务


服务端
    
	SOCKET servSock = Socket_SetupSever("127.0.0.1",1234);
	
	SOCKADDR clntAddr;
	int nSize = sizeof(SOCKADDR);
	
	while (true) {
		SOCKET clntSock = accept(servSock, (SOCKADDR*)& clntAddr, &nSize);
		if (clntSock != 0) {
			
			
			std::string info = Socket_Socket_ReadInfo("info.txt",'f');
			send(clntSock, info.c_str(), info.size(), NULL);
			//关闭此位client的socket
			closesocket(clntSock);
		}
	}
	closesocket(servSock);

首先用Socket_SetupSever获取配置好的socket,这里在本地做实验,IP配置为127.0.0.1,端口号为1234,你也可以选其他可用的端口。
然后进入While循环中,用accpe()t持续监听是否有client访问本地的1234端口。
如果有客户成功连接(if (clntSock != 0)),那就用Socket_Socket_ReadInfo()获取准备返回的内容,用send()发回客户端。


客户端

客户端只需要用Socket_ClientGet()发起请求就行啦,这里把返回的内容输出到标准输出。

std::cout << Socket_ClientGet("127.0.0.1", 1234) << endl;

二、细品代码

话不多说,上菜。


第一个是建立端口监听服务的Socket_SetupSever,以主机IP和开放端口为参数,返回配置好的Socket。


SOCKET Socket_SetupSever(const std::string& address, const unsigned short& port)
{
	//创建套接字
	SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	//绑定套接字
	sockaddr_in sockAddr;
	memset(&sockAddr, 0, sizeof(sockAddr));  //每个字节都用0填充
	sockAddr.sin_family = PF_INET;  //使用IPv4地址
	sockAddr.sin_addr.s_addr = inet_addr(address.c_str());  //具体的IP地址
	sockAddr.sin_port = htons(port);  //端口
	bind(servSock, (SOCKADDR*)& sockAddr, sizeof(SOCKADDR));
	//进入监听状态
	listen(servSock, 20);

	return servSock;
}

第二个是在客户端发起请求的Socket_ClientGet()


std::string Socket_ClientGet(const std::string& address, const unsigned short& port) {
	
	SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	
	sockaddr_in sockAddr;
	memset(&sockAddr, 0, sizeof(sockAddr));  //每个字节都用0填充
	sockAddr.sin_family = PF_INET;
	sockAddr.sin_addr.s_addr = inet_addr(address.c_str());
	sockAddr.sin_port = htons(port);
	
	connect(sock, (SOCKADDR*)& sockAddr, sizeof(SOCKADDR));
	
	char szBuffer[MAXINT16] = { 0 };
	recv(sock, szBuffer, MAXINT16, NULL);
	
	closesocket(sock);
	return std::string(szBuffer);
}

第三个是获取返回信息的Socket_Socket_ReadInfo()

std::string Socket_Socket_ReadInfo(const std::string& infopath,char mod='s')
{
	if (mod == 's') {
		return infopath;
	}else {
		std::ifstream f(infopath);
		std::string text, te;
		while (getline(f, te)) {
			text.append(te);
		}
		return text;
	}
}

注意第一部分展示的服务端代码中有一句

std::string info = Socket_Socket_ReadInfo("info.txt",'f');

就是返回文本文件info.txt的内容。而如果写成

std::string info = Socket_Socket_ReadInfo("info.txt");

返回的就是值为"info.txt"的string类型对象。


三、可用代码

把上面介绍的三个函数放到头文件"socketpocket.h"中。


socketpocket.h
#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include 
#include 
#include 
#include 

#pragma comment (lib, "ws2_32.lib")  //加载 ws2_32.dll

SOCKET Socket_SetupSever(const std::string& address, const unsigned short& port);
std::string Socket_Socket_ReadInfo(const std::string& infopath, char mod);
std::string Socket_ClientGet(const std::string& address, const unsigned short& port);

服务端
#include 
#include "socketpocket.h"
using namespace std;
int main() {
	//初始化 DLL
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);
	
	SOCKET servSock = Socket_SetupSever("127.0.0.1",1234);
	
	SOCKADDR clntAddr;
	int nSize = sizeof(SOCKADDR);
	
	while (true) {
		SOCKET clntSock = accept(servSock, (SOCKADDR*)& clntAddr, &nSize);
		if (clntSock != 0) {
			
			
			std::string info = Socket_Socket_ReadInfo("info.txt",'f');
			send(clntSock, info.c_str(), info.size(), NULL);
			//关闭此位client的socket
			closesocket(clntSock);
		}
	}
	closesocket(servSock);
	//终止 DLL 的使用
	WSACleanup();
	return 0;
}

客户端
#include 
#include "socketpocket.h"
int main() {
	
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);	      
	           
	std::cout << Socket_HttpGet("127.0.0.1", 1234) << endl;
	
	WSACleanup();
	system("pause");
	return 0;
}
四 区别和注意 区别

Windows 下的 socket 程序和 Linux 思路相同,但细节有所差别:
Windows 下的 socket 程序依赖 Winsock.dllws2_32.dll,必须提前加载。DLL 有两种加载方式,请查看:动态链接库DLL的加载

Linux 使用“文件描述符”的概念,而 Windows 使用“文件句柄”的概念;Linux 不区分 socket 文件和普通文件,而 Windows 区分;Linux 下 socket() 函数的返回值为 int 类型,而 Windows 下为 SOCKET 类型,也就是句柄。

Linux 下使用 read() / write() 函数读写,而 Windows 下使用 recv() / send() 函数发送和接收。

关闭 socket 时,Linux 使用 close() 函数,而 Windows 使用 closesocket() 函数。

注意

测试示例代码先运行服务器端,再运行客户端,文件路径用绝对路径,要保证对应的文件存在并且可读。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/333442.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号