【问题标题】:Chat room using socket programming with select() - winsock - C聊天室使用带有 select() 的套接字编程 - winsock - C
【发布时间】:2022-01-05 21:15:21
【问题描述】:

我尝试创建一个服务器-客户端应用程序,其中服务器为连接到服务器的所有客户端提供聊天服务。服务器和客户端使用加密算法和协议来保护通过网络传输的数据。我不知道为什么聊天代码不能正常工作。

我使用select()函数同时操作多个抽屉。如果当多个客户端连接到服务器并将数据发送到服务器并且它获取所有内容时我只使用一段代码,那很好,但是一旦我尝试编写一段将成为聊天功能的代码,即使多个客户端连接,服务器仅服务于最后连接的客户端。我使用链接动态列表来存储必要的客户端信息,当我可以列出当前连接的客户端时,如果我不使用部分聊天室代码,我连接的所有客户端都会被接受,并且一旦我使用聊天室代码部分,只有最后连接的客户端。

这是服务器代码:

 while(1) {
        fd_set reads;
        reads = master;

        //The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O
        if (select(max_socket+1, &reads, 0, 0, 0) < 0) {
            fprintf(stderr, "select() failed. (%d)\n", GETSOCKETERRNO());
            return 1;
        }

        SOCKET i;
        //Loop through each possible socket 
        for(i = 1; i <= max_socket; ++i) {
            if (FD_ISSET(i, &reads)) {

                //If socket_listen, create TCP connection of accept() function
                 if (i == socket_listen) {
                    //
                    client_info = create_client();
                    client_info->client_len = sizeof(client_info->client_address);
                    client_info->sock_fd = accept(socket_listen,
                            (struct sockaddr*) &client_info->client_address,
                            &client_info->client_len);

                    if (!ISVALIDSOCKET(client_info->sock_fd)) {
                        fprintf(stderr, "accept() failed. (%d)\n",
                                GETSOCKETERRNO());
                        return 1;
                    }

                    FD_SET(client_info->sock_fd, &master);
                    if (client_info->sock_fd > max_socket)
                        max_socket = client_info->sock_fd;
                
                    //Prints the client address using the getnameinfo() function
                    getnameinfo((struct sockaddr*)&client_info->client_address,
                            client_info->client_len,
                            client_info->address_buffer, 
                            100, 0, 0,
                            NI_NUMERICHOST);
                    printf("New connection %s\n", client_info->address_buffer);
                    

                    printf("\nWaiting for succeses Salt handshake...\n");

                    //Salt handshake 
                    salt_hndshk(client_info);

                    //Insert client to the list of clients
                    insert(p_list, client_info);

                    //List of clients connected to the server with a successful Salt handshake       
                    listing_clients(p_list);
      
                } else {
                    
                    memset(rx_buffer, 0, sizeof(hndsk_buffer));

                    //Search for clients by sockets and the is in the list
                    //the server decrypts the data from the client

                    CLIENT *client_decrypt = create_client();

                    client_decrypt = search_client(p_list, i);

                    ret_msg = salt_read_begin_pom(&client_decrypt->channel, rx_buffer, 
                                       sizeof(rx_buffer), &msg_in, pom_buffer, &decrypt_size);
                       
                    //Check if SALT_ERROR from message
                   if(ret_msg == SALT_ERROR) {
                        printf("\tThe client disconnects from the server.\n");
                        printf("\tThe server has closed him socket\n");
                        realese_client(p_list, client_decrypt);
                        FD_CLR(i, &master);
                        CLOSESOCKET(i);
                        continue;
                    }

                    //Freeing client memory
                    free(client_decrypt);
                }

            //Chat room service 
                
                SOCKET j;
                    for(j = 1; j <= max_socket; ++j){
                        if(FD_ISSET(j, &master)){
                            if (j == socket_listen || j == i){
                                continue;

                            } else {
                                memset(rx_buffer, 0, sizeof(hndsk_buffer));

                                //Search for clients by sockets and the is in the list
                                CLIENT *client_encrypt = create_client();
                                client_encrypt = search_client(p_list, j);

                                //Prepare data before send
                                salt_write_begin(tx_buffer, sizeof(tx_buffer), &msg_out);

                                //Copy clear text message to be encrypted to next encrypted package
                                salt_write_next(&msg_out, (uint8_t * )pom_buffer, decrypt_size);

                                //Wrapping, creating encrpted messages
                                salt_write_execute(&client_encrypt->channel, &msg_out, false);

                                //Freeing client memory
                                free(client_encrypt);
                            }
                            
                        } //if(FD_ISSET(j, &master)
                    } //for(j = 1; j <= max_socket; ++j)
                   
            //Finish chat room service

            } //if FD_ISSET
        } //for i to max_socket
    }

此链接上有应用程序的链接:

tcp_salt

【问题讨论】:

  • Stack Overflow 不是一个完整的调试服务。倾倒数百行代码并期望有人为您调试它是不合理的。您需要将代码简化为完整的minimal reproducible example。此外,生成一个最小的示例本身就是一种标准的调试技术,甚至可以帮助您自己找到问题。
  • 我已经剪了
  • 请阅读链接:minimal reproducible example。代码需要简洁而完整

