如何检查c套接字中是否有TCP端口可用?

时间:2018-08-09 02:00:26

标签: c sockets

我正在编写一个程序,其中有2个播放器想要连接到服务器来播放石头,纸张和剪刀。第一个播放器连接到端口60000,而第二个播放器想要连接时,它尝试连接到端口60000。如果失败,它将连接到端口60001。此刻,我不确定如何实现第二个播放器。

客户:

int sock = 0;
char *hostname = "127.0.0.1";
struct sockaddr_in serv_addr;
char buffer[1024] = {0};
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
    printf("\n Socket creation error \n");
    return -1;
}

memset(&serv_addr, '0', sizeof(serv_addr));

serv_addr.sin_family = AF_INET;


// Clear this field; sin_zero is used for padding for the struct.
memset(&(serv_addr.sin_zero), 0, 8);
// Lookup host IP address.
struct hostent *hp = gethostbyname(hostname);
if (hp == NULL) {
    fprintf(stderr, "unknown host %s\n", hostname);
    exit(1);
}
serv_addr.sin_addr = *((struct in_addr *) hp->h_addr);

serv_addr.sin_port = htons(PORT);


if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
    printf("\nConnection Failed \n");
    return -1;
}

//getting the user name
printf("please enter your name:");
scanf("%s",buffer);
send(sock , buffer , strlen(buffer) , 0 );

//initializing the game
read( sock , buffer, 1024);

//playing the game until the user enters e  
do{
    printf("%s",buffer);
    memset(buffer,0,sizeof(buffer));
    scanf("%s",buffer);
    while(check_input(buffer)==0){
        printf("wrong input,try again:");
        memset(buffer,0,sizeof(buffer));
        scanf("%s",buffer);
    }
    send(sock , buffer , strlen(buffer) , 0 );//sending the input to the server
    printf("client:sent %s\n",buffer);
    read( sock , buffer, 1024);
    printf("client:received %s\n",buffer);
}while(is_over(buffer)==2);
return 0;

在服务器中:

char player1Name[1024];
char player2Name[1024];
int p1_score = 0;
int p2_score = 0;
char buffer[1024] = {0};
int server_fd;
int server_fd2;
int player1_socket;
int player2_socket;
struct sockaddr_in player1;
struct sockaddr_in player2;
int opt = 1;
int opt2=1;
int player1len = sizeof(player1);
int player2len = sizeof(player2);

// Creating socket file descriptor for player 1
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0){
    perror("socket failed");
    exit(EXIT_FAILURE);
}

// Creating socket file descriptor for player 2
if ((server_fd2 = socket(AF_INET, SOCK_STREAM, 0)) == 0){
    perror("socket failed");
    exit(EXIT_FAILURE);
}

// making the first socket reusable
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,&opt, sizeof(opt))){
    perror("setsockopt");
    exit(EXIT_FAILURE);
}

// making the second socket reusable
if (setsockopt(server_fd2, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,&opt2, sizeof(opt2))){
    perror("setsockopt");
    exit(EXIT_FAILURE);
}

//specifying the address of the first player
player1.sin_family = AF_INET;
player1.sin_addr.s_addr = INADDR_ANY;
player1.sin_port = htons( PORT1 );

//specifying the address of the second player
player2.sin_family = AF_INET;
player2.sin_addr.s_addr = INADDR_ANY;
player2.sin_port = htons( PORT2 );

// Forcefully attaching socket to the port 6000
if (bind(server_fd, (struct sockaddr *)&player1, sizeof(player1))<0){
    perror("bind failed");
    exit(EXIT_FAILURE);
}


if (listen(server_fd, 1) < 0){
    perror("listen");
    exit(EXIT_FAILURE);
}


if ((player1_socket = accept(server_fd, (struct sockaddr *)&player1,(socklen_t*)&player1len))<0){
    perror("accept");
    exit(EXIT_FAILURE);
}

get_playerName(player1Name,&player1_socket);

// Forcefully attaching socket to the port 6001
if (bind(server_fd2, (struct sockaddr *)&player2, sizeof(player2))<0){
    perror("bind failed");
    exit(EXIT_FAILURE);
}


if (listen(server_fd2, 1) < 0){
    perror("listen");
    exit(EXIT_FAILURE);
}


if ((player2_socket = accept(server_fd2, (struct sockaddr *)&player2,(socklen_t*)&player2len))<0){
    perror("accept");
    exit(EXIT_FAILURE);
}

get_playerName(player2Name,&player2_socket);
char input1;
char input2;
do{
        input1=get_nextMoves(player1Name,buffer,&player1_socket);
        printf("%c\n",input1);
        input2=get_nextMoves(player2Name,buffer,&player2_socket);
        printf("%c\n",input2);
        evaluate(input1,input2,&p1_score,&p2_score);
}while(input1!='e' && input2!='e');
strcpy(buffer,result(1,p1_score,p2_score));
send(player1_socket , buffer , strlen(buffer) , 0 );
strcpy(buffer,result(2,p1_score,p2_score));
send(player2_socket , buffer , strlen(buffer) , 0 );
return 0;

此刻,为了实验起见,我同时为播放器1和播放器2运行此代码。当我运行播放器2的代码时,它只是卡住了。 我希望出现错误(更具体地讲,EADDRINUSE)。这是怎么回事?我该如何进一步处理我的代码?

1 个答案:

答案 0 :(得分:1)

为了使您得到一个错误,当第一个客户端连接时,服务器必须关闭正在侦听端口6000的套接字。否则,您的连接将成功,但由于服务器第二次不调用accept()而挂起。

如果服务器这样做,则第二个客户端应该收到错误ECONNREFUSED,并且它可以尝试第二个端口。

if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
    if (errno == ECONNREFUSED) {
        serv_addr.sin_port = htons(PORT + 1);
        if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
        {
            printf("\nConnection Failed \n");
            return -1;
        }
    } else {
        printf("\nConnection Failed \n");
        return -1;
    }
}

但是,请注意,由于计时窗口的原因,这具有潜在的故障模式。如果两个客户端都尝试同时连接,则第二个客户端的连接请求可能会在服务器关闭监听套接字之前到达,因此即使服务器从未处理过该连接,对connect()的调用仍将成功。

要解决此问题,需要进行更为精细的服务器设计,在该设计中,服务器接受第二个连接并返回一条响应,指出该端口已在使用中。尽管可以做到这一点,但您首先不需要两个端口。