天天看點

伺服器端struct.hserver.c

struct.h

#ifndef _STRUCT_H_//伺服器端頭檔案

#define _STRUCT_H_

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <pthread.h>

#include <sqlite3.h>

#define PORT  9999

char myName[20];

sqlite3 * database;

struct Msg

{

    int  cmd;        // 消息類型

    char fromname[20];

    char password[10];

    char tag[50];

    char toname[20];

    char msg[1024];  // 消息内容    

};

struct online

{

    int sfd;

    int on_flag;

    int speek_flag;

    char name[20];

    struct online *next;

};

struct online *header;

#endif

server.c

#include "struct.h"

struct online *header = NULL;

// 初始化套接字,傳回監聽套接字

int init_socket()

{

    int listen_socket = socket(AF_INET, SOCK_STREAM, 0);

    if (listen_socket == -1)

    {

        perror ("socket");

        return -1;

    }

    struct sockaddr_in addr;

    memset(&addr, 0, sizeof(addr));

    addr.sin_family  = AF_INET;     // 設定位址族

    addr.sin_port    = htons(PORT); // 設定本地端口

    addr.sin_addr.s_addr = htonl(INADDR_ANY);   // 使用本地的任意IP位址

    int  ret = bind(listen_socket,  (struct sockaddr *)&addr, sizeof(addr));

    if (ret == -1)

    {

        perror ("bind");

        return -1;

    }

    ret = listen(listen_socket, 5);

    if (ret == -1)

    {

        perror ("listen");

        return -1;

    }

    printf ("等待用戶端連接配接.......\n");

    return listen_socket;

}

// 處理用戶端連接配接,傳回與連接配接上的用戶端通信的套接字

int  MyAccept(int listen_socket)

{

    struct sockaddr_in client_addr; // 用來儲存用戶端的ip和端口資訊

    int len = sizeof(client_addr);

    int client_socket = accept(listen_socket,   (struct sockaddr *)&client_addr,  &len);

    if (client_socket == -1)

    {

        perror ("accept");

    }

    printf ("成功接收一個用戶端: %s\n", inet_ntoa(client_addr.sin_addr));

    return client_socket;

}

//注冊

int reg(int client_socket, struct Msg *msg)

{

    printf ("%s進行注冊\n", msg->fromname);

    char *errmsg = NULL;

    char **resultp = NULL;

    int nrow, ncolumn;

    char buf[100];

    sprintf(buf,"select * from user where name = '%s';",msg->fromname);

    int ret = sqlite3_get_table(database, buf, &resultp, &nrow, &ncolumn, &errmsg);

    if (ret != SQLITE_OK)

    {

        printf ("資料庫操作失敗:%s\n", errmsg);

        return -1;

    }    

    //使用者名已存在

    if( nrow != 0 )

    {

        printf("該使用者名已存在!\n");

        msg->cmd = 1001;

        write (client_socket, msg, sizeof(struct Msg));

        return 0;

    }

    else

    {

        printf("使用者名不存在,可以注冊!\n");

        char tag[20] = "emmmmm.....";

        strcpy( msg->tag, tag );

        //向資料庫中插入使用者資訊

        sprintf (buf, "insert into user values('%s', '%s', '%s')", msg->fromname, msg->password, msg->tag);

        ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);

        if (ret != SQLITE_OK)

        {

            printf ("資料庫操作失敗:%s\n", errmsg);

            msg->cmd = 1002;

            write (client_socket, msg, sizeof(struct Msg));

            return -1;

        }

        msg->cmd = 1003;

        printf("注冊成功!\n");

        write (client_socket, msg, sizeof(struct Msg));

    }

    sqlite3_free_table(resultp);

}

//登入

int login(int client_socket, struct Msg *msg)

