1. 程式人生 > >基於UDP的聊天系統 Ubuntu14.04

基於UDP的聊天系統 Ubuntu14.04

總體思想:
先是建立一個連結串列用來存放連入伺服器的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;
}