C++ 套接字:客户端调用 connect() 时 accept() 挂起,但 accept() 响应 HTTP GET 请求
C++ sockets: accept() hangs when client calls connect(), but accept() responds to HTTP GET request
我正在尝试用 C++ 编写演示 server/client 程序。我首先在我的 Macbook 上 运行 我的服务器程序,使用 ngrok 并将 Internet 上的 public 地址转发到我机器上的本地地址。当我尝试 运行 我的客户端程序连接到服务器时,我看到了一些我不明白的东西:
- 如果客户端尝试在服务器程序中定义的本地端口连接到本地主机,服务器按预期接受并且客户端成功连接,
- 如果客户端尝试在端口 80(ngrok 的默认值)连接到 ngrok 服务器地址,则客户端会连接,但服务器仍会在接受调用时被阻止。 (这个我不懂!)
- 如果我向ngrok服务器地址发送HTTP GET请求,服务器成功接受连接。
为什么我会看到这些?在理想情况下,我希望我的服务器接受来自我的客户端程序的连接,而不仅仅是响应 HTTP GET 请求。
如果有帮助,这是我的代码:对于客户,
#include "helpers.hh"
#include <cstdio>
#include <netdb.h>
// usage: -h [host] -p [port]
int main(int argc, char** argv) {
const char* host = "x.xx.xx.xx"; // use the server's ip here.
const char* port = "80";
// parse arguments
int opt;
while ((opt = getopt(argc, argv, "h:p:")) >= 0) {
if (opt == 'h') {
host = optarg;
} else if (opt == 'p') {
port = optarg;
}
}
// look up host and port
struct addrinfo hints, *ais;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // use TCP
hints.ai_flags = AI_NUMERICSERV;
if (strcmp(host, "ngrok") == 0) {
host = "xxxx-xxxx-xxxx-1011-2006-00-27b9.ngrok.io";
}
int r = getaddrinfo(host, port, &hints, &ais);
if (r != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(r));
exit(1);
}
// connect to server
int fd = -1;
for (auto ai = ais; ai && fd < 0; ai = ai->ai_next) {
fd = socket(ai->ai_family, ai->ai_socktype, 0);
if (fd < 0) {
perror("socket");
exit(1);
}
r = connect(fd, ai->ai_addr, ai->ai_addrlen);
if (r < 0) {
close(fd);
fd = -1;
}
}
if (fd < 0) {
perror("connect");
exit(1);
}
freeaddrinfo(ais);
//
printf("Connection established at fd %d\n", fd);
FILE* f = fdopen(fd, "a+");
fwrite("!", 1, 1, f);
fclose(f);
while (true) {
}
}
对于服务器:
#include "helpers.hh"
void handle_connection(int cfd, std::string remote) {
(void) remote;
printf("Received incoming connection at cfd: %d\n", cfd);
usleep(1000000);
printf("Exiting\n");
}
int main(int argc, char** argv) {
int port = 6162;
if (argc >= 2) {
port = strtol(argv[1], nullptr, 0);
assert(port > 0 && port <= 65535);
}
// Prepare listening socket
int fd = open_listen_socket(port);
assert(fd >= 0);
fprintf(stderr, "Listening on port %d...\n", port);
while (true) {
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
// Accept connection on listening socket
int cfd = accept(fd, &addr, &addrlen);
if (cfd < 0) {
perror("accept");
exit(1);
}
// Handle connection
handle_connection(cfd, unparse_sockaddr(&addr, addrlen));
}
}
与在本地路由器中完成的典型端口转发相反,ngrok 不是传输级别 (TCP) 的端口转发器,而是 HTTP 级别的请求转发器。
因此,如果客户端执行 TCP 连接到外部 ngrok 服务器,则不会转发任何内容。只有在客户端发送 HTTP 请求后,才会确定目的地,然后将此请求发送到内部机器上的 ngrok 连接器,然后它会启动与内部服务器的连接并转发请求。
我正在尝试用 C++ 编写演示 server/client 程序。我首先在我的 Macbook 上 运行 我的服务器程序,使用 ngrok 并将 Internet 上的 public 地址转发到我机器上的本地地址。当我尝试 运行 我的客户端程序连接到服务器时,我看到了一些我不明白的东西:
- 如果客户端尝试在服务器程序中定义的本地端口连接到本地主机,服务器按预期接受并且客户端成功连接,
- 如果客户端尝试在端口 80(ngrok 的默认值)连接到 ngrok 服务器地址,则客户端会连接,但服务器仍会在接受调用时被阻止。 (这个我不懂!)
- 如果我向ngrok服务器地址发送HTTP GET请求,服务器成功接受连接。
为什么我会看到这些?在理想情况下,我希望我的服务器接受来自我的客户端程序的连接,而不仅仅是响应 HTTP GET 请求。
如果有帮助,这是我的代码:对于客户,
#include "helpers.hh"
#include <cstdio>
#include <netdb.h>
// usage: -h [host] -p [port]
int main(int argc, char** argv) {
const char* host = "x.xx.xx.xx"; // use the server's ip here.
const char* port = "80";
// parse arguments
int opt;
while ((opt = getopt(argc, argv, "h:p:")) >= 0) {
if (opt == 'h') {
host = optarg;
} else if (opt == 'p') {
port = optarg;
}
}
// look up host and port
struct addrinfo hints, *ais;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // use TCP
hints.ai_flags = AI_NUMERICSERV;
if (strcmp(host, "ngrok") == 0) {
host = "xxxx-xxxx-xxxx-1011-2006-00-27b9.ngrok.io";
}
int r = getaddrinfo(host, port, &hints, &ais);
if (r != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(r));
exit(1);
}
// connect to server
int fd = -1;
for (auto ai = ais; ai && fd < 0; ai = ai->ai_next) {
fd = socket(ai->ai_family, ai->ai_socktype, 0);
if (fd < 0) {
perror("socket");
exit(1);
}
r = connect(fd, ai->ai_addr, ai->ai_addrlen);
if (r < 0) {
close(fd);
fd = -1;
}
}
if (fd < 0) {
perror("connect");
exit(1);
}
freeaddrinfo(ais);
//
printf("Connection established at fd %d\n", fd);
FILE* f = fdopen(fd, "a+");
fwrite("!", 1, 1, f);
fclose(f);
while (true) {
}
}
对于服务器:
#include "helpers.hh"
void handle_connection(int cfd, std::string remote) {
(void) remote;
printf("Received incoming connection at cfd: %d\n", cfd);
usleep(1000000);
printf("Exiting\n");
}
int main(int argc, char** argv) {
int port = 6162;
if (argc >= 2) {
port = strtol(argv[1], nullptr, 0);
assert(port > 0 && port <= 65535);
}
// Prepare listening socket
int fd = open_listen_socket(port);
assert(fd >= 0);
fprintf(stderr, "Listening on port %d...\n", port);
while (true) {
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
// Accept connection on listening socket
int cfd = accept(fd, &addr, &addrlen);
if (cfd < 0) {
perror("accept");
exit(1);
}
// Handle connection
handle_connection(cfd, unparse_sockaddr(&addr, addrlen));
}
}
与在本地路由器中完成的典型端口转发相反,ngrok 不是传输级别 (TCP) 的端口转发器,而是 HTTP 级别的请求转发器。
因此,如果客户端执行 TCP 连接到外部 ngrok 服务器,则不会转发任何内容。只有在客户端发送 HTTP 请求后,才会确定目的地,然后将此请求发送到内部机器上的 ngrok 连接器,然后它会启动与内部服务器的连接并转发请求。