基於UDP的聊天系統 Ubuntu14.04
阿新 • • 發佈:2018-11-19
總體思想:
先是建立一個連結串列用來存放連入伺服器的sockaddr_in資訊,有人登入加入連結串列節點,有人下線刪除節點。
建立一個類似訊息佇列結構的結構體,用來存放使用者的當前狀態的型別、名字、訊息。
將每個UDP客戶端連線伺服器後將其的sockaddr_in資訊加入到連結串列中,通過資料的型別去判斷伺服器去執行哪個函式(登入廣播、轉發聊天資訊、下線廣播)。
1、UDP伺服器部分
第一點 執行伺服器會fork()一個子程序,該子程序用於伺服器本身傳送聊天的內容,父程序用於接收客戶端的訊息。
第二點 伺服器通過接收的資訊去判斷型別(‘L’ ‘C’ ‘Q’)執行相應的處理函式。
第三點 處理函式 type = ‘L’ 會向當前連結串列中除自己以外的所有成員廣播 name上線,隨後將其加入到連結串列中。type = ‘C’ 會向當前連結串列中除自己以外的所有成員廣播訊息內容。type = ‘Q’ 首先是通知所有使用者name下線,在連結串列中將其刪除。
2、客戶端部分
第一點 執行客戶端程式,首先是將訊息型別設定成’L’,令伺服器廣播告知其他成員該使用者上線。
第二點 隨後建立一個子程序,該程序用於傳送聊天的訊息,父程序用於接收伺服器轉發的其他使用者發過來的訊息。
第三點 當子程序判斷輸入的訊息是"quit"將資料型別改成’Q’,以便伺服器將其在連結串列中刪除,並告知其他使用者某人下線。
service:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <signal.h> #define N 128 typedef struct msg { char type; char name[N]; char text[N]; }msg_t; int main(int argc, const char *argv[]) { char buf[N] = {0}; int sockfd,ret,newsockfd; struct sockaddr_in server,client; pid_t pid; msg_t msg; memset(&msg,0,sizeof(msg)) ; if(argc != 3) { printf("執行程式時帶入ip 和 port\n"); exit(-1); } socklen_t addrlen = sizeof(client); sockfd = socket(AF_INET,SOCK_DGRAM,0); if(sockfd < 0) { perror("socket"); exit(-1); } printf("sockfd=%d\n",sockfd); memset(&server,0,sizeof(server)); server.sin_family = AF_INET ; server.sin_port = htons( atoi(argv[2]) ); server.sin_addr.s_addr = inet_addr(argv[1]); msg.type = 'L'; printf("input your name >:"); fgets(msg.name,N,stdin); msg.name[strlen(msg.name) -1 ] = 0 ; ret = sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&server,addrlen) ; if(ret < 0) { perror("write"); exit(-1); } pid = fork(); if(pid < 0) { perror("fork"); exit(-1); } else if(pid == 0) { msg.type = 'C'; while(1) { printf(">:"); fgets(msg.text,N,stdin); if(strncmp(msg.text,"quit",4) == 0 ) { msg.type = 'Q'; ret = sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&server,addrlen) ; if(ret < 0) { perror("write"); exit(-1); } kill(getppid(),SIGUSR1); exit(0); } ret = sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&server,addrlen) ; if(ret < 0) { perror("write"); exit(-1); } } } else { while(1) { ret = recvfrom(sockfd,&msg,sizeof(msg),0,NULL,NULL); if(ret < 0) { printf("read"); exit(-1); } printf("%s\n",msg.text); } } close(sockfd); return 0; }
client:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define N 128 typedef struct node { struct sockaddr_in addr; //ip 地址 struct node * next; }linklist_t; //連結串列 typedef struct msg { char type; char name[N]; char text[N]; }msg_t; linklist_t * create_empty_linkist(void) { linklist_t * h = (linklist_t *)malloc(sizeof(linklist_t)) ; h->next = NULL; memset(&h->addr,0,sizeof(h->addr)); return h; } int process_login(linklist_t * h,int sockfd,struct sockaddr_in *addr,msg_t *msgp) { linklist_t * p = h->next; int ret ; sprintf(msgp->text,"%s login",msgp->name); while(p != NULL) { if(memcmp(&p->addr,addr,sizeof(*addr)) != 0 ) { ret = sendto(sockfd,msgp,sizeof(msg_t),0,(struct sockaddr *)&p->addr,sizeof(p->addr)); if(ret < 0) { printf("sendto"); exit(-1); } } p = p->next; } p = (linklist_t * )malloc(sizeof(linklist_t)) ; //倒敘插入法 p->next = h->next; h->next = p ; p->addr = *addr; return 0; } int process_chat(linklist_t * h,int sockfd,struct sockaddr_in *addr,msg_t *msgp) { linklist_t * p = h->next; char buf[128] = {0}; int ret ; sprintf(buf,"%s said %s",msgp->name,msgp->text); strcpy(msgp->text,buf); while(p != NULL) { if(memcmp(&p->addr,addr,sizeof(*addr)) != 0 ) { ret = sendto(sockfd,msgp,sizeof(msg_t),0,(struct sockaddr *)&p->addr,sizeof(p->addr)); if(ret < 0) { printf("sendto"); exit(-1); } } p = p->next; } return 0; } int process_quit(linklist_t * h,int sockfd,struct sockaddr_in *addr,msg_t *msgp) { linklist_t * p = h->next; linklist_t * q; char buf[128] = {0}; int ret ; sprintf(msgp->text,"%s offline",msgp->name); while(p != NULL) { if(memcmp(&p->addr,addr,sizeof(*addr)) != 0 ) { ret = sendto(sockfd,msgp,sizeof(msg_t),0,(struct sockaddr *)&p->addr,sizeof(p->addr)); if(ret < 0) { printf("sendto"); exit(-1); } } p = p->next; } p = h ; while(p->next != NULL) { if(memcmp(&p->next->addr,addr,sizeof(*addr)) == 0 ) { q = p->next; p->next = p->next->next; free(q); break ; } p = p->next; } return 0; } int main(int argc, const char *argv[]) { char buf[N] ={0}; int sockfd,ret,newsockfd; struct sockaddr_in myaddr,client; pid_t pid; msg_t msg; memset(&msg,0,sizeof(msg)); if(argc != 3) { printf("執行程式時帶入ip 和 port\n"); exit(-1); } socklen_t addrlen = sizeof(client); sockfd = socket(AF_INET,SOCK_DGRAM,0); if(sockfd < 0) { perror("socket"); exit(-1); } printf("sockfd=%d\n",sockfd); memset(&myaddr,0,sizeof(myaddr)); myaddr.sin_family = AF_INET ; myaddr.sin_port = htons(atoi(argv[2])); myaddr.sin_addr.s_addr = inet_addr(argv[1]); ret = bind(sockfd,(struct sockaddr *)&myaddr,sizeof(myaddr)) ; // 給socket 一個固定的ip 和埠 if(ret < 0) { perror("bind"); exit(-1); } pid = fork(); if(pid < 0) { perror("fork"); exit(-1); } else if(pid == 0 ) // 傳送廣播 { msg.type = 'C'; while(1) { printf(">:"); fgets(msg.text,N,stdin); strcpy(msg.name,"server"); ret = sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&myaddr,addrlen) ; if(ret < 0) { perror("write"); exit(-1); } memset(buf,0,N); } } else // parent { linklist_t * H = create_empty_linkist(); while(1) { ret = recvfrom(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&client,&addrlen); if(ret < 0) { printf("read"); exit(-1); } printf("msg.type = %c\n",msg.type); printf("msg.name = %s\n",msg.name); printf("msg.text = %s\n",msg.text); switch(msg.type) { case 'L': process_login(H,sockfd,&client,&msg); break ; case 'C': process_chat(H,sockfd,&client,&msg); break ; case 'Q': process_quit(H,sockfd,&client,&msg); break ; default: break ; } #if 0 ret = sendto(sockfd,buf,N,0,(struct sockaddr *)&client,addrlen); if(ret < 0) { printf("sendto"); exit(-1); } #endif } } return 0; }