多進程實現linux 下即時聊天軟件

網絡編程項目要求
一、 實現目標
一個在Linux下可以使用的聊天軟件,要求至少實現如下功能:
1. 採用Client/Server架構
2. Client A 登陸聊天服務器前,需要註冊自己的ID和密碼
3. 註冊成功後,Client A 就可以通過自己的ID和密碼登陸聊天服務器
4. 多個Client X 可以同時登陸聊天服務器之後,與其他用戶進行通訊聊天
5. Client A成功登陸後可以查看當前聊天室內其他在線用戶Client x
6. Client A可以選擇發消息給某個特定的Client X,即”悄悄話”功能
7. Client A 可以選擇發消息全部的在線用戶,即”羣發消息”功能
8. Client A 在退出時需要保存聊天記錄
9. Server端維護一個所有登陸用戶的聊天會的記錄文件,以便備查
 
可以選擇實現的附加功能:
1. Server可以內建一個特殊權限的賬號admin,用於管理聊天室
2. Admin可以將某個Client X “提出聊天室”
3. Admin可以將某個Client X ”設爲只能旁聽,不能發言”
4. Client 端發言增加表情符號,可以設置某些自定義的特殊組合來表達感
情.如輸入:),則會自動發送”XXX向大家做了個笑臉”
5. Client段增加某些常用話語,可以對其中某些部分進行”姓名替換”,例
如,輸入/ClientA/welcome,則會自動發送”ClientA 大俠,歡迎你來到咱
們的聊天室” 

6 文件傳輸
 
二、 考覈內容
網絡編程:
設計Client和Server的通訊協議,並實現Server向登陸客戶的消息發送
文件I/O編程:
設計聊天記錄的文件格式
設計註冊用戶和密碼及配置參數的”數據庫”文件
多線程或進程編程:
Server端需要至少創建2個線程或進程,一個用於監聽Client端的連接請求,
一個用於給登陸的Client用戶列表發送某個client的消息內容.
項目文檔的編寫:
系統概要設計文檔
系統詳細設計文檔
用戶使用手冊

 

本程序通過多進程,非阻塞socket採用輪詢方式實現。

 

package.h

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/wait.h>
#include <sqlite3.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <time.h>

typedef struct mesg
{
    char name[15];
    char word[50];
    int fd;
}mesg;

typedef struct usrmesg
{
    char name[10];
    char pwd[10];
    int flag;
}usrmesg;

typedef struct usrdata
{
    char name[10];
    int num;
}usrdata;


extern int Socket();
extern void Bind( int, struct sockaddr_in, int);
extern void Listen( int, int);
extern int Accept( int, struct sockaddr *, socklen_t *);
extern void Connect( int, struct sockaddr_in, int);
extern pid_t Fork();
extern int Read( int, void *, size_t);
extern void clientlogin( int sockfd, usrmesg *usr);
extern void usrfunction( int sockfd, usrmesg usr);
extern void rootfunction( int sockfd);
extern int Shmget();
extern sqlite3 * initdb();
extern void login( int , sqlite3 *);
extern void searchfunction( int, mesg *, sqlite3 *);
extern void transit( mesg *, sqlite3 *);


 

package.c

#include"../../include/package.h"

typedef struct sockaddr SA;

int Socket()
{
    int listenfd;
    if( ( listenfd = socket( PF_INET, SOCK_STREAM, 0 ) ) < 0)
    {
        fprintf( stderr, "fail to socket :%s\n", strerror(errno) );
	exit(-1);
    }
    return listenfd;
}

void Bind( int sockfd, struct sockaddr_in my_addr, int addrlen )
{
    if ( bind( sockfd, (SA *)&my_addr, addrlen ) < 0)
    {
        perror("fail to bind");
	exit(-1);
    }
}

void Listen( int listenfd,int backlog)
{
    if( listen( listenfd, backlog) == -1 )
    {
        perror("fail to listen");
	exit(-1);
    }
}

int Accept( int listenfd, struct sockaddr *addr, socklen_t *addrlen)
{
    if( ( listenfd = accept( listenfd, NULL, NULL) ) == -1)
    {
        perror("fail to accept");
        exit(-1);
    }
    return listenfd;
}


