我在使用Windows 10的DELL E6400上使用Visual Studio Enterprise 2017.我正在编写一个充当服务器的功能。在连接一次连接后我无法接受连接。我已关闭之前的连接。我使用Wireshark并且可以看到SYN,SYN / ACK,ACK,因此逻辑上已经建立了连接。但我的程序并没有出现在select调用中(请注意,第一次连接顺利,并且在关闭第一个连接后会出现此问题。)
以下是代码序列的要点“()”表示它是一个函数调用:
gethostname()
gethostbyaddr()
server_addr->sin_family
server_addr->sin_port
socket()
setsockopt()
bind()
getsockname()
listen()
FD_ZERO();
FD_SET();
k = select()
closesocket()
以下是实际代码:
#include <stdlib.h>
#include <conio.h>
#include "EasyTcp.h"
// On Windows, use Ws2_32.dll
/* need to define some linux-only symbols for other systems */
#ifndef SOL_TCP
#ifndef IPPROTO_TCP
#define IPPROTO_TCP 6 // 6 usually = TCP
#endif
#define SOL_TCP IPPROTO_TCP /* tcp's protocol number (from /etc/protocols) */
#endif
#define LISTENING_DEPTH 3
#define MAXHOSTNAMELEN 132
void start_up(void);
SOCKET openserver( unsigned int server_port, char *server_name,
struct sockaddr_in *server_addr );
int print_address(char *message, int len, struct sockaddr_in *addr,
unsigned int *host, unsigned int *port);
int print_ip_address( unsigned int ip_address );
SOCKET get_local_address( SOCKET fd, unsigned int *host, unsigned int *port);
int get_ip_address( char *name, unsigned int *host );
SOCKET accept_a_client(SOCKET listening_fd, struct sockaddr_in *addr,
unsigned int *whole, unsigned int *fraction);
/* Accept a connection on port local_port.
Returns the socket or -1 if an error.
*/
SOCKET EasyAccept( int local_port )
{
SOCKET listening_fd = -1; // -1 when not connected
SOCKET fd;
unsigned int sys_remote_port = 0;
unsigned int sys_local_host = 0;
unsigned int sys_remote_host = 0;
struct sockaddr_in target_addr;
struct sockaddr_in initiator_addr;
unsigned int sys_local_port = local_port; // The port to listen on
unsigned int *whole_ptr = NULL, *fraction_ptr = NULL; // The amount of time to listen
char *default_host = NULL; // The host you want to listen to, else NULL
listening_fd = openserver( sys_local_port, default_host, &target_addr );
if( listening_fd >= 0 )
{
print_address("Listening", 1, &target_addr, &sys_local_host,
&sys_local_port);
}
else
{
printf("openserver returned an error\n");
return -1;
}
if( (fd = accept_a_client(listening_fd, &initiator_addr,
whole_ptr,fraction_ptr)) >= 0 )
{/* accept succeeded, fd is connection to new initiator */
print_address("Server", 1, &initiator_addr, &sys_remote_host,
&sys_remote_port);
get_local_address(fd, &sys_local_host, &sys_local_port);
}
else
{/* accept failed */
printf("accept failed, fd=%p\n", (void *)fd);
return -1;
}
return fd;
}
int EasyConnect( int remote_port, char *remote_server )
{
// not done yet
printf("EasyConnect() is not finished\n");
_getch();
exit(1);
}
/* Opens a new TCP socket for the server at interface server_name, */
/* port server_port. Returns in server_addr the server's internet */
/* address structure. The server is NOT connected to a client on return. */
/* Returns an fd that is a "listening post" on which to make connections. */
SOCKET openserver( unsigned int server_port, char *server_name,
struct sockaddr_in *server_addr )
{
int len;
SOCKET fd;
struct sockaddr_in address1;
struct in_addr address0;
struct hostent *node_ptr;
char local_node[MAXHOSTNAMELEN];
char *host_name;
char buffer[64];
start_up();
/* get the internet name of the local host node on which we are running */
if( gethostname(local_node, MAXHOSTNAMELEN) < 0 )
{
printf("openserver gethostname: %s\n", strerror(errno));
strcpy(local_node, "Unknown Host");
}
if( server_name == NULL )
host_name = local_node; /* default to local node */
else
host_name = server_name;
/* get structure for local interface server is to use */
if( isdigit((int)host_name[0]) )
{/* must be an ip address, not a DNS name */
#ifdef __sun__
if( (*((int *)&address0) = inet_addr(host_name)) == -1 )
#else
if( !inet_aton(host_name, &address0) )
#endif
{
printf("Invalid IP address \"%s\"\n", host_name);
return -1;
}
if( (node_ptr = gethostbyaddr((char *)&address0,
sizeof(address0), AF_INET)) == NULL )
{
herror(host_name);
return -1;
}
}
else if( (node_ptr = gethostbyname(host_name)) == NULL )
{
herror(host_name);
return -1;
}
/* set up Internet address structure for the server */
if (server_addr == NULL) /* user doesn't want this back */
{
server_addr = &address1; /* so store it locally */
}
memset(server_addr, 0, sizeof(struct sockaddr_in));
server_addr->sin_family = AF_INET; // node_ptr->h_addrtype; /* should be AF_INET */
server_addr->sin_port = htons( (u_short)server_port );
if( server_name == NULL )
server_addr->sin_addr.s_addr=htonl(INADDR_ANY); /* use any interface */
else
memcpy(&server_addr->sin_addr, node_ptr->h_addr, node_ptr->h_length);
/* open an internet TCP socket */
if( (fd = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
{
printf("openserver socket: %s\n", strerror(errno));
return -1;
}
/* set this socket to reuse port addresses quickly */
if (setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, buffer, sizeof( buffer ) ) < 0)
{
printf( "openserver setsockopt REUSEADDR: %s\n", strerror( errno ) );
}
/* bind this socket to the server's Internet address */
if( bind(fd,(struct sockaddr *)server_addr,sizeof(struct sockaddr_in))<0 )
{
printf("Bind of IP address %s returned an error, port %d: %s\n",
inet_ntoa(server_addr->sin_addr), ntohs(server_addr->sin_port),
strerror(errno));
//close(fd);
return -1;
}
/* now find out what local port number was assigned to this server */
len = sizeof(struct sockaddr);
if( getsockname(fd, (struct sockaddr *)server_addr, (void *)&len) < 0 )
{
printf("openserver getsockname: %s\n", strerror(errno));
close(fd);
return -1;
}
/* set up listening backlog for connect requests from clients */
if( listen(fd, LISTENING_DEPTH) < 0 )
{
printf("openserver listen: %s\n", strerror(errno));
close(fd);
return -1;
}
/* we are now successfully established as a server */
return fd; /* return fd of listening socket */
}
int print_address(char *message, int len, struct sockaddr_in *addr,
unsigned int *host, unsigned int *port)
{
printf("%*s at IP address %s, port %d\n",
len, message, addr->sin_addr.S_un.S_addr == 0 ? "any" : inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
if( host != NULL )
*host = ntohl(addr->sin_addr.s_addr);
if( port != NULL )
*port = ntohs(addr->sin_port);
return 0;
}
int print_ip_address( unsigned int ip_address )
{
struct sockaddr_in x;
x.sin_addr.s_addr = htonl(ip_address);
printf("%s", inet_ntoa(x.sin_addr));
return 0;
}
SOCKET get_local_address( SOCKET fd, unsigned int *host, unsigned int *port)
{
#if defined(__osf__) || defined(_HPUX_SOURCE)
struct sockaddr_in addr;
int n = sizeof(addr);
#else
struct sockaddr_in addr;
int n = sizeof(addr);
#endif
if( getsockname(fd, (struct sockaddr *)&addr, &n) < 0 )
{
perror("getsockname");
return -1;
}
if( host != NULL )
*host = ntohl(addr.sin_addr.s_addr);
if( port != NULL )
*port = ntohs(addr.sin_port);
return 0;
}
int get_ip_address( char *name, unsigned int *host )
{
struct in_addr address0;
struct hostent *node_ptr;
/* get structure for remote host node on which server resides */
if( isdigit((int)name[0]) )
{/* must be an ip address, not a DNS name */
#ifdef __sun__
if( (*((int *)&address0) = inet_addr(name)) == -1 )
#else
if( !inet_aton(name, &address0) )
#endif
{
printf("Invalid IP address \"%s\"\n", name);
return -1;
}
if( (node_ptr = gethostbyaddr((char *)&address0,
sizeof(address0), AF_INET)) == NULL )
{
herror(name);
return -1;
}
}
else if( (node_ptr = gethostbyname(name)) == NULL )
{
herror(name);
return -1;
}
if( host != NULL )
*host = ntohl(*(unsigned int *)node_ptr->h_addr);
return 0;
}
/* wait for at most whole.fraction seconds to accept a connection */
/* returns fd >= 0 if ok, -1 on error, -2 on timeout or control-C */
SOCKET accept_a_client( SOCKET listening_fd, struct sockaddr_in *addr,
unsigned int *whole, unsigned int *fraction )
{
struct sockaddr *client;
struct sockaddr local_addr;
int len, k;
SOCKET fd;
struct timeval local_tv, *tv;
fd_set readset;
char buffer[64];
len = sizeof(struct sockaddr);
if( addr == NULL )
client = &local_addr;
else
client = (struct sockaddr *)addr;
//printf("Accepting connection\n");
fflush(stdout);
do {
if( whole == NULL || fraction == NULL )
tv = NULL; /* no timeout given */
else
{/* have a timeout, must set it up each time around loop */
tv = &local_tv;
tv->tv_sec = *whole;
tv->tv_usec = *fraction;
}
FD_ZERO(&readset);
FD_SET(listening_fd, &readset);
k = select(0, &readset, NULL, NULL, tv); // for Windows the 1st parameter is not used
if( k < 0 )
{/* select found an error */
if( errno == EINTR )
{/* return forced by control-C, treat it like timeout */
return -2;
}
else
{
printf("Select on fd %p: %s\n", (void *)listening_fd, strerror(errno));
return -1;
}
}
else if( k == 0 )
{/* select timed out */
return -2;
}
else if( (fd = accept(listening_fd, client, (void *)&len)) < 0 )
{/* accept found an error */
if( errno == EINTR )
return -2;
else
{
printf("Accept on fd %p: %s\n", (void *)listening_fd, strerror(errno));
return -1;
}
}
else
{/* fd is now the newly accepted connection from a new client */
/* turn off the Nagle Algorithm on this connection */
if( setsockopt(fd, SOL_TCP, TCP_NODELAY, buffer, sizeof(buffer)) < 0 )
{
printf("accept_a_client setsockopt NODELAY: %s\n",
strerror(errno));
}
break;
}
}
while( 1 );
/*****
printf("accept_a_client returning fd %p\n", fd);
*****/
return fd;
}
答案 0 :(得分:0)
由于您的interface Visitor {
void Visit(Target target);
}
class Target {
void Accept(Visitor visitor) {
visitor.Visit(this); // not just a field but a whole instance
}
}
函数未包含在已发布的代码中,因此很难确定发生了什么,但如果我可以假设它正在调用{{1在一个循环中,问题是你的main()
函数形成错误。
特别是,EasyAccept()
似乎调用EasyAccept()
,它会创建一个新的TCP连接接受套接字,并在其上调用EasyAccept()
和openserver()
。到现在为止还挺好。
然后,有问题的部分:bind()
然后继续使用该套接字接受传入的TCP连接并返回TCP连接的套接字。
那为什么会出现这个问题呢?因为当listen()
返回时,您的程序已经泄露了#34; EasyAccept()
套接字 - 也就是说,套接字仍处于打开状态,但您的程序无法知道前一次调用中EasyAccept()
的值是多少,因此它不能再接受TCP连接。
因此,第二次调用listening_fd
时,它将尝试创建另一个接受TCP连接的套接字,但这次listening_fd
调用将失败,因为已经存在TCP连接 - 接受套接字侦听要绑定的端口 - &#34;忘记&#34;第一次拨打EasyAccept()
时的TCP套接字。
要解决此问题,您需要将bind()
的呼叫从EasyAccept()
功能移出;相反,在程序执行开始时(可能在openserver()
的顶部附近)只调用EasyAccept()
一次,并且在程序的持续时间内保持其返回值&# 39;执行。然后修改openserver()
以将main()
值作为其参数而不是EasyAccept()
。这样,每次调用SOCKET
都可以重用一个接受TCP连接的套接字,而不是在每次调用时错误地尝试创建一个新套接字。