/*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); }
/*send a login request to server*/ void login(){ if(system("clear"));//unused warnning printf( "Hello, this is an IM program.\n" "You may pick a nickname and login.\n" "==========================================\n"); while(true){ while(true){ printf("Your name (no more than 15 characters):"); if(get_keyboard_input(username, 20) >= 3) break; else printf("It's too short.\n"); } /*construct a login request packet and send it*/ memset(sendbuf, 0, sizeof(sendbuf)); unsigned short data_size = 20; //request im packet data size construct_im_pkt_head((struct im_pkt_head *)sendbuf, TYPE_REQUEST, SERVICE_LOGIN, data_size); concat_im_pkt_data((struct im_pkt_head *)sendbuf, username); send(client_sock, sendbuf, IM_PKT_HEAD_SIZE + data_size, 0); /*get and parse the response packet*/ memset(recvbuf, 0 ,sizeof(recvbuf)); int n = readvrec(client_sock, recvbuf, BUF_SIZE); struct im_pkt_head *response_head = (struct im_pkt_head *)recvbuf; if( (n == 1) && (response_head -> service == SERVICE_LOGIN)){ //the response data size is 1 char *response_data = (char *)(response_head + 1);//right after the head bool login_success = (bool) response_data[0]; if(login_success){ query_online_all(); break; } else{ //login falied. name repeat printf( "\nSorry, that name seems to have been taken, pick another one!\n"); continue; } } printf("Sorry ,the server seems overloaded, wait a moment and try again.\n"); exit(-1); } }
int main(int argc,char **argv) { struct sockaddr_in peer; SOCKET s; SOCKET s1; int peerlen = sizeof(peer); int n; char buf[10]; INIT(); if(argc == 2) { s = tcp_server(NULL,argv[1]); } else { s = tcp_server(argv[1],argv[2]); } s1 = accept(s,(struct sockaddr*)&peer,(socklen_t *)&peerlen); if(!isvalidsock(s1)) { error(1,errno,"accept failed"); } for(;;) { n = readvrec(s1,buf,sizeof(buf)); if(n < 0) { error(0,errno,"readvrec returned error"); } else if(n == 0) { error(1,0,"client disconnected\n"); } else { write(1,buf,n); } } EXIT(0); }
/*query for all online friends' names*/ void query_online_all(){ /*build a query packet*/ memset(sendbuf, 0, sizeof(sendbuf)); int data_size = 0; construct_im_pkt_head((struct im_pkt_head *)sendbuf, TYPE_REQUEST, SERVICE_QUERY_ONLINE, data_size); //concat_im_pkt_data((struct im_pkt_head *)sendbuf, NULL); send(client_sock, sendbuf, IM_PKT_HEAD_SIZE + data_size, 0); /*get and parse the response packet*/ memset(recvbuf, 0 ,sizeof(recvbuf)); readvrec(client_sock, recvbuf, BUF_SIZE); struct im_pkt_head *response_head = (struct im_pkt_head *)recvbuf; assert((response_head -> type == TYPE_RESPONSE) && (response_head -> service == SERVICE_QUERY_ONLINE)); /*add all user into online_friend_queue*/ int i; for (i = 0; i < response_head -> data_size / 20; ++i){ struct user_node *pnode = init_user_node(-1, (char *)(response_head + 1) + 20 * i); enqueue(online_friend_queue, pnode); } }
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); }