void Connect( int sockfd, struct sockaddr_in my_addr, int addrlen )
{
    if ( connect( sockfd, (SA *)&my_addr, addrlen ) < 0)
    {
        perror("fail to connect to server");
	exit(-1);
    }
}

pid_t Fork()
{
    pid_t pid;
    if( ( pid = fork() ) < 0 )
    {
        perror("fail to create process");
        exit(-1);
    }
    return pid;
}

int Shmget ()
{
    int shmid;
    if( (  shmid = shmget( IPC_PRIVATE, sizeof(mesg), 0777 ) ) < 0 )
    {
        perror("fail to shmget");
	exit(-1);
    }
    return shmid;
}

int Read( int fd, void * ptr, size_t size)
{
    int n = 0;
    if( ( n = read( fd, ptr, size) ) < 0)
    {
        printf("fail to read\n");
	exit(-1);
    }
    return n;
}


 

client.c

#include"../../include/package.h"                  

usrmesg usr;
int main(int argc, char *argv[])
{
    int sockfd;
    int ret;
    if( argc < 2 )                     //出錯判斷
    {
        printf("請輸入IP地址!\n");
	exit(0);
    }
    struct sockaddr_in client_addr;
    sockfd = Socket();
    memset(&client_addr, 0, sizeof(client_addr));
    client_addr.sin_family = PF_INET;
    client_addr.sin_port = htons(8888);                 //鏈接服務器
    client_addr.sin_addr.s_addr = inet_addr(argv[1]);
    Connect( sockfd, client_addr, sizeof(client_addr));
    usr.flag = 0;
    system("clear");
    printf("\n\n\n\n\n\n\n\n\n\n\t\t\t歡迎使用易水蕭風聊天室\n\n\n\n\n\n\n\n\n\n\n\n");
    sleep(2);
    clientlogin(sockfd,&usr); 
    system("clear");
    if( strcmp( usr.name,"root") == 0)        //用戶甄別
    {
        goto management;
    }
    usrfunction( sockfd,usr);           //用戶功能選擇
    management:
    rootfunction( sockfd);             //管理員功能選擇
    return 0;
}


 


 

 

clientlogin.c

 

#include"../../include/package.h"                  //客戶端登錄處理函數

void clientlogin( int sockfd, usrmesg *usr)
{
    int chose;                              //flag識別服務器返回消息類型
    enum flag { success = 3, wrong = -3, reregistration = -4, resuccess = 4
                , dberror = -2, relogin = -5 , login = 1, registration = 2};
    while(1)
    {
        system("clear");
        if(usr->flag == success)
	{
	    printf("登錄成功\n");
	    sleep(1);
	    break;
	}
	if( usr->flag == wrong)
	{
	    printf("用戶名或密碼錯誤,請重新登錄\n");
	    sleep(1);
	}
	if( usr->flag == reregistration)
	{
	    printf("該用戶名已被註冊\n");
	    sleep(1);
	}
	if( usr->flag == resuccess)
	{
	    printf("註冊成功,你現在可以用該帳號登錄了\n");
	    sleep(1);
	}
	if( usr->flag == dberror)
	{
	    printf("數據庫故障,註冊失敗\n");
	    sleep(1);
	}
	if( usr->flag == relogin)
	{
	    printf("該用戶已登錄,請不要重複登錄\n");
	    sleep(1);
	}
        printf("\n\n\n\n\n\n\n\t\t\t\t聊天室登錄        \n\n");
        printf("                 \t\t1.登錄               \n\n");
        printf("                 \t\t2.註冊               \n\n");
        printf("                 \t\t3.退出                \n\n");
	printf("\n\n\n\n\n\n請選擇:");
	scanf("%d",&chose);
        setbuf( stdin, 0);
        switch(chose)                 //登錄輸入的出錯處理沒做。。。。
        {
            case 1:
	          {
	              system("clear");
		      printf("****************登錄********************\n");
	              printf("用戶名(不含空格):");
		      scanf("%s",usr->name);
		      setbuf(stdin,0);
                      printf("密碼(不含空格):");
		      scanf("%s",usr->pwd);
		      setbuf(stdin,0);
		      usr->flag = login;
		      write( sockfd, usr, sizeof(usrmesg) );
		      Read( sockfd, usr, sizeof(usrmesg) );
		      break;
	          }
	    case 2:
	          {
	              system("clear");
		      printf("*****************註冊********************\n");
	              printf("請輸入字母數字開頭(不含空格)10位內用戶名:");
		      scanf("%s", usr->name);
		      setbuf(stdin,0);
		      printf("請輸入10位密碼:");
		      scanf("%s", usr->pwd);
		      setbuf(stdin,0);
		      usr->flag = registration;
		      write( sockfd, usr, sizeof(usrmesg) );
		      Read( sockfd, usr, sizeof(usrmesg) );
		      break;
	          }
	    case 3:
	          {
	              printf("歡迎使用!\n");
		      exit(0);
	          }
	    default:
 	           {
	               printf("輸入有誤,請重新輸入\n");
                       break;
	           }
	}
    }
}


 

