多进程实现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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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所接收

 

后面做了所有的出错处理和注释,概要设计什么的,发上来太麻烦了,就不发了啊

 

你可能感兴趣的:(写的程序)