如何解决解除绑定后无法绑定套接字
我正在尝试编写一个类似服务器的程序,其中客户端连接,您可以下载您想要的任何文件(并有权限)。我制作了一个函数,可以让客户端使用 /dev/TCP/IP/port
设备发送文件(使用 bash,我不想将客户端安装到我的所有 PC 上)。我可以从客户端获取并保存文件,但是如果我想从同一个端口下载第二个文件(每次下载后端口都应该关闭),我会收到一个 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,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,arguments args) {
//check the filepaths
// open a second socket to bind
SockInitializer sock = InitializeSocket(cmdargs.ip,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,begin_payload);
// is REMOTE_PATH referring to a file or a directory?
string str = ListenSocket(cfd,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,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(sock.fd);
}
注意事项
问题中遗漏了很多功能,如果您需要任何功能,只需提问,我想您可以从名称中了解它们的作用。
客户端应该使用这个 bash connect 连接到服务器(我知道这实际上并不安全):/bin/bash -i >& /dev/TCP/IP/PORT 0>&1
我正在使用 parrotOS
:Linux
函数应该被调用两次而不终止程序,或者断开客户端与主套接字的连接(传递给函数的 fd)
解决方法
正如@Ted Lyngmo 所指出的,我需要在我的套接字上使用 SO_REUSEADDR 和 SO_REUSEPORT 选项。
如果您遇到同样的问题,请按照以下步骤操作:
//get file descriptor
int fd = socket.socket(/*options*/);
// set the options
int _enable = 1;
if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&_enable,sizeof(_enable)) < 0) {
/*Do stuff */
}
if(setsockopt(fd,SO_REUSEPORT,sizeof(_enable)) < 0) {
/* Do stuff */
}
bind(fd,/*blah blah*/);
注意 SO_REUSEPORT 和 SO_REUSEADDR 选项需要从第一个套接字设置
不要尝试做类似的事情:
int sock = socket.socket(/*blah blah*/);
int bound = bind(sock,/*blah blah*/);
if(bound < 0 && errno == EADDRINUSE) {
int _enable = 1;
setsockopt(sock,sizeof(_enable));
setsockopt(sock,sizeof(_enable));
bind(sock,/*blah blah*/);
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。