urrfunction.c

#include"../../include/package.h"           //用戶功能選擇函數


void usrfunction( int sockfd, usrmesg usr)
{
    int choose;
    mesg out;
    mesg get;
    time_t tm;
    usrdata data[10];
    pid_t pid = Fork();
    enum flag { all = -1, file = -5, busy = -6, quit = -2, kicking = -4
                ,useless = -3 };
    if( pid > 0)
    {
        while(1)
	{
             printf("請選擇: 1 羣聊  2 私聊  3 傳送  4 退出\n");
             scanf("%d",&choose);
	     switch( choose )
	     {
	         case 1:
	               {
		           strcpy( out.name,"all");   //羣發將發送給所有人
		           printf("請輸入:");
			   setbuf( stdin,0);
		           fgets( out.word, sizeof(out.word),stdin);
		           out.fd = 0;
		           write( sockfd, &out,sizeof(out));
			   break;
		       }
		 case 2:
		       {
		           int i;
			   int choose;
		           strcpy( out.word, "getusrmesg");  //請求在線用戶
			   write( sockfd, &out,sizeof(out));
			   sleep(1);
			   Read(sockfd, data, sizeof(data));
                           for( i = 0; i <= data[0].num ;i++)
			   {
			       printf("%d.%s  ",i+1,data[i].name);
			   }
			   printf("\n");
                           printf("請選擇私聊對象:");
			   scanf("%d",&choose);
			   if( strcmp ( data[choose-1].name, "root") == 0 )
			   {
			       printf("對不起你不能給管理員發消息\n");
			       sleep(1);
			       break;                     //簡單出錯判斷
			   }
                           strcpy( out.name, data[choose-1].name);
                           out.fd = 0;
			   printf("你想對他說:");
			   setbuf(stdin,0);
			   fgets( out.word,sizeof(out.word), stdin);
		           write( sockfd, &out,sizeof(out));
			   break;
		       }
		 case 3:
		       {
		           int i;
		           int choose;
		           strcpy( out.word, "getusrmesg");  //請求在線用戶
			   write( sockfd, &out,sizeof(out));
			   sleep(1);
			   read(sockfd, data, sizeof(data));
                           for( i = 0; i <= data[0].num ;i++)
			   {
                               printf("%d.%s  ",i+1,data[i].name);
			   }
			   printf("\n");
			   printf("你想把你的文件傳給誰:( 0 取消):\n");
                           scanf("%d",&choose);
			   if( strcmp ( data[choose-1].name, "root") == 0 )
			   {
			       printf("對不起你沒有權限傳給管理員東西\n");
			       sleep(1);
			       break;                    //簡單判斷
			   }
			   if( strcmp ( data[choose-1].name,usr.name) == 0 )
			   {
			       printf("不是吧,自己的東西還要傳。。。\n");
			       sleep(1);
			       break;
			   }
			   if( choose == 0)
			   {
			       break;
			   }
			   strcpy( out.name, data[choose-1].name);
			   strcpy( out.word, "transport");
                           write( sockfd, &out, sizeof(out));
			   sleep(1);
			   read( sockfd, &out, sizeof(out));
			   if( strcmp( out.word, "wait") == 0 )
			   {
	                       printf("服務器繁忙,請稍後再試...\n");
			       break;
			   }
                           int fd =open("send",O_RDWR|O_CREAT|O_APPEND,0666);                          
			   if( fd < 0)
			   {
			       printf("打開測試文件失敗\n");
			       break;
			   }
			   lseek( fd, 0, SEEK_SET);  // 坑爹的3小時,注意!!!
			   memset( out.word, 0, sizeof(out.word));
			   int n = 0;
			   sleep(1);
			   while( ( n = read(fd,out.word,sizeof(out.word))>0))
			   {
			       write( sockfd,out.word,sizeof(out.word));
			       memset( out.word, 0, sizeof(out.word));
			   }
			   strcpy(out.word,"finish"); 
                           write( sockfd,out.word,sizeof(out.word));
			   sleep(3);
			   printf("發送完成\n");
			   close(fd);                 //“finish”解除服務器
                           break;                     //讀阻塞
		       }
		 case 4:
		       {
		           strcpy( out.name,"sever");
			   strcpy( out.word,"quit");
			   out.fd = 0;
			   write( sockfd, &out,sizeof(out));
		           printf("歡迎使用\n"); 
			   sleep(1);            //等待子進程退出
			   exit(0);
		       }
		 default:
		        {
			    printf("輸入有誤\n");
			    break;
			}
	    }
        }

    }
    if( pid == 0)
    {
        while(1)
	{
            Read( sockfd, &get,sizeof(get));
	    if( get.fd == kicking)
	    {
	        printf("你被管理員踢下線了..........\n");
		printf("請猛按4退出或強制退出............\n");
		printf("你什麼都幹不了了,認命吧騒年\n");
                close(sockfd);
		exit(0);
	    }
	    if(get.fd == file) //傳送文件
	    {
	        int n;
	        printf("%s給你發送了個文件\n",get.word);
	        int fd = open( "recv",O_RDWR|O_CREAT|O_APPEND,0666);
		if( fd < 0)
		{
		    printf("fail to open the file\n");
		    exit(-1);
		}
		memset( get.word,0,sizeof(get.word));
		while( ( n = Read( sockfd,get.word,sizeof(get.word) ) ) > 0 )
		{
		    if( strcmp( get.word,"finish") == 0)
		    {
		        break;
		    }
                    write( fd,get.word,sizeof(get.word));
		    memset( get.word, 0, sizeof(get.word));
		}
		printf("接收完成\n");
		close(fd);
	    }
	    if( get.fd == busy )
	    {
	        printf("服務器繁忙,請稍後再試...\n");
	    }
       	    if( get.fd == useless )
	    {
	        sleep(1);  //爲了防止和主進程,搶奪在線用戶列表而定義的消息
	    }
	    if( get.fd == all)
	    {
       	        tm = time(NULL);
	        printf("%s",ctime(&tm));
	        printf("羣消息:%s說:%s",get.name,get.word);
	    }
	    if( get.fd > 0 )
	    {
       	        tm = time(NULL);
	        printf("%s",ctime(&tm));
	        printf("私聊:%s對你說:%s",get.name,get.word);
            }
	    if( get.fd == quit )
	    {
	        exit(0);          //主進程叫你退出啦
	    }

	}
    }
}


 

