我正在尝试实现多客户端聊天,使用基于tcp套接字的连接和线程来管理客户端和输入。然而,发送或接收的字符串的读取和写入没有正确执行,我无法弄清楚原因。
服务器:
#define IP "127.0.0.1"
#define PORT 9000
#define MAX_CONNECTION_Q 10
#define CLIENTS 10
#define BUFFSIZE 1024
#define NAMELEN 32
#define COMMANDS 16
#define MESSAGESIZE 256
#define LINENUM 512
struct chat{
char userlist[CLIENTS][NAMELEN];
char admin[NAMELEN];
char ID[NAMELEN];
char logg[512][216];
struct chat *next;
int pos;
};
struct client_info{
pthread_t client_ID;
int sockfd;
struct chat chats;
char user[NAMELEN];
struct client_info *next;
};
struct header{
struct client_info *fnode, *lnode;
}*client_head;
struct chat_head{
struct chat *fnode,*lnode;
}*chat_head;
int sockfd, clinfo;
struct client_info thread_info[CLIENTS];
struct header client_list;
pthread_mutex_t mutex;
void list_init() {
client_head = malloc(sizeof(struct header));
chat_head = malloc(sizeof(struct chat_head));
client_head->fnode = NULL;
chat_head->fnode = NULL;
}
int compare(int a, int b){
return (a-b);
}
void chat_insert(struct chat *node){
if(chat_head->fnode == NULL){
chat_head->fnode = node;
chat_head->lnode = node;
}
else{
chat_head->lnode->next = node;
chat_head->lnode = node;
}
}
int search_cli(char *name){
struct client_info *tmp = client_head->fnode;
while(tmp!=NULL){
if(strcmp(tmp->user,name) == 0){
return tmp->sockfd;
}
tmp = tmp->next;
}
return -1;
}
void client_insert(struct client_info *node){
if(client_head->fnode == NULL){
client_head->fnode = node;
client_head->lnode = node;
node->next = NULL;
}
else{
client_head->lnode->next = node;
client_head->lnode = node;
node->next = NULL;
}
}
void client_delete(struct client_info *node){
struct client_info *last = client_head->fnode;
struct client_info *tmp = last->next;
if(client_head->fnode == NULL){
return;
}
if(compare(last->sockfd, node->sockfd) == 0){
client_head->fnode = tmp;
free(last);
if(client_head->fnode == NULL){
client_head->lnode = NULL;
}
return;
}
while(tmp!=NULL){
if(compare(tmp->sockfd, node->sockfd) == 0) {
last->next = tmp->next;
free(tmp);
return;
}
tmp = tmp->next;
}
}
void display_clients(){
struct client_info *tmp = client_head->fnode;
while(tmp!=NULL){
printf("Username: %s\nSocket: %d\n--------------------\n",tmp->user,tmp->sockfd);
tmp = tmp->next;
}
}
void change_username(int sock,char *sender){
struct client_info *tmp = client_head->fnode;
while(tmp!=NULL){
if(tmp->sockfd == sock){
strcpy(tmp->user,sender);
break;
}
tmp = tmp->next;
}
}
void *client_commands_handler(void *fd){
struct client_info *clinfo = (struct client_info *)fd;
char *buffer = malloc(NAMELEN);
char receiver[NAMELEN],sender[NAMELEN],message[BUFFSIZE];
recv(clinfo->sockfd,buffer,NAMELEN,0);
strcpy(clinfo->user,buffer);
memset(buffer,0,sizeof(buffer));
int nbytes;
while(1){
memset(buffer,0,sizeof(buffer));
nbytes = recv(clinfo->sockfd, buffer, BUFFSIZE, 0);
if(nbytes<=0) {
printf("%s lost connection\n", clinfo->user);
pthread_mutex_lock(&mutex);
client_delete(clinfo);
pthread_mutex_unlock(&mutex);
break;
}
printf("%s\n",buffer);
else if(strncmp(buffer, "private", 7) == 0) {
int sockid;
sscanf(buffer,"private %s %s %s",sender,receiver,message);
printf("%s%s%s",sender,receiver,message);
if((sockid = search_cli(sender)) == -1){
char *server = malloc(NAMELEN);
strcpy(server,"<Server> User doesn't exist\n");
write(clinfo->sockfd,server,strlen(server));
}
else{
struct chat new_chat;
new_chat.pos = 0;
strcpy(new_chat.logg[new_chat.pos],message);
new_chat.pos++;
strcpy(new_chat.userlist[0],sender);
strcpy(new_chat.userlist[1],receiver);
sprintf(new_chat.ID,"%s%s",new_chat.userlist[0],new_chat.userlist[1]);
new_chat.next = NULL;
pthread_mutex_lock(&mutex);
chat_insert(&new_chat);
pthread_mutex_unlock(&mutex);
send(sockid,message,strlen(message),0);
}
}
else {
fprintf(stderr, "Garbage data from [%d] %s...\n", clinfo->sockfd, clinfo->user);
}
}
close(clinfo->sockfd);
return NULL;
}
void *server_commands_handler() {
}
int main() {
int size,clientfd;
struct sockaddr_in server_addr, client_addr;
pthread_t server_commands;
list_init();
pthread_mutex_init(&mutex, NULL);
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Couldn't get server socket");
exit(0);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(IP);
memset(&(server_addr.sin_zero), 0, 8);
if(bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
perror("Couldn't bind socket");
exit(0);
}
if(listen(sockfd, MAX_CONNECTION_Q) == -1){
perror("Couldn't listen");
exit(0);
}
if(pthread_create(&server_commands, NULL, *server_commands_handler, NULL) != 0){
perror("Couldn't create thread");
exit(0);
}
while(1){
size = sizeof(struct sockaddr_in);
if((clientfd = accept(sockfd, (struct sockaddr *)&client_addr,(socklen_t*)&size))==-1){
perror("Connection failed");
exit(0);
}
printf("Client accepted\n");
struct client_info clinfo;
clinfo.sockfd = clientfd;
clinfo.next = NULL;
pthread_mutex_lock(&mutex);
client_insert(&clinfo);
pthread_mutex_unlock(&mutex);
pthread_create(&clinfo.client_ID, NULL, client_commands_handler, (void *)&clinfo);
}
return 0;
}
客户端:
#define SERVERIP "127.0.0.1"
#define SERVERPORT 9000
#define BUFFSIZE 1024
#define USERLEN 32
#define OPTLEN 16
struct threadinfo{
pthread_t thread_ID;
int sockfd;
};
struct USER{
char user[USERLEN];
int sockfd;
char current_convo[USERLEN];
};
int isconnected, sockfd, sent;
char option[BUFFSIZE];
struct USER me;
int connect_with_server();
void logout(struct USER *me);
void login(struct USER *me);
void *receiver();
void login(struct USER *me){
if(isconnected){
printf("Already connected\n");
return;
}
sockfd = connect_with_server();
if(sockfd == -1){
perror("Couldn't connect to server\n");
}
if(sockfd >= 0) {
isconnected = 1;
me->sockfd = sockfd;
printf("Logged in as %s\n", me->user);
struct threadinfo thread;
pthread_create(&thread.thread_ID, NULL, receiver, (void *)&thread);
}
else {
printf("Couldn't connect.\n");
exit(0);
}
}
void *receiver(){
int recvd;
char msg[BUFFSIZE];
while(isconnected){
memset(msg,0,sizeof(msg));
recvd = read(sockfd, msg, sizeof(msg));
if(!recvd) {
printf("Connection Lost from Server\n");
isconnected = 0;
close(sockfd);
break;
}
if(recvd > 0) {
fputs(msg,stdout);
}
}
return NULL;
}
int main() {
memset(&me, 0, sizeof(struct USER));
char msg[BUFFSIZE];
char *token;
while(fgets(option,BUFFSIZE-1,stdin)){
memset(msg,0,sizeof(msg));
if(strncmp(option,"exit", 4) == 0){
logout(&me);
break;
}
else if(strncmp(option, "login", 5) == 0){
token = strtok(option, " ");
token = strtok(NULL, "\n");
memset(me.user, 0, sizeof(char)*USERLEN);
if(token != NULL) {
strcpy(me.user, token);
login(&me);
}
else{
printf("Couldn't get valid username\n");
}
}
else if(strncmp(option, "private", 7) == 0){
if(isconnected == 0){
printf("User not connected\n");
}
else{
char *tmp = malloc(USERLEN);
token = strtok(option, " ");
token = strtok(NULL, " ");
strcpy(tmp,token);
token = strtok(NULL, "\n");
sprintf(msg,"private %s %s %s",me.user,tmp,token);
send(sockfd, msg, strlen(msg),0);
}
}
return 0;
}
代码有很多o代码对于简单的基于双向输入的连接是不必要的,但是它将在我试图实现的长期内有更多的功能。这里有很多东西要解决,但我的问题是主要集中在通过“私人”输入发送字符串,应该如下:
私人(接收者的用户名)(要发送的消息)
答案 0 :(得分:0)
我仍然在看它,但我注意到了一些事情。在您的服务器中,您有
char *buffer = malloc(NAMELEN);
char receiver[NAMELEN],sender[NAMELEN],message[BUFFSIZE];
recv(clinfo->sockfd,buffer,NAMELEN,0);
strcpy(clinfo->user,buffer);
memset(buffer,0,sizeof(buffer));
int nbytes;
while(1){
memset(buffer,0,sizeof(buffer));
nbytes = recv(clinfo->sockfd, buffer, BUFFSIZE, 0);
缓冲区变量接收指向NAMELEN(32)字节块的指针。你在不查看状态的情况下重新获取NAMELEN字节。到现在为止还挺好。然后清除第一个sizeof缓冲区字节(因为缓冲区是一个指针,你正在清除4或8个字节)。不久之后,你将BUFFSIZE(1024)字节恢复到糟糕的32字节缓冲区。
我建议你通过telnet测试来运行服务器
telnet 127.0.0.1 9000
并输入您的程序所期望的内容。请记住,只有在您返回时才会传输数据。
客户端可以使用netcat进行类似的操作:
nc -l 127.0.0.1 9000
虽然我在这,但为什么你的客户端多线程?如果直接调用receiver()而不是创建另一个线程,它会简化操作。
还要留意你的mallocs并确保它们与免费配对。我看到你在malloc一个固定大小的内存块的许多地方然后不释放它。使用局部变量可以实现更多清洁。当你回来时,那些消失了。或者,查看alloca(),当你返回时它也会消失。它无处不在,但非常有用。