{

    printf ("%s進行登入\n", msg->fromname);

    char *errmsg = NULL;

    char **resultp = NULL;

    int nrow, ncolumn;

    char buf[100];

    sprintf(buf,"select * from user where name = '%s';",msg->fromname);

    int ret = sqlite3_get_table(database, buf, &resultp, &nrow, &ncolumn, &errmsg);

    if (ret != SQLITE_OK)

    {

        printf ("資料庫操作失敗:%s\n", errmsg);

        return -1;

    }    

    if ( nrow == 0 )

    {

        printf ("該使用者名不存在!\n");

        msg->cmd = 2001;

        write (client_socket, msg, sizeof(struct Msg));

    }

    else

    {

        //密碼正确

        if ( strcmp ( msg->password, resultp[4] ) == 0 )

        {    

            //檢查使用者是否線上

            struct online *temp = header;

            while (temp != NULL)

            {

                //使用者已線上

                if (strcmp (temp->name, msg->fromname) == 0)

                {

                    if (temp->on_flag == 1)

                    {

                        msg->cmd = 2002;

                        printf ("使用者已線上\n");

                        write (client_socket, msg, sizeof(struct Msg));

                        return 0;

                    }

                }

                temp = temp->next;

            }

            //使用者不線上,儲存使用者資訊

            struct online *node = (struct online *)malloc(sizeof(struct online));

            node->sfd = client_socket;

            node->on_flag     = 1;

            node->speek_flag  = 1;

            strcpy (node->name, msg->fromname);

            strcpy (msg->tag, resultp[5]);

            //将使用者資訊頭插在線上連結清單中

            node->next = header;

            header = node;

            msg->cmd = 2003;

            printf ("登入成功!\n");

            write (client_socket, msg, sizeof(struct Msg));

            return 0;

        }

        printf ("密碼輸入錯誤!\n");

        msg->cmd = 2004;

        write (client_socket, msg, sizeof(struct Msg));

    }

    sqlite3_free_table (resultp);

    return 0;

}

// 私聊

void priva(int client_socket, struct Msg *msg)

{

    printf ("%s要給%s發一條消息\n", msg->fromname, msg->toname);

    //周遊線上使用者連結清單

    struct online *temp = header;

    while (temp != NULL)

    {

        //找到要發送的對象

        if (strcmp (temp->name, msg->toname) == 0)

        {

            msg->cmd = 3001;

            write (temp->sfd, msg, sizeof(struct Msg));

            printf ("找到要發送的對象,消息發送成功!\n");

            msg->cmd = 3002;

            write (client_socket, msg, sizeof(struct Msg));

            return ;

        }

        temp = temp->next;

    }

    printf ("沒有找到要發送的對象!\n");

    msg->cmd = 3003;

    write (client_socket, msg, sizeof(struct Msg));    

}

// 群發

void group(int client_socket, struct Msg *msg)

{

    printf ("%s發一次群消息\n", msg->fromname);

    //周遊線上使用者連結清單

    struct online *temp = header;

    while (temp != NULL)

    {

        msg->cmd = 4001;

        write (temp->sfd, msg, sizeof(struct Msg));

        temp = temp->next;

    }

    msg->cmd = 4002;

    printf ("群發消息成功!\n");

    write (client_socket, msg, sizeof(struct Msg));

}

//檢視線上使用者

void check(int client_socket, struct Msg *msg)

{

    printf ("%s檢視線上使用者\n", msg->fromname);

    char buf[300] = {0};

    int len;

    //周遊線上使用者連結清單

    struct online *temp = header;

    while (temp != NULL)

    {

        strcat ( buf, temp->name );

        len = sizeof(buf);

        buf[len] = ' ';

        temp = temp->next;

    }

    strcpy ( msg->msg, buf );

    printf ("線上使用者有:%s\n",buf);

    msg->cmd = 5001;

    printf ("檢視線上使用者成功!\n");

    write (client_socket, msg, sizeof(struct Msg));

}

//修改密碼

int changepwd(int client_socket, struct Msg *msg)

{

    printf ("%s修改密碼\n", msg->fromname);

    char *errmsg = NULL;

    char **resultp = NULL;

    int nrow, ncolumn;

    char buf[100];

    sprintf(buf,"select * from user where name = '%s';",msg->fromname);

    int ret = sqlite3_get_table(database, buf, &resultp, &nrow, &ncolumn, &errmsg);

    if (ret != SQLITE_OK)

    {

        printf ("資料庫操作失敗:%s\n", errmsg);

        return -1;

    }    

    //密碼正确

    if ( strcmp ( msg->password, resultp[4] ) == 0 )

    {

        char buf[100];

        sprintf (buf, "update user set password = '%s' where name = '%s'", msg->msg, msg->fromname);

        ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);

        if (ret != SQLITE_OK)

        {

            printf ("資料庫操作失敗:%s\n", errmsg);

            msg->cmd = 6001;

            write (client_socket, msg, sizeof(struct Msg));

            return -1;

        }

        msg->cmd = 6002;

        printf("修改密碼成功!\n");

        write (client_socket, msg, sizeof(struct Msg));

    }

    else

    {

        msg->cmd = 6003;

        printf("密碼錯誤!\n");

        write (client_socket, msg, sizeof(struct Msg));

    }

}