rootfunction.c

#include "../../include/package.h"            //管理員功能選擇函數
                                              //管理員不具有聊天功能
void rootfunction( int sockfd)
{
    mesg out;
    mesg get;
    usrdata data[10];
    while(1)
    {
	int choose;
	system("clear");
        printf("請選擇功能:1.踢人     2.禁言      3退出\n");
	scanf("%d",&choose);
	switch(choose)
	{
	    case 1:
		  {
		      int i;
                      strcpy( out.word, "getusrmesg");  //請求在線用戶
                      write( sockfd, &out,sizeof(out));
                      Read(sockfd, &get, sizeof(get));
                      Read(sockfd, data, sizeof(data));
                      for( i = 0; i <= data[0].num ;i++)
		      {
                          printf("%d.%s  ",i+1,data[i].name); //顯示
		      }
		      printf("\n");
                      printf("你要踢除誰:(0 放棄):");
		      setbuf(stdin,0);
		      scanf("%d",&choose);
		      if( choose != 0)             //簡單判斷
		      {
			 if( strcmp (data[choose-1].name, "root") == 0)
			 {
			     system("clear");
			     printf("你不能剔除自己哦。。親\n");
			     sleep(1);
			 }
			 else
			 {
			     strcpy( out.name, data[choose-1].name);
			     strcpy( out.word,"delete");  //發送請求
			     write(sockfd, &out,sizeof(out));
			 }
		       }
		       break;
		  }
		case 2:
		      {
		          int i;
                          strcpy( out.word, "getusrmesg"); //請求在線用戶
                          write( sockfd, &out,sizeof(out));
                          Read(sockfd, &get, sizeof(get));
                          Read(sockfd, data, sizeof(data));
                          for( i = 0; i <= data[0].num ;i++)
      			  {
                              printf("%d.%s  ",i+1,data[i].name);
			  }
			  printf("\n");
                          printf("你要禁言誰:(0 放棄):");
			  setbuf(stdin,0);
			  scanf("%d",&choose);
			  if( choose != 0)
			  {
                              strcpy( out.name, data[choose-1].name);
			      strcpy( out.word,"Gay");
			      write(sockfd, &out,sizeof(out));
			  }
		          break;
		      }
		case 3:
		      {
		          strcpy( out.word, "quit");
			  write( sockfd, &out, sizeof(out));
			  printf("\n\n\n\n\n\n\n\n\n\n\t\t\t\t感謝您的管理\n\n\n\n\n\n\n\n\n\n\n");
			  sleep(1);
			  system("clear");
			  exit(0);
		      }
		default:
		       {
		           printf("輸入有誤,請重新輸入\n");
			   break;
		       }
            }
    }
}


 

