我正在使用 UDP 编写私人聊天,但在实现注册服务时遇到了一些问题。当客户端写入他要使用的ID和密码时,服务器会检查ID是否已经存在(ID和密码保存在一个文件中),如果不存在,客户端可以开始与其他用户聊天(我仍然没有实现这部分),如果 ID 已经在使用中,我想告诉用户选择另一个 ID。在这种情况下,我使用 sendto 向客户端发送消息“ID 已在使用中”,但 recvfrom 有时会接收其他消息。正是当用户第一次选择了错误的 ID 时,他收到了正确的消息,然后,如果他再次选择一个已经在使用的 ID,从他从服务器收到的消息中,他读取了他尝试选择的 ID,而不是错误信息。最奇怪的是,在第三次错误尝试时,错误消息是正确的。消息和相关函数在名为 utils.h 的文件中定义
typedef struct Message{
int type; //Message's type: 0->registration; 1->login; 2->message; 3->ack
char* dest;
char* mitt; //source
char msg[1024];
}Message;
//createMsg is used to fill message struct
int createMsg(int type, char* dest, char* mitt, char * msg, struct Message* message){ //char msg[1024]
//printf("Sono in createMsg\n");
memset(message,0,sizeof(*message));
message->type = type;
message->dest = dest;
message->mitt = mitt;
//printf("msg to write: %s\n",msg);
strcpy(message->msg,msg);
//printf("msg written: %s\n",message->msg);
}
//print messages
void printMsg(struct Message* message){
if(strlen(message->msg)!=0) printf("type: %d, dest: %s, mitt:%s, msg: %s\n",message->type,message->dest,message->mitt,message->msg);
printf("dim msg tot: %lu\n",sizeof(*message));
printf("type dim: %lu, dest dim: %lu, mitt dim:%lu, msg dim: %lu, dim tot msg: %lu\n",sizeof(message->type),sizeof(message->dest),sizeof(message->mitt),strlen(message->msg),sizeof(message->msg));
}
//Set message's field to zero
void setToZeroMsg(struct Message* message){
message->type = 0;
message->dest = NULL;
message->mitt = NULL;
int i=0;
for(i=0;i<strlen(message->msg);i++){
message->msg[i]=0;
}
//memset(message->msg,0,strlen(message->msg));
}
这是服务器:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <arpa/inet.h> // htons()
#include <netinet/in.h> // struct sockaddr_in
#include <sys/socket.h>
//#include "utente.h"
#include "utils.h"
#define MAX_CONNECTIONS 5
#define MAX_MESSAGE 1024
int main(){
int sockfd, ret;
char buffer[MAX_MESSAGE];
size_t buf_len = sizeof(buffer);
struct sockaddr_in server_addr, client_addr;
int serv_addr_len = sizeof(server_addr);
int client_addr_len = sizeof(client_addr);
//socket file descriptor
sockfd = socket(AF_INET, SOCK_DGRAM,0);
if(sockfd < 0){
perror("Errore nel creare la socket!");
exit(EXIT_FAILURE);
}
//Setting structure's content to zero
memset(&server_addr,0,serv_addr_len);
memset(&client_addr,0,client_addr_len);
//Server addr
server_addr.sin_family = AF_INET; //IPv4
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(5500);
//Bind
ret = bind(sockfd, (const struct sockaddr *)&server_addr, serv_addr_len);
if(ret < 0){
perror("Errore nella bind!");
exit(EXIT_FAILURE);
}
struct Message* newmsg = (struct Message*)malloc(sizeof(struct Message));
//Start comunication with clients
//int num_clients = 0;
while(1){
setToZeroMsg(newmsg); //Cancel previous messages
printf("waiting for a message from a client...\n");
//Receiving a message
ret = recvfrom(sockfd, newmsg, sizeof(*newmsg), 0, (struct sockaddr*)&client_addr, &client_addr_len);
if(ret == -1){
perror("Errore nella recvfrom!");
}
printMsg(newmsg);
int type = newmsg->type;
if(type == 0){ //Type==0 -> register request
printf("register request\n");
int newID = 0; //if is set to -1 the ID already exist
size_t msg_len=sizeof(newmsg->msg);
FILE* file = fopen("utenti.txt","r+");
if(file==NULL){
perror("Errore nell'apertura del file");
}
char* newuser = strcpy(newser,newmsg->msg);
strtok(newuser,";");
int size_newuser = sizeof(newuser);
while(1){
char* res = fgets(buffer,32,file);
if(res==NULL) break;
//printf("%s",buffer);
char* user = strtok(buffer,";");
//printf("%s\n",user);
int size_user = sizeof(user);
int size = (size_newuser > size_user)? size_newuser : size_user;
if(strncmp(newuser,user,size) == 0){
//printf("This name is already in use!\n");
/*memset(newmsg,0,sizeof(Message));
newmsg->type = 3;
newmsg->dest = NULL;
strcpy(newmsg->msg, "This ID is already taken!\n");
printf("mess: %s",newmsg->msg);*/
//Error message to client
creaMsg(3, NULL, NULL, "This ID is already taken!\n", newmsg); //buffer
printMsg(newmsg);
printf("msg: %s\n",newmsg->msg);
//ret = sendto(sockfd, (struct Message*)newmsg, sizeof(*newmsg), 0, (struct sockaddr*)&client_addr, (socklen_t) client_addr_len);
ret = sendto(sockfd,newmsg->msg,sizeof(newmsg->msg),0,(struct sockaddr*)&client_addr, (socklen_t) client_addr_len );
if(ret == -1){
perror("Errore nella sendto!");
exit(EXIT_FAILURE);
}
newID = -1;
break;
}
}
if(newID != -1){
ret = fprintf(file,newmsg->msg,msg_len);
if(ret == -1){
perror("Errore nella scrittura su file");
}
}
if(fclose(file) == -1) perror("Errore nella chiusura del file");
}
else if(type==1){
}
else if(type==2){
}
//This part is not implemented yet
/*ret = sendto(sockfd, buffer, buf_len,0,(const struct sockaddr*)&client_addr, (socklen_t) client_addr_len);
if(ret == -1){
perror("Errore nella sendto!");
}*/
}
close(sockfd);
return 0;
}
这是客户端文件:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <arpa/inet.h> // htons()
#include <netinet/in.h> // struct sockaddr_in
#include <sys/socket.h>
//#include "utente.h"
#include "utils.h"
#define MAX_MESSAGE 1024
#define quit_command "QUIT"
int main(){
int sockfd, ret;
struct sockaddr_in server_addr;
int serv_addr_len = sizeof(server_addr);
char buffer[MAX_MESSAGE];
size_t buf_len = sizeof(buffer);
size_t msg_len;
int quit_command_len = strlen(quit_command);
//Message struct
struct Message message = {};
setToZeroMsg(&message);
//socket file descriptor
sockfd = socket(AF_INET, SOCK_DGRAM,0);
if(sockfd < 0){
perror("Errore nel creare la socket!");
exit(EXIT_FAILURE);
}
//Structures to zero
memset(&server_addr,0,serv_addr_len);
//Initialize server_addr
server_addr.sin_family = AF_INET; //IPv4
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(5500);
printf("Welcome in Private Chat!\n");
while(1){
char buf_credentials[32];
memset(buf_credentials,0,sizeof(buf_credentials));
memset(buffer,0,sizeof(buffer));
//setToZeroMsg(&message);
printf("If you want to register write 'r', to login type 'l':\n");
if (fgets(buffer, MAX_MESSAGE, stdin) != (char*)buffer) {
fprintf(stderr, "Error while reading from stdin, exiting...\n");
exit(EXIT_FAILURE);
}
if(!memcmp(buffer, "l", 1)){
printf("not implemented yet");
break;
}
else if(!memcmp(buffer,"r",1)){
printf("To register choose an ID and a password.\n");
printf("ID:\n");
if (fgets(buffer, 15, stdin) != (char*)buffer) {
fprintf(stderr, "Error while reading from stdin, exiting...\n");
exit(EXIT_FAILURE);
}
//printf("%s\n",buffer);
strncpy(buf_credentials,buffer,strlen(buffer)-1);
strcat(buf_credentials,";");
printf("Inserire password:\n");
if (fgets(buffer, 15, stdin) != (char*)buffer) {
fprintf(stderr, "Error while reading from stdin, exiting...\n");
exit(EXIT_FAILURE);
}
strcat(buf_credentials,buffer);
printf("utente+pass: %s",buf_credentials);
//Message struct
/*
struct Message message = {
};*/
setToZeroMsg(&message);
strcpy(message.msg, buf_credentials);
//send ID e password to server
ret = sendto(sockfd, (struct Message*)&message, sizeof(message), 0, (struct sockaddr*)&server_addr, (socklen_t) serv_addr_len);
if(ret == -1){
perror("Errore nella sendto!");
continue;
}
setToZeroMsg(&message);
printf("msg: %s\n",message.msg);
//ret = recvfrom(sockfd, (struct Message*)&message, sizeof(message), 0, (struct sockaddr*)&server_addr, (socklen_t*)&serv_addr_len);
ret = recvfrom(sockfd, message.msg, sizeof(message.msg), 0, (struct sockaddr*)&server_addr, (socklen_t*)&serv_addr_len);
printMsg(&message);
printf("msg: %s\n",message.msg);
if(ret == -1){
perror("Errore nella recvfrom!");
}
continue;
}
else{ //User didn't type 'r' or 'l'
printf("Wrong, you have to type 'r' o 'l'\n");
}
memset(buffer,0,MAX_MESSAGE);
}
//Chatting with server
while(1){
memset(buffer,0,MAX_MESSAGE);
printf("Type the message for the server, QUIT to leave the chat:\n");
if (fgets(buffer, MAX_MESSAGE, stdin) != (char*)buffer) {
fprintf(stderr, "Error while reading from stdin, exiting...\n");
exit(EXIT_FAILURE);
}
printf("You wrote: %s\n", buffer);
msg_len=strlen(buffer);
//Send Message
ret = sendto(sockfd, buffer, MAX_MESSAGE, 0, (struct sockaddr*)&server_addr, (socklen_t) serv_addr_len);
if(ret == -1){
perror("Errore nella sendto!");
continue;
}
//Server wrote back
memset(buffer,0,MAX_MESSAGE);
ret = recvfrom(sockfd, buffer, buf_len, 0, (struct sockaddr*)&server_addr, &serv_addr_len);
if(ret == -1){
perror("Errore nella recvfrom!");
}
printf("Message received from server: %s\n",buffer);
//Controlla se il messaggio ricevuto è QUIT
if (msg_len == quit_command_len+1 && !memcmp(buffer, quit_command, quit_command_len)) break;
}
printf("Closing connection!\n");
close(sockfd);
return 0;
}
ID和密码存放在文件“utenti.txt”中,用分号隔开,依次为:ID1;password1 ID2;password2 ... 希望我已经把我的问题搞清楚了,谢谢你的帮助!