gpt4 book ai didi

c - C 中的 IPv6 连接问题

转载 作者:行者123 更新时间:2023-12-02 12:35:51 25 4
gpt4 key购买 nike

我正在尝试编写一个不可知的回显服务器,它可以接受 IPv4 和 IPv6 连接。我正在使用 addrinfo 结构,并使用 getaddrinfo 设置。
Ipv4 连接没有问题,但我无法获得有效的 ipV6 连接。我认为我的问题可能是由于错误的 getaddrinfo 参数造成的,但我看不出哪里出了问题。
这是我的代码

client.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;
}

最佳答案

正如 @JoachimPileborg 在评论中提到的,问题是您的服务器代码没有为 getaddrinfo() 的每个地址打开监听套接字。给你。您正在指定 AF_UNSPEC在你的simpleServer结构体,所以getaddrinfo()将为您提供 IPv4 和 IPv6 地址的列表。您仅为该列表中的第一个条目(恰好是 IPv4 地址)创建监听套接字。这就是 IPv4 客户端成功而 IPv6 客户端失败的原因。您需要循环遍历列表,为每个条目创建一个单独的监听套接字。

您还犯了其他错误,例如没有重置您的 clientNameLength每次调用 accept() 时都有变量,使用addrinfo作为您的clientName当你应该使用 sockaddr_storage 时进行缓冲相反,不关注 read() 的返回值.

尝试更多类似这样的事情:

include <stdio.h>      
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <poll.h>

#define MAX_SERVERS 10
#define MAX_CLIENTS 50
#define MAX_SOCKETS (MAX_SERVERS + MAX_CLIENTS)

int main(int argc, char *argv[])
{
int simpleSocket, simplePort, returnStatus, n, m;
char buffer[1024];
pollfd simpleSockets[MAX_SOCKETS];
int numSockets = 0, numServers = 0;

struct addrinfo simpleServer, *res, *addr;

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 and/or IPv6, whatever is available
simpleServer.ai_socktype = SOCK_STREAM;
simpleServer.ai_flags = AI_PASSIVE; // fill in my IP for me

if (0 != getaddrinfo(NULL, argv[1], &simpleServer, &res))
{
fprintf(stderr, "getaddrinfo failed\n");
exit(1);
}

addr = res;
while (NULL != addr)
{
simpleSocket = socket(res->ai_family, addr->ai_socktype, addr->ai_protocol);
if (-1 == simpleSocket)
{
fprintf(stderr, "socket failed\n");
}
else
{
returnStatus = bind(simpleSocket, addr->ai_addr, addr->ai_addrlen);
if (0 == returnStatus)
returnStatus = listen(simpleSocket, 5);

if (0 == returnStatus)
{
simpleSockets[numSockets].fd = simpleSocket;
simpleSockets[numSockets].events = POLLIN;
simpleSockets[numSockets].revents = 0;
++numSockets;
++numServers;
if (MAX_SERVERS == numServers)
break;
}
else
{
fprintf(stderr, "bind/listen failed\n");
close(simpleSocket);
}
}
addr = addr->next;
}

freeaddrinfo(res);

if (0 == numServers)
{
fprintf(stderr, "no servers are listening\n");
exit(1);
}

struct sockaddr_storage clientName;
int clientNameLength;

while (1)
{
returnStatus = poll(simpleSockets, numSockets, -1);
if (-1 == returnStatus)
{
fprintf(stderr, "poll failed\n");
exit(1);
}

if (0 == returnStatus)
continue;

for (n = 0; n < numSockets; ++n)
{
if (simpleSockets[n].revents & POLLIN)
{
if (n < numServers)
{
clientNameLength = sizeof(clientName);

simpleSocket = accept(simpleSockets[n].fd, (struct sockaddr *)&clientName, &clientNameLength);
if (-1 == simpleSocket)
{
fprintf(stderr, "accept failed\n");
continue;
}

for (m = numServers; m < numSockets; ++m)
{
if (-1 == simpleSockets[m].fd)
{
simpleSockets[m].fd = simpleSocket;
simpleSockets[m].events = POLLIN;
simpleSockets[m].revents = 0;
simpleSocket = -1;
break;
}
}

if ((-1 != simpleSocket) && (MAX_SOCKETS > numSockets))
{
simpleSockets[numSockets].fd = simpleSocket;
simpleSockets[numSockets].events = POLLIN;
simpleSockets[numSockets].revents = 0;
++numSockets;
simpleSocket = -1;
}

if (-1 != simpleSocket)
{
fprintf(stderr, "Too many clients connected\n");
close(simpleSocket);
}
else
fprintf(stdout, "Client connected\n");
}
else
{
returnStatus = read(simpleSockets[n].fd, buffer, sizeof(buffer));
if (0 >= returnStatus)
{
if (0 > returnStatus)
fprintf(stdout, "Client error, disconnecting\n");
else
fprintf(stdout, "Client disconnected\n");

close(simpleSockets[n].fd);
simpleSockets[n].fd = -1;
simpleSockets[n].events = 0;
simpleSockets[n].revents = 0;

continue;
}

fprintf(stdout, "Message: %.*s\n", returnStatus, buffer);
write(simpleSockets[n].fd, buffer, returnStatus);
}
}

if (simpleSockets[n].revents & (POLLERR|POLLHUP|POLLNVAL))
{
if (simpleSockets[n].revents & POLLHUP)
fprintf(stdout, "Client disconnected\n");
else if (n >= numServers)
fprintf(stdout, "Client error, disconnecting\n");
else
fprintf(stdout, "Server error, closing\n");

close(simpleSockets[n].fd);
simpleSockets[n].fd = -1;
simpleSockets[n].events = 0;
simpleSockets[n].revents = 0;
}
}
}

for (n = 0; n < numSockets; ++n)
{
if (-1 != simpleSockets[n].fd)
close(simpleSockets[n].fd);
}

return 0;
}

话虽如此,如果您运行在支持双栈套接字的平台上,您的服务器根本不必创建任何 IPv4 监听套接字。它只能创建 IPv6 套接字,然后禁用它们的 IPV6_V6ONLY选项。这将使它们能够接受 IPv4 和 IPv6 客户端。 accept()返回的客户端地址将告诉您 IPv4 或 IPv6 客户端是否已连接。

memset(&simpleServer, 0, sizeof simpleServer);
simpleServer.ai_family = AF_INET6;
simpleServer.ai_socktype = SOCK_STREAM;
simpleServer.ai_flags = AI_PASSIVE;

if (0 != getaddrinfo(NULL, argv[1], &simpleServer, &res))
{
fprintf(stderr, "getaddrinfo failed\n");
exit(1);
}

addr = res;
while (NULL != addr)
{
simpleSocket = socket(res->ai_family, addr->ai_socktype, addr->ai_protocol);
if (-1 == simpleSocket)
{
fprintf(stderr, "socket failed\n");
}
else
{
n = 0;
returnStatus = setsockopt(simpleSocket, IPPROTO_IPV6, IPV6_V6ONLY, &n, sizeof(n));
...
}
addr = addr->next;
}

...

关于c - C 中的 IPv6 连接问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34837978/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com