我试图编写一个不可知的回显服务器,它可以接受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;
}
server.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;
}
答案 0 :(得分:3)
正如@JoachimPileborg在评论中提到的,问题是你的服务器代码没有为getaddrinfo()
给你的每个地址打开监听套接字。您在AF_UNSPEC
结构中指定了simpleServer
,因此getaddrinfo()
将为您提供两个 IPv4和IPv6地址的列表。您正在为该列表中的第一个条目创建一个侦听套接字,该条目恰好是IPv4地址。这就是您的IPv4客户端成功并且您的IPv6客户端失败的原因。您需要遍历列表,为每个条目创建一个单独的侦听套接字。
您还犯了其他错误,例如在您每次拨打clientNameLength
时都没有重置accept()
变量,当您应该使用addrinfo
作为clientName
缓冲区时{ {1}}而不是注意sockaddr_storage
的返回值。
尝试更像这样的事情:
read()
话虽如此,如果您在支持双栈套接字的平台上运行,您的服务器根本不必创建任何IPv4侦听套接字。它只能创建IPv6套接字,然后禁用其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()
答案 1 :(得分:0)
client.c代码有缺陷,它将完整的addrinfo存储在sockaddr_in结构中。
res的类型为masterfile.txt
(http://man7.org/linux/man-pages/man3/getaddrinfo.3.html)
addrinfo
所以而不是
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
它应该是
struct sockaddr_in *addr_in = (struct sockaddr_in *)res;
喜欢
struct sockaddr_in *addr_in = (struct sockaddr_in *)res->ai_addr;
此修改后,代码将按预期工作。