我正在展示服务器和客户端的代码。平台是Linux(Fedora)。我遇到的问题是,客户端在一段时间后在 connect() 调用上超时并失败。然而,更神秘的是 getaddrinfo() 的作用。它用于 connect() 的地址与我传递给结构提示的地址不同。为什么会这样?
为什么程序客户端无法连接?
Server.cpp
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include<arpa/inet.h>
#include <sys/un.h>
#include<unistd.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
int main(int argc, char *argv[]) {
if (argc != 2) {
cout << "Usage: " << argv[0] << " port-number" << endl;
exit(1);
}
else cout << "Server(), incoming port: " << argv[1] << endl;
int sockDescriptor, reqSocket, childpid;
struct addrinfo hints, *result, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
char buf[126];
if (gethostname(buf, sizeof(buf)) < 0){
cout << "Server(), gethostname() failed." << endl;
exit(1);
}
else cout << "Server(), Hostname: " << buf << endl;
if (getaddrinfo(buf, argv[1], &hints, &result) != 0) {
cout << "Server(), getaddrinfo() failed" << endl;
exit(1);
}
struct addrinfo sa, sa4;
memset(&sa, 0, sizeof(sa));
memset(&sa4, 0, sizeof(sa4));
bool AllDone = false;
for (p = result; p != NULL; p = p->ai_next) {
if (p->ai_family == AF_INET6) {
if (!AllDone) {
memcpy(&sa, p, sizeof(sa));
sockDescriptor = socket(sa.ai_family, sa.ai_socktype, sa.ai_protocol);
if (sockDescriptor == -1) continue;
if (bind(sockDescriptor, sa.ai_addr, sa.ai_addrlen) == -1) {
close(sockDescriptor);
}
else {
AllDone = true;
inet_ntop(sa.ai_family, &(sa.ai_addr), buf, 125);
cout << "Server(), Server Address: " << buf << endl;
}
} //endif AllDone
}
else if (p->ai_family == AF_INET) {
memcpy(&sa4, p, sizeof(sa4));
inet_ntop(sa4.ai_family, &(sa4.ai_addr), buf, 125);
cout << "Server(), Server Address: " << buf << endl;
}
}
freeaddrinfo(result);
if (!AllDone) {
cout << "Server(), getaddrinfo() did not get address..." << endl;
exit(1);
}
else cout << "Server(), getaddrinfo() succeeded..." << endl;
if (listen(sockDescriptor, 5) == -1) {
cout << "Server(), listen() failed" << endl;
close(sockDescriptor);
exit(1);
}
for(;;) {
if ((reqSocket = accept(sockDescriptor, NULL, NULL)) == -1) {
cout << "Server(), accept() failed, continuing" << endl;
continue;
}
if ((childpid = fork()) < 0) {
cout << "Server(), Could not fork for accept. Quitting" << endl;
close(sockDescriptor);
exit(1);
}
else if (!childpid) {
cout << "Server(), accepted client request..." << endl;
close(reqSocket);
exit(0);
}
}
return 0;
}
Client.cpp
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include<arpa/inet.h>
#include <sys/un.h>
#include<unistd.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
int main(int argc, char *argv[]) {
if (argc != 3) {
cout << "Usage: " << argv[0] << " host-address" << " port-number" << endl;
exit(1);
}
else {
cout << "Client(), incoming address: " << argv[1] << endl;
cout << "Client(), incoming port: " << argv[2] << endl;
}
int sockDescriptor;
struct addrinfo hints, *result, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if (getaddrinfo(argv[1], argv[2], &hints, &result) != 0) {
cout << "Client(), getaddrinfo() failed" << endl;
exit(1);
}
struct addrinfo sa;
memset(&sa, 0, sizeof(sa));
bool AllDone = false;
for (p = result; p != NULL; p = p->ai_next) {
if (p->ai_family == AF_INET6) {
if (!AllDone) {
memcpy(&sa, p, sizeof(sa));
sockDescriptor = socket(sa.ai_family, sa.ai_socktype, sa.ai_protocol);
if (sockDescriptor == -1) continue;
if (connect(sockDescriptor, sa.ai_addr, sa.ai_addrlen) == -1) {
close(sockDescriptor);
char buf[126];
inet_ntop(sa.ai_family, &(sa.ai_addr), buf, 125);
cout << "Client(), connect() failed for address: " << buf << endl;
}
else {
AllDone = true;
break;
}
} //endif AllDone
}
}
freeaddrinfo(result);
if (!AllDone) {
cerr << "Client(), getaddrinfo() did not get address..." << endl;
exit(1);
}
else {
cout << "Client(), connection to server succeeded." << endl;
close(sockDescriptor);
}
return 0;
}
问题最终解决如下。使用节点的主机名,不要使用 NULL。丢弃包含 0000 的地址。我将只显示针对服务器修改的主循环。
struct addrinfo sa;
memset(&sa, 0, sizeof(sa));
bool AllDone = false;
for (p = result; p != NULL; p = p->ai_next) {
if (p->ai_family == AF_INET6) {
struct sockaddr_in6 *psa = (struct sockaddr_in6*)p->ai_addr;
inet_ntop(AF_INET6, &(psa->sin6_addr), buf, 125);
if (strstr(buf, "::")) {
cout << "Server(), Server ipv6 Address (skipped): " << buf << endl;
continue;
}
if (!AllDone) {
memcpy(&sa, p, sizeof(sa));
sockDescriptor = socket(sa.ai_family, sa.ai_socktype, sa.ai_protocol);
if (sockDescriptor == -1) continue;
if (bind(sockDescriptor, sa.ai_addr, sa.ai_addrlen) == -1) {
close(sockDescriptor);
}
else {
AllDone = true;
cout << "Server(), bind() succeeded for address: " << buf << endl;
}
} //endif AllDone
}
else if (p->ai_family == AF_INET) {
struct sockaddr_in *psa = (struct sockaddr_in*)p->ai_addr;
inet_ntop(AF_INET, &(psa->sin_addr), buf, 125);
cout << "Server(), Server ipv4 Address: " << buf << endl;
}
}
freeaddrinfo(result);