我正在学习 C 语言的多线程,我正在尝试构建一个简单的聊天应用程序。这个想法是客户端连接到中央服务器,服务器将客户端发送的消息转发给其他客户端,就像群聊一样。
除了接收客户的消息之外,我几乎完成了所有事情。我的 TCP 服务器很好地接收消息,将适当的参数(例如客户端套接字文件描述符)和接收到的缓冲区传递给重新发送所有内容的相应函数。但客户只是没有收到任何东西。我不知道该怎么办。
这里是我的应用程序的一些相关功能,但您也可以在这个存储库中看到我的应用程序的当前阶段。
/* sock_server.c */
#include "sock_util.h"
#define SRV_ADDR ""
#define SRV_PORT 2357
#define LST_BACKLOG 2
int main() {
int server_sfd;
if ((server_sfd = runTCPServer(SRV_ADDR, SRV_PORT, LST_BACKLOG)) < 0) {
return -1;
}
runThreadConnections(server_sfd);
shutdown(server_sfd, SHUT_RDWR);
LOG_INFO_MESSAGE("Application closed.\n");
return 0;
}
/* sock_client.c */
#include "sock_util.h"
#define SRV_ADDR "127.0.0.1"
#define SRV_PORT 2357
#define NEWCONN_CMD "//newconn\n"
#define ENDCONN_CMD "//endconn\n"
void *threadMessageReceiver(void *sfd) {
char buffer[1024];
int client_sfd = *(int *)sfd;
ssize_t char_count;
free(sfd);
LOG_INFO_MESSAGE("Receiving messages. [SFD %d].\n", client_sfd);
while (true) {
if ((char_count = recv(client_sfd, buffer, strlen(buffer), 0)) < 0) {
printf("Error.\n");
return NULL;
}
if (char_count == 0) {
printf("Received nothing.\n");
continue;
}
buffer[char_count] = 0; // Add NULL character to end the string.
LOG_INFO_MESSAGE("Message received: [ %s ]\n", buffer);
}
LOG_INFO_MESSAGE("No longer receiving messages.\n");
return 0;
}
int runThreadMessageReceiver(int *client_sfd) {
runInThread(threadMessageReceiver, client_sfd, sizeof(client_sfd));
return 0;
}
int main() {
int sfd = createTCPv4Socket();
struct sockaddr_in *addr = createIPv4Sockaddr(SRV_ADDR, SRV_PORT);
// connect to the address using the socket.
if (connect(sfd, (struct sockaddr *) addr, sizeof(*addr)) == -1) {
printf("- something went wrong: %s", strerror(errno));
return -1;
}
// receive messages assycronously
// runThreadMessageReceiver(&sfd);
// beginning of chat application
char *line = NULL;
size_t line_s = 0;
ssize_t char_count;
while (true) {
if ((char_count = getline(&line, &line_s, stdin)) < 0) {
return -1;
}
fflush(stdout);
// finish conversation.
if (strlen(line) == 1) {
send(sfd, ENDCONN_CMD, strlen(ENDCONN_CMD), 0);
break;
};
if (send(sfd, line, char_count, 0) == -1) {
return -1;
}
}
close(sfd);
return 0;
}
sock_util.c
中的相关功能
int runInThread(void *(*routine)(void *), void *routine_arg, size_t arg_size) {
/*
Helper function to run any function with any argument in a sepate thread.
The function will return -1 on error or the thread id on success.
*/
// Allocate memory for the thread argument
void *arg_copy = malloc(arg_size);
if (!arg_copy) {
LOG_ERROR_MESSAGE("Error with malloc(): %s.\n", strerror(errno));
return -1;
}
// Copy the content of the argument into the allocated memory
memcpy(arg_copy, routine_arg, arg_size);
pthread_t id;
if(pthread_create(&id, NULL, routine, arg_copy) < 0) {
LOG_ERROR_MESSAGE("Error with pthread(): %s.\n", strerror(errno));
return -1;
}
LOG_DEBUG_MESSAGE("Thread created succesfully: ID %02x.\n", id);
return id;
}
int runThreadConnections(int sfd_srv) {
/*
Start running each client connection in a separate thread.
Client messages are handled within a thread in each client's
corresponding thread.
*/
while (true) {
struct acceptedConn *client_conn = acceptNewConn(sfd_srv);
if (client_conn->error < 0) {
printf("- Error with AcceptNewConn(): %s.\n", strerror(errno));
return -1;
}
LOG_DEBUG_MESSAGE("Client connection accepted: SocketFD %d.\n", client_conn->sfd_client);
int sfd_client = client_conn->sfd_client;
int *sfd_client_ptr = &sfd_client;
connections_list[connections_count++] = *client_conn;
LOG_DEBUG_MESSAGE("Connections list updated.\n");
// Run threadNewConn() from a new thread pthread_create()
LOG_DEBUG_MESSAGE("Calling runInThead() from runThreadConnections().\n");
runInThread(threadConnections, sfd_client_ptr, sizeof(sfd_client));
}
}
void *threadConnections(void *arg_sfd_client) {
int sfd_client = *(int *)arg_sfd_client;
free(arg_sfd_client);
listenConn(sfd_client);
close(sfd_client);
return NULL;
}
int listenConn(int sfd_client) {
/*
Process a connection by listening to a received accepted connection from `acceptedConn()` using
the `listen()^ function.
Loop indifinitely until the connection is closed by the client or an error arise.
*/
char buff_recv[1024];
ssize_t recv_content = 0;
while (true) {
//BUG Verify the recv_content errno case and when recv_content = 1 || 0.
if ( (recv_content = recv(sfd_client, buff_recv, 1024, 0)) < 0) {
if (errno == 104) { // if connection reset exit gracefully
LOG_DEBUG_MESSAGE("Client disconnected. ClientFD %d.\n", sfd_client);
break;
}
printf("- error with recv(): %s.\n", strerror(errno));
return -1;
}
if(recv_content == 1 || recv_content == 0) {
break;
}
buff_recv[recv_content - 1] = 0; // We assume all received messages end with a breakline \n.
LOG_DEBUG_MESSAGE("Client [SocketFD %d] message received: ", sfd_client);
printf("[ %s ]\n", buff_recv);
//TODO: Check for messages starting with "//" (Commands)
if (strstr(buff_recv, "//") == NULL) {
runThreadReply(buff_recv, sfd_client, 0);
}
}
return 0;
}
int runThreadReply(char* buffer, int sender_sfd, int receiver_sfd) {
/*
Send the sender_sfd received buffer to receiver_sfd.
if receiver_sfd is 0, the buffer is sent to every client connected with the server.
*/
LOG_DEBUG_MESSAGE("Calling runInThead() from runThreadReplies().\n");
for (int i = 0; i < connections_count; i++) {
int sfd_client = connections_list[i].sfd_client;
// if (sfd_client == sender_sfd) continue;
LOG_DEBUG_MESSAGE("Buffer from %d to %d.\n", sender_sfd, sfd_client);
struct recv_info *info = malloc(sizeof(struct recv_info));
info->recv_sfd = sfd_client;
info->buffer = buffer;
info->buffer_size = strlen(buffer);
info->flags = 0;
send(sfd_client, buffer, strlen(buffer), 0);
if (sfd_client == receiver_sfd || receiver_sfd == 0) {
// runInThread(threadReply, info, sizeof(struct recv_info));
}
}
return 0;
}
void *threadReply(void *recv_info) {
struct recv_info info = *(struct recv_info *)recv_info;
if (strlen(info.buffer) <= 0) {
send(info.recv_sfd, info.buffer, strlen(info.buffer), info.flags);
}
LOG_DEBUG_MESSAGE("Message %s sended to %d.\n", info.buffer, info.recv_sfd);
return NULL;
}
我尝试一步步记录所有内容,但我找不到逻辑中的错误。由于我不太了解多线程,这可能是我的线程应用程序中的一些竞争条件或一些管理失败,或者只是一个愚蠢的错误。我不知道。
原因是
recv
调用的第三个参数。它应该是缓冲区长度,但你使用了strlen(buffer)
,它始终是0
。这就是您的客户不断打印的原因 Received nothing.