标签: c windows sockets winsock2


【解决方案1】:

您的两个内部 for 循环都有逻辑错误。

在读取/写入非侦听客户端套接字时,根本不要调用create_client(),您正在使用它造成内存泄漏:

CLIENT *client_decrypt = create_client();
client_decrypt = search_client(...); // <-- LEAK!
CLIENT *client_encrypt = create_client();
client_encrypt = search_client(...); // <-- LEAK!

仅当您 accept() 成为新客户时才致电 create_client()。并且不要在您读取/写入的任何CLIENT 上调用free()。仅当您从 p_list 中删除 CLIENT 时才调用它。

您在每次循环迭代时都在破坏您的 p_list,留下一堆指向无效 CLIENTs 的悬空指针。

另外,您编写的代码不会检查错误以断开连接和删除死客户端。

试试这样的:

while(1) {
    fd_set reads;
    reads = master;

    //The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O
    if (select(max_socket+1, &reads, 0, 0, 0) < 0) {
        fprintf(stderr, "select() failed. (%d)\n", GETSOCKETERRNO());
        return 1;
    }

    //Loop through each possible socket 
    for(SOCKET i = 1; i <= max_socket; ++i) {
        if (!FD_ISSET(i, &master)) {
            continue;
        }
        if (FD_ISSET(i, &reads)) {

            //If socket_listen, create TCP connection of accept() function
            if (i == socket_listen) {
                //
                CLIENT *client_info = create_client();
                client_info->client_len = sizeof(client_info->client_address);
                client_info->sock_fd = accept(socket_listen,
                        (struct sockaddr*) &client_info->client_address,
                        &client_info->client_len);

                if (!ISVALIDSOCKET(client_info->sock_fd)) {
                    fprintf(stderr, "accept() failed. (%d)\n",
                            GETSOCKETERRNO());
                    return 1;
                }

                FD_SET(client_info->sock_fd, &master);
                if (client_info->sock_fd > max_socket)
                    max_socket = client_info->sock_fd;
            
                //Prints the client address using the getnameinfo() function
                getnameinfo((struct sockaddr*)&client_info->client_address,
                        client_info->client_len,
                        client_info->address_buffer, 
                        100, 0, 0,
                        NI_NUMERICHOST);
                printf("New connection %s\n", client_info->address_buffer);

                printf("\nWaiting for succesful Salt handshake...\n");

                //Salt handshake 
                salt_hndshk(client_info);

                //Insert client to the list of clients
                insert(p_list, client_info);

                //List of clients connected to the server with a successful Salt handshake       
                listing_clients(p_list);

                continue;
            }
                
            memset(rx_buffer, 0, sizeof(rx_buffer));

            //Search for clients by sockets and the is in the list
            //the server decrypts the data from the client

            CLIENT *client_decrypt = search_client(p_list, i);

            ret_msg = salt_read_begin_pom(&client_decrypt->channel, rx_buffer, 
                                   sizeof(rx_buffer), &msg_in, pom_buffer, &decrypt_size);
                   
            //Check if SALT_ERROR from message
            if (ret_msg == SALT_ERROR) {
                printf("\tThe client disconnects from the server.\n");
                printf("\tThe server has closed his socket\n");
                release_client(p_list, client_decrypt);
                free(client_decrypt);
                CLOSESOCKET(i);
                FD_CLR(i, &master);
                continue;
            }

            //Chat room service 
            for(SOCKET j = 1; j <= max_socket; ++j){
                if (!FD_ISSET(j, &master) || j == socket_listen || j == i){
                    continue;
                }

                memset(rx_buffer, 0, sizeof(rx_buffer));

                //Search for clients by sockets and the is in the list
                CLIENT *client_encrypt = search_client(p_list, j);

                //Prepare data before send
                ret_msg = salt_write_begin(tx_buffer, sizeof(tx_buffer), &msg_out);

                //Copy clear text message to be encrypted to next encrypted package
                if (ret_msg != SALT_ERROR)
                    ret_msg = salt_write_next(&msg_out, (uint8_t * )pom_buffer, decrypt_size);

                //Wrapping, creating encrpted messages
                if (ret_msg != SALT_ERROR
                    ret_msg = salt_write_execute(&client_encrypt->channel, &msg_out, false);

                //Check if SALT_ERROR from message
                if (ret_msg == SALT_ERROR) {
                    printf("\tThe client disconnects from the server.\n");
                    printf("\tThe server has closed his socket\n");
                    release_client(p_list, client_decrypt);
                    free(client_decrypt);
                    CLOSESOCKET(i);
                    FD_CLR(i, &master);
                    continue;
                }
            }
        } 
    }
}

【讨论】:

  • 你是对的。我已经重新设计了代码并且它可以正常工作。我还在学习用 C 编程,你可以看到我还没有很好的逻辑,我需要编程、编程和思考。
  • 我创建了 realese_client () 函数来删除死客户。
猜你喜欢
  • 2018-03-22
  • 2019-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-30
  • 1970-01-01
  • 2014-08-11
相关资源
最近更新 更多