使用套接字编程的聊天室 select() - winsock - C
Chat room using socket programming with select() - winsock - C
我尝试创建一个服务器-客户端应用程序,其中服务器为连接到服务器的所有客户端提供聊天服务。服务器和客户端使用加密算法和协议来保护通过网络传输的数据。我不知道为什么聊天代码不能正常工作。
我使用select()
功能同时操作多个抽屉。如果当多个客户端连接到服务器并向服务器发送数据并且它获得所有内容时我只使用一段代码,那很好,但是一旦我尝试编写一段将成为聊天功能的代码,即使多个客户端连接,服务器只为最后一个连接的客户端服务。我使用一个 link 动态列表来存储必要的客户端信息,当我可以列出当前连接的客户端时,如果我不使用部分聊天室代码,我连接的所有客户端都会被接受,并尽快因为我使用聊天室代码部分,只有最后一个连接的客户端。
这是服务器代码:
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
}
这个 link 上有一个 link 的应用程序:
你的两个内部 for
循环都有逻辑错误。
当读取 from/writing 到非监听客户端套接字时,根本不要调用 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()
。并且不要在您阅读 from/write 的任何 CLIENT
上调用 free()
。仅当您从 p_list
.
中删除 CLIENT
时才调用它
你在每次循环迭代中破坏你的 p_list
,留下一堆指向无效 CLIENT
s 的悬空指针。
此外,您编写的代码没有检查错误以断开连接和删除死客户端。
试试这样的东西:
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;
}
}
}
}
}
我尝试创建一个服务器-客户端应用程序,其中服务器为连接到服务器的所有客户端提供聊天服务。服务器和客户端使用加密算法和协议来保护通过网络传输的数据。我不知道为什么聊天代码不能正常工作。
我使用select()
功能同时操作多个抽屉。如果当多个客户端连接到服务器并向服务器发送数据并且它获得所有内容时我只使用一段代码,那很好,但是一旦我尝试编写一段将成为聊天功能的代码,即使多个客户端连接,服务器只为最后一个连接的客户端服务。我使用一个 link 动态列表来存储必要的客户端信息,当我可以列出当前连接的客户端时,如果我不使用部分聊天室代码,我连接的所有客户端都会被接受,并尽快因为我使用聊天室代码部分,只有最后一个连接的客户端。
这是服务器代码:
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
}
这个 link 上有一个 link 的应用程序:
你的两个内部 for
循环都有逻辑错误。
当读取 from/writing 到非监听客户端套接字时,根本不要调用 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()
。并且不要在您阅读 from/write 的任何 CLIENT
上调用 free()
。仅当您从 p_list
.
CLIENT
时才调用它
你在每次循环迭代中破坏你的 p_list
,留下一堆指向无效 CLIENT
s 的悬空指针。
此外,您编写的代码没有检查错误以断开连接和删除死客户端。
试试这样的东西:
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;
}
}
}
}
}