/*a thread created after login, to receive all packets from server *the main thread will no more receive packet after this thread created*/ void * recv_packet_thread(void *this_is_no_use){ int n = 0; //number received while( (n = readvrec(client_sock, recvbuf, BUF_SIZE)) > 0){ struct im_pkt_head *response_head = (struct im_pkt_head *)recvbuf; char *response_data = (char *)(response_head + 1); //struct im_pkt_head *response_head = (struct im_pkt_head *)sendbuf; //int response_data_size; //printf("debug:received a packet from socket:%d, type:%d, service:%d, data size:%d\n" // , client_socket, request_head -> type, request_head -> service, request_head -> data_size); memset(sendbuf, 0, sizeof(sendbuf)); if(response_head -> type != TYPE_RESPONSE){ printf("\nReceived a error packet, drop it.\n enter >> "); break; } switch(response_head -> service){ /*case SERVICE_LOGIN: ; break; case SERVICE_LOGOUT: ; break; case SERVICE_QUERY_ONLINE: ; break;*/ case SERVICE_SINGLE_MESSAGE: ; char *sender = response_data; char *text = response_data + 40;/*two names' length*/ printf("\nMessage comes from %s:\n \t%s\nenter >> ", sender,text); fflush(stdout); save_recent_messages(sender, text); break; case SERVICE_MULTI_MESSAGE: ; sender = response_data; text = response_data + 20;/*one name's length*/ printf("\nMessage comes from %s:\n \t%s\nenter >> ", sender,text); fflush(stdout); save_recent_messages(sender, text); break; case SERVICE_ONLINE_NOTIFY: ; char *that_online_username = response_data; if(find_user_by_name(online_friend_queue, that_online_username) == NULL){ struct user_node *n = init_user_node(-1, that_online_username); enqueue(online_friend_queue, n); } break; case SERVICE_OFFLINE_NOTIFY: ; char *that_offline_username = response_data; delete_user_by_name(online_friend_queue, that_offline_username); break; default: printf("\nReceived a error packet, drop it.\n enter >> ");break; } memset(recvbuf, 0, sizeof(recvbuf)); } if( n < 0) printf("Read error\n"); pthread_exit(NULL); }
pUser find_user(char *nameNum, pDataStruct workingData){ pUser tempUser = NULL; int tempNum = strict_atoi(nameNum); if ( ( ( strcmp(nameNum,"root") ) && ( strcmp(nameNum,"0") ) ) == 0 ){ return workingData->user; } if ( tempNum != 0 ){ tempUser = find_user_by_number( tempNum, workingData ); if ( tempUser != NULL ){ return tempUser; } } tempUser = find_user_by_name( nameNum, workingData); return tempUser; }
void *client_handler(void * connfd){ int n; /*number received*/ char username[20]; //the user's name connecting to this thread. will be filled when login int status = -1; //the user's status int client_socket = (int)connfd; //the user's socket connecting to this thread; //debug://long tid = pthread_self(); assert(online_user_queue != NULL); /*each thread has its own buf*/ char sendbuf[BUF_SIZE]; char recvbuf[BUF_SIZE]; //printf("debug:thread %lu created for dealing with client requests\n", tid); /*Receive the request im packet by two steps. *Firstly receive the head field, secondly receive the data field. *Then handle the request packet (usually by a response packet)*/ while( (n = readvrec(client_socket, recvbuf, BUF_SIZE)) > 0){ struct im_pkt_head *request_head = (struct im_pkt_head *)recvbuf; struct im_pkt_head *response_head = (struct im_pkt_head *)sendbuf; char *request_data = (char *)(request_head + 1); int response_data_size = 0; //printf("debug:received a packet from socket:%d, type:%d, service:%d, data size:%d\n" // , client_socket, request_head -> type, request_head -> service, request_head -> data_size); memset(sendbuf, 0, sizeof(sendbuf)); if(request_head -> type != TYPE_REQUEST){ printf("Received a error packet, drop it.\n"); break; } switch(request_head -> service){ case SERVICE_LOGIN: ; /*response a login response packet, *it contains only 1 byte data to indicate that login succeeded or failed*/ response_data_size = 1; bool login_result; strncpy(username, (char *)(request_head + 1), 20); //check repeat pthread_mutex_lock(&mutex); if(find_user_by_name( online_user_queue, username) == NULL){ //printf("debug:here2\n"); struct user_node *pnode = init_user_node(client_socket, username); enqueue(online_user_queue, pnode); status = ONLINE_STATUS; printf("\tuser %s login.\n", username); login_result = true; /*notify all othre users*/ on_off_line_notify(SERVICE_ONLINE_NOTIFY, username, sendbuf); } else{ login_result = false; } pthread_mutex_unlock(&mutex); //printf("debug: thread %lu release lock\n", tid); construct_im_pkt_head(response_head, TYPE_RESPONSE, SERVICE_LOGIN, response_data_size); concat_im_pkt_data(response_head, (char *)&login_result); send(client_socket, sendbuf, IM_PKT_HEAD_SIZE + response_data_size, 0); //printf("debug:response packet send to socket:%d, type:%d, service:%d, data size:%d\n" // , client_socket, response_head -> type, response_head -> service, response_head -> data_size); break; case SERVICE_LOGOUT: ; /*nothing need to do here.*/ break; case SERVICE_QUERY_ONLINE: ; /*copy all usernames into the data field of the response packet*/ pthread_mutex_lock(&mutex); response_data_size = 20 * copy_all_user_name((char *)(response_head + 1), online_user_queue); pthread_mutex_unlock(&mutex); construct_im_pkt_head(response_head, TYPE_RESPONSE, SERVICE_QUERY_ONLINE, response_data_size); /*send the packet*/ send(client_socket, sendbuf, IM_PKT_HEAD_SIZE + response_data_size, 0); break; case SERVICE_SINGLE_MESSAGE: ; //printf("debug: sender %s, recipient %s, text %s\n", request_data, request_data + 20, request_data + 40); /*simple resend the packet to the recipient*/ char *recipient = request_data + 20; struct user_node *recipient_node = find_user_by_name(online_user_queue, recipient); if(recipient_node != NULL){ response_data_size = request_head -> data_size; construct_im_pkt_head(response_head, TYPE_RESPONSE, SERVICE_SINGLE_MESSAGE, response_data_size); concat_im_pkt_data(response_head, request_data); send(recipient_node -> socket, sendbuf,IM_PKT_HEAD_SIZE + response_data_size, 0); }else printf("Error, no recipient %s. drop the packet\n", recipient); break; case SERVICE_MULTI_MESSAGE: ; //printf("debug: sender %s, recipient all, text %s\n", request_data, request_data + 20); response_data_size = request_head -> data_size; construct_im_pkt_head(response_head, TYPE_RESPONSE, SERVICE_MULTI_MESSAGE, response_data_size); concat_im_pkt_data(response_head, request_data); char *sender = request_data; /*send to all online users except this message's sender*/ for(recipient_node = online_user_queue -> front; recipient_node != NULL; recipient_node = recipient_node -> next) if(strcmp(recipient_node -> username, sender) != 0) send(recipient_node -> socket, sendbuf,IM_PKT_HEAD_SIZE + response_data_size, 0); break; default: printf("Received a error packet, drop it.\n");break; } memset(recvbuf, 0, sizeof(recvbuf)); } if( n < 0) printf("Read error\n"); if(status == ONLINE_STATUS){ /*remove logout or disconnected users from the queue*/ on_off_line_notify(SERVICE_OFFLINE_NOTIFY, username, sendbuf); pthread_mutex_lock(&mutex); delete_user_by_name(online_user_queue, username); pthread_mutex_unlock(&mutex); status = -1; printf("\tuser %s logout\n", username); } pthread_exit(NULL); }
int main(void){ struct sockaddr_in server_addr;//server address /*client start preparations*/ if((client_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1){ printf("error create socket\n"); exit(-1); } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERV_PORT);//the weather server port inet_pton(AF_INET, SERV_IP, &server_addr.sin_addr.s_addr);//weather server ip address if(connect(client_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1){ printf("error connect\n"); exit(-1); } online_friend_queue = init_user_queue(); //load login page login(); /*after login, the username and current online friends list will be settled down*/ /*create a thread to receive packets asynchronously */ pthread_t tid; int rc = pthread_create(&tid, NULL, recv_packet_thread, NULL); if(rc){ printf("ERROR creating thread, tid %d, return code %d\n", (int)tid, rc); exit(-1); } /*main thread loads the next page to get user's command*/ if(system("clear")) ; print_prompt_words(username); while(true){ printf("enter >> "); char command[20]; get_keyboard_input(command, 20); //printf("debug:%s\n", command); if(command[0] != '-'){ printf("input error, you can try again.\n"); continue; } if(strcmp(&command[1], "list") == 0) { assert(online_friend_queue -> front != NULL); print_online_friends(online_friend_queue); continue; } else if(strcmp(&command[1], "clear") == 0){ if(system("clear")) ; print_prompt_words(username); continue; } else if(strcmp(&command[1], "logout") == 0){ logout(); break;//this lead to the end of the program. } else if(strcmp(&command[1], "recent") == 0){ print_recent_messages(recent_messages, message_num); continue; }else if(strcmp(&command[1], "chat") == 0){ /*bug:if someone's name is "all", bug happens*/ char recipient[20]; printf("Who do you want to talk to?\n" "Type he/she's name or type \"all\":"); get_keyboard_input(recipient, 20); if(strcmp(recipient, username) == 0){ printf("it's funny to talk to yourself.\n"); } else if(strcmp(recipient, "all") == 0){ char text[100]; printf("Input your words to say to %s.\n" "Within 100 characters, end up with the Enter key):\n", recipient); get_keyboard_input(text, 100); /*build and send the packet*/ memset(sendbuf, 0, sizeof(sendbuf)); unsigned short data_size = 20 + 100; construct_im_pkt_head((struct im_pkt_head *)sendbuf, TYPE_REQUEST, SERVICE_MULTI_MESSAGE, data_size); strncpy(&sendbuf[IM_PKT_HEAD_SIZE], username, 20); strncpy(&sendbuf[IM_PKT_HEAD_SIZE + 20], text, 100); send(client_sock, sendbuf, IM_PKT_HEAD_SIZE + data_size, 0); } else if(find_user_by_name(online_friend_queue, recipient) != NULL){ //printf("debug:%s\n", recipient); char text[100]; printf("Input your words to say to %s.\n" "Within 100 characters, end up with the Enter key):\n", recipient); get_keyboard_input(text, 100); /*build and send the packet*/ memset(sendbuf, 0, sizeof(sendbuf)); unsigned short data_size = 20 + 20 + 100; construct_im_pkt_head((struct im_pkt_head *)sendbuf, TYPE_REQUEST, SERVICE_SINGLE_MESSAGE, data_size); strncpy(&sendbuf[IM_PKT_HEAD_SIZE], username, 20); strncpy(&sendbuf[IM_PKT_HEAD_SIZE + 20], recipient, 20); strncpy(&sendbuf[IM_PKT_HEAD_SIZE + 40], text, 100); send(client_sock, sendbuf, IM_PKT_HEAD_SIZE + data_size, 0); } else{ printf("sorry, %s seems not online now\n", recipient); print_online_friends(online_friend_queue); } continue; } else{ printf("input error, you can try again.\n"); continue; } } /*close socket and stop another thread before exit*/ pthread_cancel(tid); close(client_sock); return 0; }