C UDP 客户端-服务器卡住通信
C UDP Client-Server stuck comunication
为了我的一门大学课程,我必须用 C 语言实现一个使用 UDP Client-Server 的简单聊天程序,这是老师发给我们的描述:
You should develop a private chat environment to exchange
text messages between hosts. Message encryption is optional but not required.
The project should be composed by 2 main modules:
- Server: receives and stores each message in a sort of chat database.
A very naive database would consist in a
User
struct,
that contains everything (login credentials, chats, ...).
Each Chat
structure contains the actual messages.
- Client: provides a very simple interface to select a
receiver for our message and then write the content of the message.
Login is required. If a receiver is not subscribed returns an error.
The project should be tested with at least 3 users :)
我设法实现了身份验证阶段,但是在尝试实现消息交换阶段时我遇到了困难。当我尝试将在线用户的 linked_list 从服务器发送到客户端时,执行会冻结,不仅如此,它还会产生一些随机行为,有时在第一次尝试时卡住,有时在第二次尝试时卡住,依此类推。我还注意到,当我在客户端中引入一个单独的线程来处理消息的收件箱时,情况变得更糟,比以前更容易卡住。我会将负责发送和接收在线用户的函数代码以及 link 添加到我的 git 存储库中,如果您需要,可以在其中找到完整代码。
这是服务器中的代码:
void Send_list(ListHead* head, int sockfd,struct sockaddr_in cliaddr, int size){
int written_bytes;
int len = sizeof(cliaddr);
char username[50];
if(head->size == 1){
return;
}
ListItem* aux = head->first;
for(int i=0;i<size;i++){
memset(username,0,sizeof(username));
UserListItem* uitem = (UserListItem*) aux;
strcpy(username,uitem->user.username);
do{
written_bytes = 0;
written_bytes = sendto(sockfd,(const char *)username,strlen(username),0,(const struct sockaddr*)&cliaddr,len);
}while(written_bytes != strlen(username));
printf("\nusername mandato: %s",username);
printf("\n");
if(aux->next){
aux = aux->next;
}
}
}
这是客户端中的代码:
int recv_list(int sockfd,struct sockaddr_in servaddr, ListHead* head,int size, char username[50]){
char onuser[50];
int len = sizeof(servaddr);
int read_bytes;
int idx = 1;
if(size == 1){
return 1;
}
for(int i=0;i<size;i++){
read_bytes = recvfrom(sockfd,(char *)onuser,sizeof(onuser),0,(struct sockaddr*)&servaddr,&len );
onuser[read_bytes] = '[=11=]';
if(List_find_by_username(head,onuser) == 0 || strcmp(onuser,username)){
UserListItem* uitem = malloc(sizeof(UserListItem));
memset(uitem,0,sizeof(UserListItem));
UList_init(uitem,onuser);
uitem->idx = idx++;
ListItem* result = List_insert(head,head->last,(ListItem*)uitem);
assert(result);
}
memset(onuser,0,sizeof(onuser));
}
UserList_print(head);
return 0;
}
这是我的 git 回购的 link:https://gitlab.com/antonio_ciprani/so-progetto-20_21
我在基于 Ubuntu 的系统中工作。
我真的希望有人能帮助我,因为这让我发疯:(
I also noticed that when I introduced a separated thread in the Client to handle the inbox of messages the situation got worst getting stuck more often then before.
确实,在您的程序中使用线程弊大于利。特别是在 main
循环中你 pthread_create
一个新的 reciving
线程,它与主线程竞争传入消息,扰乱了 recv_list
的过程。最好不要在你的项目中使用线程 - 你会避免很多问题。
我们先写两个辅助函数:
void store(thread_args_t *targs, Message *msg)
{ // code taken from your function "reciving"
if (!strcmp(targs->user->username, msg->reciver))
{
Inbox *mitem = malloc(sizeof (Inbox));
strcpy(mitem->msg.sender, msg->sender);
strcpy(mitem->msg.reciver, msg->reciver);
strcpy(mitem->msg.data, msg->data);
ListItem *result =
List_insert(targs->inbox, targs->inbox->last, (ListItem *)mitem);
assert(result);
}
}
char *input(thread_args_t *targs)
{ // wait for user input and store incoming messages
fflush(stdout);
fd_set fds, fdr;
FD_ZERO(&fds);
FD_SET(0, &fds); // add STDIN to the fd set
FD_SET(targs->sockfd, &fds); // add socket to the fd set
for (; ; )
{
if (fdr = fds, select(targs->sockfd+1, &fdr, NULL, NULL, NULL) < 0)
perror("select"), exit(1);
if (FD_ISSET(0, &fdr))
{ // this is the user's input
static char data[256];
if (!fgets(data, sizeof data, stdin)) return NULL; // no more user input
data[strlen(data)-1] = '[=11=]';
return data;
}
// if no user input, then there's a message
Message msg;
socklen_t len = sizeof targs->servaddr;
if (recvfrom(targs->sockfd, &msg, sizeof msg, 0,
(struct sockaddr *)targs->servaddr, &len) < 0)
perror("recvfrom"), exit(1);
store(targs, &msg);
}
}
现在您可以将 main
中的主循环体替换为:
int ret, op;
printf("\nPlease choose an option: ");
printf("\n1.Send a message!");
printf("\n2.Incoming messages!");
printf("\n3.Logout!");
printf("\nYour choice:\t");
char *str = input(&targs);
sscanf(str, "%d", &ret);
printf("\nqua bro?\n");
if (ret == 1)
{
printf("\nqua loz?\n");
int res, read_bytes, size, id;
op = 3;
socklen_t len = sizeof servaddr;
sendto(sockfd, &op, sizeof op, MSG_CONFIRM,
(struct sockaddr *)&servaddr, len);
printf("\nqua shiiis?\n");
// We cannot preclude that a message arrives here,
// therefore we must handle that case.
Message msg;
while ((read_bytes = recvfrom(sockfd, &msg, sizeof msg, 0,
(struct sockaddr *)&servaddr,
&len)) == sizeof msg)
store(&targs, &msg);
if (read_bytes == -1) perror("recvfrom"), exit(1);
size = *(int *)&msg;
printf("\nqua ci siamo?\n");
res = recv_list(sockfd, servaddr, &on_list, size, user.username);
printf("\nqua?\n");
if (res == 0)
{
printf("\nChoose whom you want to send a message to");
printf("\nYour choice:\t");
str = input(&targs);
sscanf(str, "%d", &id);
printf("\nWrite the message you want to send:\n");
str = input(&targs);
Init_msg(&msg, str, id, &on_list, user.username);
int written_bytes = sendto(sockfd, &msg, sizeof msg, MSG_CONFIRM,
(struct sockaddr *)&servaddr, len);
if (written_bytes == -1) perror("sendto"), exit(1);
// With your present server, a message cannot arrive here, but you
// possibly will want to change that, so let's handle it already.
while ((read_bytes = recvfrom(sockfd, &msg, sizeof msg, 0,
(struct sockaddr *)&servaddr,
&len)) == sizeof msg)
store(&targs, &msg);
if (read_bytes == -1) perror("recvfrom"), exit(1);
int sent = *(int *)&msg;
if (sent == 0) printf("\nMessage sent!");
else
if (sent == 1) printf("\nUser is offline :(");
}
else
if (res == 1) printf("\nNo online user :(");
}
else
if (ret == 2) Print_msg(&inbox);
您可能想要改进的下一件事是修改服务器函数 Forward_message
以便它允许在等待消息时接收来自另一个客户端的传入命令。
为了我的一门大学课程,我必须用 C 语言实现一个使用 UDP Client-Server 的简单聊天程序,这是老师发给我们的描述:
You should develop a private chat environment to exchange text messages between hosts. Message encryption is optional but not required. The project should be composed by 2 main modules:
- Server: receives and stores each message in a sort of chat database. A very naive database would consist in a
User
struct, that contains everything (login credentials, chats, ...). EachChat
structure contains the actual messages.- Client: provides a very simple interface to select a receiver for our message and then write the content of the message. Login is required. If a receiver is not subscribed returns an error.
The project should be tested with at least 3 users :)
我设法实现了身份验证阶段,但是在尝试实现消息交换阶段时我遇到了困难。当我尝试将在线用户的 linked_list 从服务器发送到客户端时,执行会冻结,不仅如此,它还会产生一些随机行为,有时在第一次尝试时卡住,有时在第二次尝试时卡住,依此类推。我还注意到,当我在客户端中引入一个单独的线程来处理消息的收件箱时,情况变得更糟,比以前更容易卡住。我会将负责发送和接收在线用户的函数代码以及 link 添加到我的 git 存储库中,如果您需要,可以在其中找到完整代码。
这是服务器中的代码:
void Send_list(ListHead* head, int sockfd,struct sockaddr_in cliaddr, int size){
int written_bytes;
int len = sizeof(cliaddr);
char username[50];
if(head->size == 1){
return;
}
ListItem* aux = head->first;
for(int i=0;i<size;i++){
memset(username,0,sizeof(username));
UserListItem* uitem = (UserListItem*) aux;
strcpy(username,uitem->user.username);
do{
written_bytes = 0;
written_bytes = sendto(sockfd,(const char *)username,strlen(username),0,(const struct sockaddr*)&cliaddr,len);
}while(written_bytes != strlen(username));
printf("\nusername mandato: %s",username);
printf("\n");
if(aux->next){
aux = aux->next;
}
}
}
这是客户端中的代码:
int recv_list(int sockfd,struct sockaddr_in servaddr, ListHead* head,int size, char username[50]){
char onuser[50];
int len = sizeof(servaddr);
int read_bytes;
int idx = 1;
if(size == 1){
return 1;
}
for(int i=0;i<size;i++){
read_bytes = recvfrom(sockfd,(char *)onuser,sizeof(onuser),0,(struct sockaddr*)&servaddr,&len );
onuser[read_bytes] = '[=11=]';
if(List_find_by_username(head,onuser) == 0 || strcmp(onuser,username)){
UserListItem* uitem = malloc(sizeof(UserListItem));
memset(uitem,0,sizeof(UserListItem));
UList_init(uitem,onuser);
uitem->idx = idx++;
ListItem* result = List_insert(head,head->last,(ListItem*)uitem);
assert(result);
}
memset(onuser,0,sizeof(onuser));
}
UserList_print(head);
return 0;
}
这是我的 git 回购的 link:https://gitlab.com/antonio_ciprani/so-progetto-20_21
我在基于 Ubuntu 的系统中工作。
我真的希望有人能帮助我,因为这让我发疯:(
I also noticed that when I introduced a separated thread in the Client to handle the inbox of messages the situation got worst getting stuck more often then before.
确实,在您的程序中使用线程弊大于利。特别是在 main
循环中你 pthread_create
一个新的 reciving
线程,它与主线程竞争传入消息,扰乱了 recv_list
的过程。最好不要在你的项目中使用线程 - 你会避免很多问题。
我们先写两个辅助函数:
void store(thread_args_t *targs, Message *msg)
{ // code taken from your function "reciving"
if (!strcmp(targs->user->username, msg->reciver))
{
Inbox *mitem = malloc(sizeof (Inbox));
strcpy(mitem->msg.sender, msg->sender);
strcpy(mitem->msg.reciver, msg->reciver);
strcpy(mitem->msg.data, msg->data);
ListItem *result =
List_insert(targs->inbox, targs->inbox->last, (ListItem *)mitem);
assert(result);
}
}
char *input(thread_args_t *targs)
{ // wait for user input and store incoming messages
fflush(stdout);
fd_set fds, fdr;
FD_ZERO(&fds);
FD_SET(0, &fds); // add STDIN to the fd set
FD_SET(targs->sockfd, &fds); // add socket to the fd set
for (; ; )
{
if (fdr = fds, select(targs->sockfd+1, &fdr, NULL, NULL, NULL) < 0)
perror("select"), exit(1);
if (FD_ISSET(0, &fdr))
{ // this is the user's input
static char data[256];
if (!fgets(data, sizeof data, stdin)) return NULL; // no more user input
data[strlen(data)-1] = '[=11=]';
return data;
}
// if no user input, then there's a message
Message msg;
socklen_t len = sizeof targs->servaddr;
if (recvfrom(targs->sockfd, &msg, sizeof msg, 0,
(struct sockaddr *)targs->servaddr, &len) < 0)
perror("recvfrom"), exit(1);
store(targs, &msg);
}
}
现在您可以将 main
中的主循环体替换为:
int ret, op;
printf("\nPlease choose an option: ");
printf("\n1.Send a message!");
printf("\n2.Incoming messages!");
printf("\n3.Logout!");
printf("\nYour choice:\t");
char *str = input(&targs);
sscanf(str, "%d", &ret);
printf("\nqua bro?\n");
if (ret == 1)
{
printf("\nqua loz?\n");
int res, read_bytes, size, id;
op = 3;
socklen_t len = sizeof servaddr;
sendto(sockfd, &op, sizeof op, MSG_CONFIRM,
(struct sockaddr *)&servaddr, len);
printf("\nqua shiiis?\n");
// We cannot preclude that a message arrives here,
// therefore we must handle that case.
Message msg;
while ((read_bytes = recvfrom(sockfd, &msg, sizeof msg, 0,
(struct sockaddr *)&servaddr,
&len)) == sizeof msg)
store(&targs, &msg);
if (read_bytes == -1) perror("recvfrom"), exit(1);
size = *(int *)&msg;
printf("\nqua ci siamo?\n");
res = recv_list(sockfd, servaddr, &on_list, size, user.username);
printf("\nqua?\n");
if (res == 0)
{
printf("\nChoose whom you want to send a message to");
printf("\nYour choice:\t");
str = input(&targs);
sscanf(str, "%d", &id);
printf("\nWrite the message you want to send:\n");
str = input(&targs);
Init_msg(&msg, str, id, &on_list, user.username);
int written_bytes = sendto(sockfd, &msg, sizeof msg, MSG_CONFIRM,
(struct sockaddr *)&servaddr, len);
if (written_bytes == -1) perror("sendto"), exit(1);
// With your present server, a message cannot arrive here, but you
// possibly will want to change that, so let's handle it already.
while ((read_bytes = recvfrom(sockfd, &msg, sizeof msg, 0,
(struct sockaddr *)&servaddr,
&len)) == sizeof msg)
store(&targs, &msg);
if (read_bytes == -1) perror("recvfrom"), exit(1);
int sent = *(int *)&msg;
if (sent == 0) printf("\nMessage sent!");
else
if (sent == 1) printf("\nUser is offline :(");
}
else
if (res == 1) printf("\nNo online user :(");
}
else
if (ret == 2) Print_msg(&inbox);
您可能想要改进的下一件事是修改服务器函数 Forward_message
以便它允许在等待消息时接收来自另一个客户端的传入命令。