【发布时间】:2016-01-17 11:49:26
【问题描述】:
我正在尝试编写一个不可知的回显服务器,它可以接受 IPv4 和 IPv6 连接。我正在使用 addrinfo 结构,使用 getaddrinfo 设置。
ipv4 连接没有问题,而我无法获得有效的 ipv6 连接。
我认为我的问题可能是由于错误的 getaddrinfo 参数造成的,但我看不到哪里出错了。
这是我的代码
客户端.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int simpleSocket = 0, simplePort = 0,returnStatus = 0, n;
char buffer[1024] = "";
struct hostent *hostinfo;
struct addrinfo simpleServer, *res;
if (3 != argc) {
fprintf(stderr, "Usage: %s <server> <port>\n", argv[0]);
exit(1);
}
simplePort = atoi(argv[2]);
memset(&simpleServer, 0, sizeof simpleServer);
simpleServer.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
simpleServer.ai_socktype = SOCK_STREAM;
simpleServer.ai_flags = AI_PASSIVE; // fill in my IP for me
returnStatus = getaddrinfo(argv[1], argv[2], &simpleServer, &res);
simpleSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
char *s = NULL;
switch(res->ai_addr->sa_family) {
case AF_INET: {
struct sockaddr_in *addr_in = (struct sockaddr_in *)res;
s = malloc(INET_ADDRSTRLEN);
inet_ntop(AF_INET, &(addr_in->sin_addr), s, INET_ADDRSTRLEN);
returnStatus = connect(simpleSocket, res->ai_addr, res->ai_addrlen);
break;
}
case AF_INET6: {
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)res;
s = malloc(INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN);
returnStatus = connect(simpleSocket, res->ai_addr, res->ai_addrlen);
break;
}
default:
break;
}
fprintf(stdout, "IP address: %s\n", s);
returnStatus = connect(simpleSocket, res->ai_addr, res->ai_addrlen);
fprintf(stdout, "Type a message \n");
memset(buffer, '\0', strlen(buffer));
fgets(buffer, sizeof(buffer), stdin);
returnStatus = write(simpleSocket, buffer, sizeof(buffer));
memset(&buffer, '\0', sizeof(buffer));
fprintf(stdout, "Waiting server..\n");
returnStatus = read(simpleSocket, buffer, sizeof(buffer));
fprintf(stdout, "Message: %s\n", buffer);
close(simpleSocket);
return 0;
}
服务器.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int simpleSocket = 0, simplePort = 0, returnStatus = 0, check = 1, n;
char buffer[1024];
struct addrinfo simpleServer, *res;
if (2 != argc) {
fprintf(stderr, "Usage: %s <port>\n", argv[0]);
exit(1);
}
simplePort = atoi(argv[1]);
memset(&simpleServer, 0, sizeof simpleServer);
simpleServer.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
simpleServer.ai_socktype = SOCK_STREAM;
simpleServer.ai_flags = AI_PASSIVE; // fill in my IP for me
getaddrinfo(NULL, argv[1], &simpleServer, &res);
simpleSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
returnStatus =bind(simpleSocket, res->ai_addr, res->ai_addrlen);
returnStatus = listen(simpleSocket, 5);
struct addrinfo clientName = { 0 };
int clientNameLength = sizeof(clientName);
int simpleChildSocket = 0;
while (1) {
while (1) {
simpleChildSocket = accept(simpleSocket,(struct sockaddr *)&clientName, &clientNameLength);
fprintf(stdout,"Waiting..\n");
memset(&buffer, '\0', sizeof(buffer));
returnStatus = read(simpleChildSocket, buffer, sizeof(buffer));
fprintf(stdout, "Message: %s\n", buffer);
write(simpleChildSocket, buffer, sizeof(buffer));
}
}
close(simpleChildSocket);
close(simpleSocket);
return 0;
}
【问题讨论】:
-
为什么无限循环里面嵌套了无限循环?
-
至于您的问题,在服务器中,您使用
getaddrinfo返回的第一个条目来创建套接字并绑定到接口。如果您想接受同时使用 IPv4 和 IPv6 的连接,您需要 两个 套接字,并将一个绑定到 IPv4 地址,另一个绑定到 IPv6 地址。 -
哦,你需要在每次调用
accept之前设置clientNameLength,因为它可能会修改参数。 -
@JoachimPileborg 在某些系统上绑定到 :: 的套接字可以接受 IPv4 和 IPv6 连接。
-
@JoachimPileborg:“您需要两个套接字,并将一个绑定到 IPv4 地址,另一个绑定到 IPv6 地址” - 在支持 双栈的平台上 个套接字,您可以在同一个侦听套接字上同时接受 IPv4 和 IPv6 客户端。创建单个 IPv6 (
AF_INET6) 套接字并在调用bind()之前使用setsockopt()禁用其IPV6_V6ONLY选项。accept()报告的客户端 IP 地址将告诉您客户端是 IPv4 还是 IPv6,因此请确保您的接收addr缓冲区足够大以容纳sockaddr_in和sockaddr_in6。最好使用sockaddr_storage作为缓冲区。
标签: c sockets ipv6 berkeley-sockets