先将代码贴出来,然后慢慢再解释.
@Makefile
OBJS := server client all: $(OBJS) server: msg_svr.c msg.h gcc -o $@ $^ -D_DEBUG client: msg_client.c msg.h gcc -o $@ $^ -lpthread clean: $(RM) $(OBJS)
@msg.h
#include <stdlib.h> #include <string.h> #include <stdio.h> #include <fcntl.h> #include <signal.h> #include <errno.h> #include <time.h> #include <sys/msg.h> #define MSG_FILE "/tmp/msg_server" #define BUFFER 255 #define PERM S_IRUSR | S_IWUSR #define OK 1 #define ERR 0 // msg type #define TYPE_SERVER 1000 #define TYPE_SERVER_STR "1000" // msg target string #define SERVER_STR "SERVER" #define TO_ALL_STR "ALL" // send cmd // L, I, O is send to server #define CMD_LIST 'L' #define CMD_LOGIN 'I' #define CMD_LOGOUT 'O' #define CMD_TOALL 'A' // C, F send to others #define CMD_CHAT 'C' #define CMD_SEND_FILE 'F' // CMD:FROM:TIME:DATA #define DATA_LEN 4 #define OFT_CMD 0 #define OFT_FRM 1 #define OFT_TIM 2 #define OFT_DAT 3 #define DATA_TOK ":" // USR_ID:USR_NAME:TIME #define USER_DATA_LEN 3 #define OFT_USR_ID 0 #define OFT_USR_NM 1 #define OFT_LOGIN_TM 2 #define USER_DATA_TOK "#" // id admin #define START_ID 1 //-------------------------------------+ // List operations struct list_head { struct list_head *next; }; // init a new list named name #define LIST_INIT(name) \ struct list_head name = {NULL} static inline int list_add(struct list_head * head, struct list_head* new){ new->next = head->next; head->next = new; } static inline int list_delete(struct list_head * head, struct list_head* target){ while(head){ if(head->next == target){ head->next = target->next; return 0; } head = head->next; } return -1; } //-------------------------------------+ // online status enum status{ online, offline, invisible }; // available id int available_id = START_ID; // user struct to save user informations struct user{ struct list_head list; int id; char name[32]; enum status status;; long login_time; }; // message struct struct message { long mtype; char buffer[BUFFER+1]; }msg_snd, msg_rcv; int msgid = 0; // send a format message to client // CMD:FROM:TIME:DATA int send_msg(long type, int cmd, int from_id, char * data){ sprintf(msg_snd.buffer, "%c:%d:%d:%s", cmd, from_id, time(NULL), data); msg_snd.mtype = type; if(msgsnd(msgid, &msg_snd, strlen(msg_snd.buffer)+1, 0) < 0) return ERR; else return OK; } inline char *time2str(long time, char* buf){ struct tm *t = localtime(&time); strftime(buf, 32, "%Y-%m-%d-%H:%M:%S", t); return buf; } int init_msg_queue(){ key_t key; if((key = ftok(MSG_FILE, 'a')) == -1){ perror("ftok"); exit(1); } printf("Key:%d\n", key); if((msgid = msgget(key, PERM | IPC_CREAT)) == -1) { perror("msgget"); exit(1); } printf("msgid = %d\n", msgid); return msgid; } int process_msg(char* buffer);
/* msg_svr.c */ #include "msg.h" LIST_INIT(msg_list_head); int main() { msgid = init_msg_queue(); while(1) { if(msgrcv(msgid, &msg_rcv, sizeof(struct message), TYPE_SERVER, 0) == -1) perror("msgrcv"); else printf("Get: %s\n", msg_rcv.buffer); // process message process_msg(msg_rcv.buffer); } exit(0); } // process message received from message queue // message format: // CMD:TARGET:FROM:TIME:DATA int process_msg(char* buffer){ char * data[DATA_LEN]; char * str, *subtoken; int i; memset(data, NULL, sizeof(data)); for(str = buffer, i = 0; ; str = NULL, i++){ subtoken = strtok(str, DATA_TOK); if(subtoken == NULL) break; data[i] = subtoken; #ifdef _DEBUG printf("> data[%d] = %s\n", i, subtoken); #endif } // data format error if(i != DATA_LEN) return ERR; char info[256]; char buf[256]; struct user* u ; struct list_head* p; // send to server cmd switch(data[OFT_CMD][0]){ case CMD_LIST: bzero(buf, sizeof(buf)); p = (&msg_list_head)->next; while(p){ u= (struct user*)p; sprintf(info, "%d#%s#%ld#", u->id, u->name, u->login_time); #ifdef _DEUBG printf("u->name = %s\n", u->name); #endif strcat(buf, info); p = p->next; } if(p != msg_list_head.next){ // delete the end '#' buf[strlen(buf) - 1] = 0; } send_msg(atol(data[OFT_FRM]), CMD_LIST, TYPE_SERVER, buf); break; case CMD_LOGIN: u = (struct user *)malloc(sizeof(struct user)); if(NULL == u){ perror("malloc"); return ERR; } // add to list list_add(&msg_list_head, &(u->list)); u->id = available_id++; strcpy(u->name, data[OFT_FRM]); u->status = online; u->login_time = atol(data[OFT_TIM]); // login ok echo msg msg_snd.mtype = atol(data[OFT_DAT]); sprintf(msg_snd.buffer, "%d", u->id); msgsnd(msgid, &msg_snd, strlen(buf)+1, 0); break; case CMD_LOGOUT: p = (&msg_list_head)->next; while(p){ u= (struct user*)p; // find the user who request logout if(u->id == atoi(data[OFT_FRM])){ if(send_msg(u->id, CMD_LOGOUT, TYPE_SERVER, "Logout OK!") == OK){ list_delete(&msg_list_head, p); free(p); }else{ return ERR; } break; } p = p->next; } break; case CMD_TOALL: // send to all online client p = (&msg_list_head)->next; while(p){ u= (struct user*)p; send_msg(u->id, CMD_CHAT, data[OFT_FRM], data[OFT_DAT]); p = p->next; } break; default: return ERR; } return OK; }
/* msg_client.c */ #include "msg.h" int userid = 0; char name[32] = ""; void print_menu(void){ printf("\t+----------------------------------+\n"); printf("\t+ Chat Room V1.0 2013.01.08 +\n"); printf("\t+----------------------------------+\n"); printf("\t+ User Commands as follows: +\n"); printf("\t+ +\n"); printf("\t+ l: list all online user +\n"); printf("\t+ i: Login +\n"); printf("\t+ o: logOut +\n"); printf("\t+ c: Chat with other online user +\n"); printf("\t+ a: Chat with all online user +\n"); printf("\t+ f: transfer a File to others +\n"); printf("\t+ h: Help +\n"); printf("\t+----------------------------------+\n"); } int get_choice(){ printf("%s# ", name); int answer = getchar(); // eat <Enter> while(getchar() != '\n'); // eat <Enter> //putchar(answer); return answer; } void func(int sig){ printf("\n%s# ", name); fflush(stdout); } int send_to(int target, int cmd, char *data){ return send_msg(target, cmd, userid, data); } int send_server(int cmd, char *data){ return send_msg(TYPE_SERVER, cmd, userid, data); } int chat(){ if(strlen(name) == 0){ printf("You are not login!\n"); return ERR; } char id[32]; char data[256]; char buf[256]; printf("To: [USR_ID] "); fflush(stdout); if(fgets(id, sizeof(id), stdin) == NULL){ perror("fgets"); return ERR; } sprintf(data, " %s > ", name); id[strlen(id) - 1] = 0; printf(">> "); fflush(stdout); if(fgets(buf, sizeof(buf), stdin) == NULL){ perror("fgets"); return ERR; } strcat(data, buf); data[strlen(data) - 1] = 0; send_to(atoi(id), CMD_CHAT, data); } int chat_all(){ if(strlen(name) == 0){ printf("You are not login!\n"); return ERR; } char data[256]; char buf[256]; sprintf(data, " %s To all > ", name); printf("To all >> "); fflush(stdout); if(fgets(buf, sizeof(buf), stdin) == NULL){ perror("fgets"); return ERR; } strcat(data, buf); data[strlen(data) - 1] = 0; send_to(TYPE_SERVER, CMD_TOALL, data); } int login(){ printf("username: \n"); if(fgets(name, sizeof(name), stdin) == NULL){ perror("fgets"); return ERR; } name[strlen(name) - 1] = 0; int rand_type = random(); time_t t; time(&t); sprintf(msg_snd.buffer, "%c:%s:%ld:%d", CMD_LOGIN, name, t, rand_type); #ifdef _DEBUG printf("%s\n", msg_snd.buffer); #endif // get a random type to login server msg_snd.mtype = TYPE_SERVER; if(msgsnd(msgid, &msg_snd, strlen(msg_snd.buffer)+1, 0) < 0){ perror("msgsnd"); return ERR; } // wait server response if(msgrcv(msgid, &msg_rcv, sizeof(msg_rcv), rand_type, 0) < 0){ return ERR; } else{ userid = atol(msg_rcv.buffer); printf("Login OK id = %d\n", userid); return OK; } } int logout(){ if(strlen(name) == 0){ return ERR; } send_server(CMD_LOGOUT, "Logout"); // wait server response if(msgrcv(msgid, &msg_rcv, sizeof(msg_rcv), userid, 0) < 0){ return ERR; }else{ userid = 0; printf("Logout OK\n"); return OK; } } void format_user_list(char * buffer){ char * data[USER_DATA_LEN]; char * str, *subtoken; char time_buf[24]; int i, n; memset(data, NULL, sizeof(data)); printf("----------------------------------\n"); printf(" _ID_ USR_NAME Login_Time \n"); printf("----------------------------------\n"); for(str = buffer, i = 0, n = 1; ; str = NULL, i++){ subtoken = strtok(str, USER_DATA_TOK); if(subtoken == NULL) break; data[i] = subtoken; #ifdef _DEBUG printf("> data[%d] = %s\n", i, subtoken); #endif if(i != 0 && i % 2 == 0){ printf(" %4s %8s %s\n", data[OFT_USR_ID], data[OFT_USR_NM], time2str(atol(data[OFT_LOGIN_TM]), time_buf)); n++; i = -1; } } } void * receiver_looper(void * p){ if(userid == 0) return NULL; char * data[DATA_LEN]; char * str, *subtoken; int i; while(1){ if(msgrcv(msgid, &msg_rcv, sizeof(msg_rcv), userid, 0) < 0){ perror("msgrcv"); continue; }else{ #ifdef _DEBUG printf("%s received: %s\n", __func__, msg_rcv.buffer); #endif memset(data, NULL, sizeof(data)); for(str = msg_rcv.buffer, i = 0; ; str = NULL, i++){ subtoken = strtok(str, DATA_TOK); if(subtoken == NULL) break; data[i] = subtoken; #ifdef _DEBUG printf("> data[%d] = %s\n", i, subtoken); #endif } // process received data // data format error if(i != DATA_LEN) continue; switch(data[OFT_CMD][0]){ case CMD_LIST: if(strcmp(data[OFT_FRM], TYPE_SERVER_STR)){ continue; } format_user_list(data[OFT_DAT]); break; case CMD_LOGOUT: if(strcmp(data[OFT_FRM], TYPE_SERVER_STR)){ continue; } printf("> %s ", data[OFT_DAT]); printf("%s\n", time2str(atol(data[OFT_TIM]), data[OFT_DAT])); exit(0); case CMD_CHAT: // print chat content printf("\n%s \n\t\t", data[OFT_DAT]); printf("%s\n", time2str(atol(data[OFT_TIM]), data[OFT_DAT])); printf("\n%s# ", name); fflush(stdout); break; case CMD_SEND_FILE: break; } } } } int main(int argc, char **argv) { signal(SIGINT, func); // signal(SIGQUIT, func); msgid = init_msg_queue(); pthread_t thread; // print help menu print_menu(); while(1){ switch(get_choice()){ case 'i': if(login() == OK) while(pthread_create(&thread, NULL, receiver_looper, NULL) < 0); break; case 'l': send_server(CMD_LIST, "List"); sleep(1); break; case 'o': if(logout() == OK){ pthread_join(thread, NULL); return 0; }else{ printf("Not login quit\n"); return 0; } break; case 'c': chat(); break; case 'a': chat_all(); break; break; case 'h': print_menu(); break; case 'f': break; } } }