我正在尝试学习 C 网络编程,我目前正在使用 echo 服务器,该服务器发送回它收到的任何字符串,我有以下代码:
您可以跳过整个代码部分来解决实际问题
服务器.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#define PORT "8080"
#define BUFFLEN 1024
char *strin(FILE *stream);
void printaddrs(struct addrinfo *res);
int main() {
struct addrinfo hints, *res;
// sockfd -> server file descriptor (serverfd)
int sockfd, clientfd;
char buffer[BUFFLEN];
memset(&hints, 0, sizeof(hints)); // Make sure 'hints' is empty
// Fill in the 'hints' addrinfo structure
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 (IPv4 or IPv6)
hints.ai_socktype = SOCK_STREAM; // TCP
hints.ai_flags = AI_PASSIVE; // Use my address
printf("'hints' filled successfully!\n");
if (getaddrinfo(NULL, PORT, &hints, &res) != 0) {
printf("getaddrinfo(): Error!\n");
}
printf("getaddrinfo(): Success!\n");
// print all addresses returned by getaddrinfo()
printaddrs(res);
printf("Select an address to bind to: ");
// strin -> takes input from FILE *stream no matter how long it is!!!
char *id = strin(stdin);
if (id == NULL) {
printf("No input!\n");
freeaddrinfo(res);
free(id);
return 1;
}
struct sockaddr_in *ipv4;
struct sockaddr_in6 *ipv6;
char addrstr[INET6_ADDRSTRLEN];
struct addrinfo *p;
int count = 0, type, family;
char ipstr[INET6_ADDRSTRLEN], ipver[5];
for (; res != NULL; res = res->ai_next) {
if (atoi(id) == count) {
if (res->ai_family == AF_INET) { // IPv4
ipv4 = (struct sockaddr_in *) res->ai_addr;
inet_ntop(res->ai_family, ipv4, addrstr, sizeof(addrstr));
} else if (res->ai_family == AF_INET6) { // IPv6
ipv6 = (struct sockaddr_in6 *) res->ai_addr;
inet_ntop(res->ai_family, ipv6, addrstr, sizeof(addrstr));
}
printf("Chosen address: %s\n", addrstr);
break;
}
strcpy(addrstr, "");
count++;
}
free(id);
if (strcmp(addrstr, "") == 0) {
printf("Couldn't find entered id.\n");
freeaddrinfo(res);
return 1;
}
// Create the socket
sockfd = socket(res->ai_family, res->ai_socktype, 0);
if (sockfd == -1) {
printf("socket(): Failure!\n");
freeaddrinfo(res);
return 1;
}
printf("socket(): Success!\n");
// Bind the socket to an IP address and a port
if (bind(sockfd, res->ai_addr, res->ai_addrlen) == -1) {
printf("bind(): Failure!\n");
freeaddrinfo(res);
return 1;
}
printf("bind(): Success!\n");
if (listen(sockfd, 5) == -1) {
printf("listen(): Failure!\n");
freeaddrinfo(res);
return 1;
}
printf("Socket listening on port %s...\n", PORT);
struct sockaddr *clientaddr;
char clientaddrstr[INET6_ADDRSTRLEN];
while (1) {
clientfd = accept(sockfd, clientaddr, (int *)sizeof(clientaddr));
if (clientfd == -1) {
printf("accept(): Failure!\n");
freeaddrinfo(res);
return 1;
}
printf("accept(): Success!\n");
inet_ntop(clientaddr->sa_family, clientaddr, clientaddrstr, sizeof(clientaddrstr));
printf("Accepted a conenction to %s\n", clientaddrstr);
}
freeaddrinfo(res);
return 0;
}
客户端.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#define PORT "8080"
#define BUFFLEN 1024
/** TODO
* getaddrinfo() to get my address
*/
void printaddrs(struct addrinfo *res);
char *strin(FILE *stream);
int main() {
printf("Choose an address to connect to: ");
char *servaddr = strin(stdin);
// Initialize variables to be used later
struct addrinfo hints, *res;
int clientfd;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if (getaddrinfo(servaddr, PORT, &hints, &res) != 0) {
printf("getaddrinfo(): Failure!\n");
free(servaddr);
freeaddrinfo(res);
return 1;
}
printf("getaddrinfo(): Success!\n");
clientfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (clientfd == -1) {
printf("socket(): Failure!\n");
freeaddrinfo(res);
return 1;
}
printf("socket(): Success!\n");
if (connect(clientfd, res->ai_addr, res->ai_addrlen) == -1) {
printf("connect(): Failure!\n");
freeaddrinfo(res);
return 1;
}
printf("connect(): Success!\n");
printf("Connecting to %s...\n", servaddr);
free(servaddr);
freeaddrinfo(res);
return 0;
}
这是 printaddrs() 函数
void printaddrs(struct addrinfo *res) {
struct addrinfo *p;
int count = 0;
char ipstr[INET6_ADDRSTRLEN], ipver[5];
for (p = res; p != NULL; p = p->ai_next) {
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *) p->ai_addr;
inet_ntop(p->ai_family, ipv4, ipstr, sizeof(ipstr));
strcpy(ipver, "ipv4");
} else if (p->ai_family == AF_INET6) { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) p->ai_addr;
inet_ntop(p->ai_family, ipv6, ipstr, sizeof(ipstr));
strcpy(ipver, "ipv6");
} else { // Other IP format
continue;
}
printf("%d: %s\t:\t%s\n", count, ipstr, ipver);
count++;
}
}
这里是 string()
char *strin(FILE *stream) {
char *str = NULL;
size_t size = 0;
int c;
int i = 0;
while (1) {
c = getc(stream);
if (stream != stdin) {
if(c == EOF)
break;
} else {
if(c == EOF || c =='\n')
break;
}
size++;
str = realloc(str, size * sizeof(char));
*(str + i) = c;
i++;
}
if (size == 0) {
return NULL;
}
str[size] = '\0';
return str;
}
问题来了(终于……)
当我运行 server.c 时,getaddrinfo() 返回 *res 结构,它是指向地址链接列表的指针,输出如下所示:
0: 2.0.31.144 : ipv4 1: a00:1f90:: : ipv6
我本希望获得本地主机地址,IPv4 为 127.0.0.1,IPv6 为 ::1,但显然不是,当我输入
0
选择 ipv4 地址时:
'hints' filled successfully! getaddrinfo(): Success! 0: 2.0.31.144 : ipv4 1: a00:1f90:: : ipv6 Select an address to bind to: 0 Chosen address: 2.0.31.144 socket(): Success! bind(): Success! Socket listening on port 8080...
然后在运行client.c时连接到该地址
Choose an address to connect to: 2.0.31.144 getaddrinfo(): Success! socket(): Success! connect(): Failure!
连接功能不起作用...
有人有什么建议吗?
提前感谢大家的回答,我真的很感激!
我尝试向人工智能寻求解决方案......这不是最明智的举动,我知道...... 我已经快绝望了
而不是这个:
inet_ntop(p->ai_family, ipv4, ipstr, sizeof(ipstr));
这个:
inet_ntop(p->ai_family, &ipv4->sin_addr, ipstr, sizeof(ipstr));
对 ipv6 结构体的类似处理:
inet_ntop(p->ai_family, &ipv6->sin6_addr, ipstr, sizeof(ipstr));
这将为您的两个地址打印
0.0.0.0
和 ::
。我相信使用 AI_PASSIVE 时这是可以预料到的。这些是“任何”地址,告诉服务器代码绑定到所有适配器 - 这通常是您想要的。
如果从 getaddrsinfo 调用中删除 AI_PASSIVE 标志,您将得到
127.0.0.1
和 ::1
打印