//修改個性簽名

int changetag(int client_socket, struct Msg *msg)

{

    printf ("%s修改個性簽名\n", msg->fromname);

    char *errmsg = NULL;

    char **resultp = NULL;

    int nrow, ncolumn;

    char buf[100];

    sprintf(buf,"select * from user where name = '%s';",msg->fromname);

    int ret = sqlite3_get_table(database, buf, &resultp, &nrow, &ncolumn, &errmsg);

    if (ret != SQLITE_OK)

    {

        printf ("資料庫操作失敗:%s\n", errmsg);

        return -1;

    }    

    sprintf (buf, "update user set tag = '%s' where name = '%s'", msg->tag, msg->fromname);

    ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);

    if (ret != SQLITE_OK)

    {

        printf ("資料庫操作失敗:%s\n", errmsg);

        msg->cmd = 7001;

        write (client_socket, msg, sizeof(struct Msg));

        return -1;

    }

    msg->cmd = 7002;

    printf ("修改個性簽名成功!\n");

    write (client_socket, msg, sizeof(struct Msg));

}

int clientexit(int client_socket, struct Msg *msg)

{

    printf("%s要退出聊天室\n",msg->fromname);

    struct online *temp = header;

    struct online *prev = NULL;

    while (temp != NULL)

    {

        //找到線上使用者節點

        //如果是第一個節點

        if (strcmp (temp->name, msg->fromname) == 0)

        {

            header = header->next;

            free(temp);

            temp = NULL;

            msg->cmd = 1301;

            printf ("删除線上使用者節點成功!\n");

            write (client_socket, msg, sizeof(struct Msg));

            return 0;

        }

        else

        {

            prev = temp;

            temp = temp->next;

            if (strcmp (temp->name, msg->fromname) == 0)

            {

                prev->next = temp->next;

                free(temp);

                temp = NULL;

                msg->cmd = 1301;

                printf ("删除線上使用者節點成功!\n");

                write (client_socket, msg, sizeof(struct Msg));

                return 0;

            }

        }

    }

    msg->cmd = 1302;

    printf ("沒有找到該使用者節點!\n");

    write (client_socket, msg, sizeof(struct Msg));

}

// 把 負責處理用戶端通信的函數改成線程的工作函數

void* hanld_client(void* v)

{

    int client_socket = (int)v;

    struct Msg msg;

    while(1)

    {

        // 從用戶端讀一個結構體資料

        int ret = read(client_socket, &msg, sizeof(msg));

        if (ret == -1)

        {

            perror ("read");

            break;

        }

        // 代表用戶端退出

        if (ret == 0)

        {

            printf ("用戶端退出\n");

            break;

        }

        switch (msg.cmd)

        {

            case 1 :    // 注冊

                reg(client_socket, &msg);

                break;

            case 2 :    // 登入

                login(client_socket, &msg);

                break;

            case 3 :    // 私聊

                priva(client_socket, &msg);

                break;

            case 4 :    // 群聊

                group(client_socket, &msg);

                break;

            case 5 :    // 檢視線上使用者

                check(client_socket, &msg);

                break;

            case 6:        // 修改密碼

                changepwd(client_socket, &msg);

                break;

            case 7:        // 修改個性簽名

                changetag(client_socket, &msg);

                break;

            case 13:    // 用戶端退出

                clientexit(client_socket, &msg);

                break;

        }

    }

    close (client_socket);

}

int main()

{

    int listen_socket = init_socket();

    // 打開資料庫

    int ret = sqlite3_open("user.db", &database);

    if (ret != SQLITE_OK)

    {

        printf ("打開資料庫失敗\n");

        return -1;

    }

    printf ("打開資料庫成功\n");        

    char *errmsg = NULL;

    char *sql = "create table if not exists user ( name TEXT, password TEXT, tag TEXT, primary key(name) )";

    ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);

    if (ret != SQLITE_OK)

    {

        printf ("資料庫操作失敗:%s\n", errmsg);

        return -1;

    }

    while (1)

    {

        int client_socket = MyAccept(listen_socket);

        // 建立一個線程去處理用戶端的請求,主線程依然負責監聽

        pthread_t id;

        pthread_create(&id, NULL, hanld_client,  (void *)client_socket);

        pthread_detach(id); // 線程分離

    }

    close (listen_socket);

    sqlite3_close(database);

    return 0;

}

繼續閱讀