似乎流不会进入ipv6server.c中包含accept的“for”循环,因此无法接受并连接客户端。怎么回事?此代码适用于IPV4,但在IPV6更改后出现此问题
ipv6server.c
#include <stdio.h>
#include <stdlib.h> /* needed for os x */
#include <string.h> /* for memset */
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/errno.h> /* defines ERESTART, EINTR */
#include <sys/wait.h> /* defines WNOHANG, for wait() */
#include "port.h" /* defines default port */
#ifndef ERESTART
#define ERESTART EINTR
#endif
extern int errno;
void serve(int port); /* main server function */
void disconn(void);
main(int argc, char **argv)
{
extern char *optarg;
extern int optind;
int c, err = 0;
int port = SERVICE_PORT;
static char usage[] = "usage: %s [-d] [-p port]\n";
while ((c = getopt(argc, argv, "dp:")) != -1)
switch (c) {
case 'p':
port = atoi(optarg);
if (port < 1024 || port > 65535) {
fprintf(stderr, "invalid port number: %s\n", optarg);
err = 1;
}
break;
case '?':
err = 1;
break;
}
if (err || (optind < argc)) {
fprintf(stderr, usage, argv[0]);
exit(1);
}
serve(port);
}
/* serve: set up the service */
void
serve(int port)
{
int svc; /* listening socket providing service */
int rqst; /* socket accepting the request */
socklen_t alen; /* length of address structure */
struct sockaddr_in6 my_addr; /* address of this service */
struct sockaddr_in6 client_addr; /* client's address */
int sockoptval = 1;
char hostname[128]; /* host name, for debugging */
gethostname(hostname, 128);
/* get a tcp/ip socket */
/* AF_INET is the Internet address (protocol) family */
/* with SOCK_STREAM we ask for a sequenced, reliable, two-way */
/* conenction based on byte streams. With IP, this means that */
/* TCP will be used */
if ((svc = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
perror("cannot create socket");
exit(1);
}
/* we use setsockopt to set SO_REUSEADDR. This allows us */
/* to reuse the port immediately as soon as the service exits. */
/* Some operating systems will not allow immediate reuse */
/* on the chance that some packets may still be en route */
/* to the port. */
setsockopt(svc, SOL_SOCKET, SO_REUSEADDR, &sockoptval, sizeof(int));
/* set up our address */
/* htons converts a short integer into the network representation */
/* htonl converts a long integer into the network representation */
/* INADDR_ANY is the special IP address 0.0.0.0 which binds the */
/* transport endpoint to all IP addresses on the machine. */
memset((char*)&my_addr, 0, sizeof(my_addr)); /* 0 out the structure */
my_addr.sin6_family = AF_INET6; /* address family */
my_addr.sin6_port = htons(port);
my_addr.sin6_addr = in6addr_any;
client_addr.sin6_family = AF_INET6; /* address family */
client_addr.sin6_port = htons(port);
client_addr.sin6_addr = in6addr_any;
/* bind to the address to which the service will be offered */
if (bind(svc, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0) {
perror("bind failed");
exit(1);
}
/* set up the socket for listening with a queue length of 5 */
if (listen(svc, 5) < 0) {
perror("listen failed");
exit(1);
}
printf("server started on %s, listening on port %d\n", hostname, port);
/* loop forever - wait for connection requests and perform the service */
alen = sizeof(client_addr); /* length of address */
for (;;) {
while ((rqst = accept(svc,
(struct sockaddr *)&client_addr, &alen)) < 0) {
/* we may break out of accept if the system call */
/* was interrupted. In this case, loop back and */
/* try again */
if ((errno != ECHILD) && (errno != ERESTART) && (errno != EINTR)) {
perror("accept failed");
exit(1);
}
}
printf("received a connection from: %s port %d\n",
inet_ntoa(client_addr.sin6_addr), ntohs(client_addr.sin6_port));
shutdown(rqst, 2); /* close the connection */
}
}
ipv6client.c
/*
echoc: a demo of TCP/IP sockets connect
usage: client [-h serverhost] [-p port]
*/
#include <stdio.h>
#include <stdlib.h> /* needed for os x*/
#include <string.h> /* for strlen */
#include <netdb.h> /* for gethostbyname() */
#include <sys/socket.h>
#include <netinet/in.h>
#include "port.h" /* defines default port */
int conn(char *host, int port);
void disconn(void);
main(int argc, char **argv)
{
extern char *optarg;
extern int optind;
int c, err = 0;
char *prompt = 0;
int port = SERVICE_PORT; /* default: whatever is in port.h */
char *host = "localhost"; /* default: this host */
static char usage[] =
"usage: %s [-d] [-h serverhost] [-p port]\n";
while ((c = getopt(argc, argv, "dh:p:")) != -1)
switch (c) {
case 'h': /* hostname */
host = optarg;
break;
case 'p': /* port number */
port = atoi(optarg);
if (port < 1024 || port > 65535) {
fprintf(stderr, "invalid port number: %s\n", optarg);
err = 1;
}
break;
case '?':
err = 1;
break;
}
if (err || (optind < argc)) { /* error or extra arguments? */
fprintf(stderr, usage, argv[0]);
exit(1);
}
printf("connecting to %s, port %d\n", host, port);
if (!conn(host, port)) /* connect */
exit(1); /* something went wrong */
disconn(); /* disconnect */
return 0;
}
int fd; /* fd is the file descriptor for the connected socket */
/* conn: connect to the service running on host:port */
/* return 0 on failure, non-zero on success */
int
conn(char *host, int port)
{
struct hostent *hp; /* host information */
unsigned int alen; /* address length when we get the port number */
struct sockaddr_in6 myaddr; /* our address */
struct sockaddr_in6 servaddr; /* server address */
printf("conn(host=\"%s\", port=\"%d\")\n", host, port);
/* get a tcp/ip socket */
/* We do this as we did it for the server */
/* request the Internet address protocol */
/* and a reliable 2-way byte stream */
if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
perror("cannot create socket");
return 0;
}
/* bind to an arbitrary return address */
/* because this is the client side, we don't care about the */
/* address since no application will connect here --- */
/* INADDR_ANY is the IP address and 0 is the socket */
/* htonl converts a long integer (e.g. address) to a network */
/* representation (agreed-upon byte ordering */
memset((char *)&myaddr, 0, sizeof(myaddr));
myaddr.sin6_family = AF_INET6;
myaddr.sin6_addr = in6addr_any;
myaddr.sin6_port = htons(0);
if (bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
perror("bind failed");
return 0;
}
/* this part is for debugging only - get the port # that the operating */
/* system allocated for us. */
alen = sizeof(myaddr);
if (getsockname(fd, (struct sockaddr *)&myaddr, &alen) < 0) {
perror("getsockname failed");
return 0;
}
printf("local port number = %d\n", ntohs(myaddr.sin6_port));
/* fill in the server's address and data */
/* htons() converts a short integer to a network representation */
memset((char*)&servaddr, 0, sizeof(servaddr));
servaddr.sin6_family = AF_INET6;
servaddr.sin6_port = htons(port);
/* look up the address of the server given its name */
hp = gethostbyname(host);
if (!hp) {
fprintf(stderr, "could not obtain address of %s\n", host);
return 0;
}
/* put the host's address into the server address structure */
memcpy((void *)&servaddr.sin6_addr, hp->h_addr_list[0], hp->h_length);
/* connect to server */
if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("connect failed");
return 0;
}
return 1;
}
/* disconnect from the service */
void
disconn(void)
{
printf("disconn()\n");
shutdown(fd, 2); /* 2 means future sends & receives are disallowed */
}
答案 0 :(得分:5)
我可以看到一些不同程度的问题:
errno
- 使用标题。 可能是一个宏而不是int
bind
客户端套接字getaddrinfo()
代替gethostbyname()
来获取服务器的地址。 gethostbyname()
无法支持IPv6 3的最后一个实际上是真正的问题。我已经在MacOS X和CentOS 5上测试了您的代码,而gethostbyname()
只返回了一个IPv4地址。
答案 1 :(得分:5)
hp = gethostbyname(host);
如果你传递“localhost”,你怎么知道这会返回一个IPv6地址?它可能会返回IPv4地址,如果您尝试将其复制到servaddr.sin6_addr
使用getaddrinfo()并显式查找AF_INET6地址(或者更好,使程序独立于实际地址类型,请参阅here),或使用全局in6addr_loopback
作为服务器地址,用于使用localhost进行测试。