服務器

server.c

/*********************************************
 Author: zxf     Date:2012.07   Version: 1.0
 Description:   服務器的main函數
 Functionlist:
              1 initdb()   初始化數據庫
	      2 seachfunction()   客戶端功能選擇函數
	      3 transit()      功能選擇轉發函數
**********************************************/
#include"../../include/package.h"


mesg get;                       //消息結構體
time_t tm;
char *sql;
char *zErrMsg;                 // 存放數據庫錯誤信息
usrdata data[10];              //存放在線用戶姓名

int main()
{
    int listenfd, connfd;
    int column,row;
    int i;
    int opt = 1;
    int shmid = Shmget();     //定義共享內存,讓主進程來轉發子進程消息
    mesg *shmaddr; 
    sqlite3 *db = initdb();
    shmaddr = (mesg *)shmat( shmid,NULL,0);
    struct sockaddr_in server_addr;
    listenfd = Socket();
    int flags = fcntl( listenfd, F_GETFL,0);   //非阻塞
    fcntl ( listenfd,F_SETFL,flags | O_NONBLOCK);
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = PF_INET;
    server_addr.sin_port = htons(8888);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    setsockopt( listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    Bind( listenfd, server_addr, sizeof(server_addr) );
    Listen( listenfd, 10 );
    while(1)
    {
        int ret;
	connfd = -1;
	while(connfd < 0)           //輪詢
        {
	    connfd = accept( listenfd, NULL, NULL);
            transit( shmaddr,db);     //主進程轉發子進程消息
	}
	printf("有用戶請求,正在連接數據庫\n");
	pid_t pid = Fork();
	if( pid == 0)                 
	{
            int configure = 1;
	    while(1)
	    {
	        while( configure)
		{
	            login( connfd, db);   //登錄
		    configure = 0;
		}
		searchfunction( connfd,shmaddr,db);   //監聽客戶端請求
	    }
	}
    }
    return 0;
 }


 

    


 

severlogin.c

#include "../../include/package.h"     //客戶端登錄請求,響應函數,處理完後
                                      //返回給客戶端一個消息類型
void login( int connfd ,sqlite3 *db)
{
    int configure = 1;
    usrmesg usr;
    int column, row, i, j;
    char *sql;
    char **result;
    char *zErrMsg;
    enum signal { login = 1, registration = 2, relogin = -5, success = 3,
                  wrong = -3, reregistration = -4, dberror = -2, resuccess
		  = 4};
    while(configure)
    {
        int ret = Read( connfd, &usr, sizeof(usr));
	if(ret == 0)
	{
            printf("客戶端已退出\n");
	    exit(0);
	}
	if( usr.flag == login )
	{
	    printf("正在驗證用戶名和密碼\n");
            sql = sqlite3_mprintf("select *from online where ID = '%s';",usr.name);

            sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
            if( row != 0)
            {
                printf("該用戶已登錄,請不要重複登錄\n");
                usr.flag = relogin;
		write( connfd, &usr, sizeof(usr) );
	    }
	    else
            {
                sql = "select *from usrdata;";
		sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
		for(i = column; i < ( (row+1)*column ); i = i+column )
		{
		    if ( strcmp( usr.name, result[i] ) == 0 )
		    {
		        if( strcmp( usr.pwd,result[i+1] ) == 0)
			{
			    usr.flag = success;
		            configure = 0;
		            sql = sqlite3_mprintf("insert into online values('%s',%d,0);",usr.name,connfd);
                            sqlite3_exec( db, sql, NULL, NULL,&zErrMsg);
		            printf("驗證成功\n");
			    write( connfd, &usr, sizeof(usr) );
			}
		    }
		}
            }
            if( usr.flag == login )
            {
                printf("驗證失敗\n");
                usr.flag = wrong;
	        write( connfd, &usr, sizeof(usr) );
            }
	}
	else
	{
	    printf("正在註冊用戶\n");
            sql = "select *from usrdata;";
            sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
            for(i = column; i < ( (row+1)*column ); i = i+column )
            {
                if ( strcmp( usr.name, result[i] ) == 0 )
		{
		    usr.flag = reregistration;
		    write(connfd, &usr, sizeof(usr));
	            printf("註冊失敗\n");
	            break;
		}
            }
            if( usr.flag == registration)
            {
	        sql = sqlite3_mprintf("insert into usrdata values('%s','%s');",usr.name,usr.pwd);
                int result = sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
	        if(result)
	        {
	            usr.flag = dberror;
	            printf("數據庫故障註冊失敗\n");
		    printf(" 插入失敗,原因:%s\n",zErrMsg);
	        }
	        else
	        {
	            usr.flag = resuccess;
	            printf("註冊成功\n");
	        }
	        write(connfd,&usr,sizeof(usr));
	    }
        }
    }
}


 

initlib.c

#include "../../include/package.h"   //打開數據庫,創建密碼錶,在線用戶表                                        //聊天記錄表

sqlite3 * initdb()
{
    int rc;
    sqlite3 *db = NULL;
    char *zErrMsg = NULL;
    rc = sqlite3_open("usr.db",&db);
    if(rc)
    {
        fprintf(stderr,"Can't open database:%s",sqlite3_errmsg(db));
	sqlite3_close(db);
    }
    char *sql = " create table usrdata( ID text PRIMARY KEY,PWD text ); " ;
    sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
    sql = " create table online( ID text PRIMARY KEY,fd INTEGER , gay INTEGER); " ;
    sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
    sql = " create table chatrecord( time text,send  text , get text, word text ); " ;
    sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
    sql = "insert into usrdata values('root','123456');";
    sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
    return db;
}


 

seachfunction.c

#include "../../include/package.h"   //檢索客戶端的請求類型

void searchfunction( int connfd, mesg *shmaddr, sqlite3 *db)
{
    int column,row;
    char *sql = NULL;
    char *zErrMsg = NULL;
    time_t tm;
    int i;
    int ret;
    mesg get;
    usrdata data[10];
    while(1)
    {
        start:
        ret = Read( connfd, &get, sizeof(get) );  
        if( ret == 0)                       //判斷客戶端是否強制退出
	{
	    char **result;
            sql = sqlite3_mprintf( "select ID from online where fd = %d;",connfd);
            sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
	    printf("%s強制退出了客戶端\n",result[1]);
            sql = sqlite3_mprintf( "delete from online where fd = %d;",connfd);
            sqlite3_exec(db,sql,NULL,NULL,&zErrMsg);
            exit(-1);
	}
        if( strncmp( get.word, "quit", 4) == 0)   //客戶端主動退出
        {
            char **result;
            sql = sqlite3_mprintf( "select ID from online where fd = %d;",connfd);
            sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
            printf("%s正常退出了客戶端\n",result[1]);
	    get.fd = -2;
	    write( connfd,&get, sizeof(get));
            sql = sqlite3_mprintf( "delete from online where fd = %d;",connfd);
            sqlite3_exec(db,sql,NULL,NULL,&zErrMsg);
		     exit(0);
		 }

            if( strcmp( get.word,"getusrmesg") == 0)//客戶端請求在線用戶列表
            {
                get.fd = -3;
		write(connfd,&get,sizeof(get));
		char **result;
		int j = 0;
                sql = "select ID from online;";
		sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
                for( i = column; i < (row+1)*column; i = i+column)
		{
		    strcpy( data[j].name, result[i]);
	            j++;
		}
		data[0].num = j-1;
		write( connfd, data,sizeof(data) );
                continue;
	    }

            if( strcmp ( get.word,"transport") == 0) //客戶端傳送文件
            {
                get.fd = -4;
		char **result;
		int n = 0;
		int i = 0;
                sql = "select * from online;";
	        sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
                for( i = column+2; i < (row+1)*column ; i = i+column )
		{
		    if( atoi (result[i]) == 2)
		    {
		        get.fd = -3;
			write( connfd, &get, sizeof(get) );
			strcpy( get.word, "wait");
                        write( connfd, &get, sizeof(get) );
			goto start;
		    }
		}
		get.fd = -3;
	        write( connfd, &get, sizeof(get) );
                write( connfd, &get, sizeof(get) );
		get.fd = -4;
                sql = sqlite3_mprintf("select ID from online where fd = %d;",connfd);
	        sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
		strcpy( get.word, result[1]);
	        memcpy(shmaddr,&get,sizeof(get));  //通知接收端建立文件
		memset( get.word, 0, sizeof(get.word));
		printf("通知接收完成\n");
		while((n = Read(connfd, get.word,sizeof(get.word)))>0)
		{
       		    get.fd = -5;
                    memcpy(shmaddr,&get,sizeof(get));
	            sleep(1);        //拷貝太快了,保護共享內存中的內容
	            if( strcmp( get.word,"finish") == 0)
	            {
	                break;
	            }
		    memset( get.word, 0, sizeof(get.word));
		}
		printf("拷貝完成\n");
                sql = sqlite3_mprintf("update online set gay = 0 where ID = '%s';",get.name);
                sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
                continue;
	    }

            if( strcmp( get.word,"delete") == 0)  //強制用戶下線
            {
                get.fd = -2;
                sql = sqlite3_mprintf("update online set gay = 1 where ID = '%s';",get.name);
                sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
		memcpy(shmaddr,&get,sizeof(get));
		continue;
            }

            if( strcmp( get.word,"Gay") == 0)  //禁言
            {
                sql = sqlite3_mprintf("update online set gay = 1 where ID = '%s';",get.name);
                sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
		continue;
            }
            char **result;                     //把在線用戶表中,相應位置1
            int j;
            char *temp = (char *)malloc(sizeof(get.name));
            strcpy( temp, get.name);
            sql = "select * from online;";
            sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
            for( i = column; i < (row+1)*column ; i = i+column )
            {
                if( strcmp( get.name, result[i]) == 0 )
		{
		    if( atoi (result[i+2]) == 2)
		    {
		        get.fd = -6;
			write( connfd, &get, sizeof(get));
			break;
		    }
		    for( j=column+1; j< (row+1)*column; j = j+column)
	            {
                        if( atoi( result[j] ) == connfd)
			{
			    if( atoi( result[j+1]) == 1)
		            {
		                get.fd = -3; //私聊,並把聊天記錄寫在表中
		            }
			    strcpy( get.name, result[j-1]);
			    break;
			}
	            }
	            tm = time(NULL);
	            sql = sqlite3_mprintf("insert into chatrecord values('%s','%s','%s','%s');",ctime(&tm),get.name,temp,get.word);
                    sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
	            if( get.fd != -3)
	            {
		        get.fd = atoi(result[i+1]);
	            }
	            memcpy(shmaddr,&get,sizeof(get));
		    free( temp );
		    temp = NULL;
	            break;
		}
	    }
	    int flag = 1;
            if( strcmp ( get.name, "all") == 0)       //羣聊
	    {
	        for( j = column+2; j < (row+1)*column; j = j+column )
		{
                    if( atoi( result[j]) == 2 )  //有用戶傳輸文件
		    {
		        get.fd = -6;
                        write( connfd, &get, sizeof(get));  //通知等待
			flag = 0;
			break;
		    }
		}
                if( flag == 0)
		{
		    continue;
		}
                for( j = column+1; j < (row+1)*column ; j = j+column )
		{
		    if( atoi( result[j] ) == connfd)
	            {
	                if( atoi(result[j+1]) == 1)
			{
			    get.fd = -3;
			}
			strcpy(get.name,result[j-1]);
			break;
	            }
		}
                tm = time(NULL);
		sql = sqlite3_mprintf("insert into chatrecord values('%s','%s','%s','%s');",ctime(&tm),get.name,temp,get.word);
                sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
		if(get.fd != -3)
		{
		    get.fd = -1;
	        }
		memcpy(shmaddr,&get,sizeof(get));
            }
    }
}


 

transit.c

#include "../../include/package.h"       //中轉客戶端消息

void transit( mesg *shmaddr, sqlite3 *db)
{
    int row;
    int column;
    char *sql;
    int i;
    char *zErrMsg;
    enum choosefunction {  all = -1, kicking = -2, gay = -3, file = -4,
                           transport = -5 };
    if( shmaddr->fd == all)         //羣發
    {
	char **result;
	sql = "select * from online;";
	sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
	if( strncmp (shmaddr->word,"smile",5) == 0)
	{
	    strcpy( shmaddr->word, "向大家發送了笑臉\n");
	}
	for( i = column+1; i < (row+1)*column; i = i+column)
	{
            if( strcmp( result[i-1],"root") != 0)
	    {
                write(atoi(result[i]),shmaddr,sizeof(mesg));
            }
        }
	shmaddr->fd = 0;
    }

    if( shmaddr->fd == kicking )         //踢人
    {
	char **result;
	sql = "select * from online;";
	sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
	for( i = column; i < (row+1)*column; i = i+column)
	{
            if( strcmp( shmaddr->name,result[i] )  == 0)
            {
	        shmaddr->fd = -4;
		write(atoi(result[i+1]),shmaddr,sizeof(mesg));
		break;
	    }
	}
	shmaddr->fd = 0;
    }

    if( shmaddr->fd == gay )               //禁言
    {
        printf("該用戶被禁言...................\n");
	shmaddr->fd = 0;
    }

    if( shmaddr->fd == file )                  //通知客戶端,接受文件
    {
	char **result;
	int temp;
	sql = sqlite3_mprintf( "select fd from online where ID = '%s';",shmaddr->name);
        sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
	sql = sqlite3_mprintf( "update online set gay = 2 where ID ='%s';",shmaddr->name);
	sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
	shmaddr->fd = -5;
        write( atoi( result[1]),shmaddr,sizeof(mesg));
        shmaddr->fd = 0;
    }
    
    if( shmaddr->fd == transport )       //客戶1->服務器->客戶2 讀一次寫一次
    {
        char **result;
	sql = sqlite3_mprintf( "select fd from online where ID = '%s';",shmaddr->name);
	sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
        write(atoi( result[1]),shmaddr->word,sizeof(shmaddr->word));
        shmaddr->fd = 0;
    }

    if( shmaddr->fd != 0)             //私聊
    {
        write(shmaddr->fd,shmaddr,sizeof(mesg));
        shmaddr->fd = 0;
    }
}


尚未解決的問題:

客戶端在接收文件時,接收文件的客戶端發送消息會出錯

 原因:服務器返回的消息被客戶端接收文件的read所接收

 

後面做了所有的出錯處理和註釋,概要設計什麼的,發上來太麻煩了,就不發了啊

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章