多进程实现linux 下即时聊天软件

【Linux】利用消息队列实现一个简单的进程间双向通信(两种方式)_进程间通信选择一个队列还是两个队列(2) 实际上,第二个参数还需要与文件权限一起使用,如IPC_CREAT|00666表示若内核中不存在指定队列则创建它,同时进程可以对队列消息进行读写操作。简单点儿说,就是第一个用来找到队列,第二个则是定义相关的权限及操作。 阅读详情

网络编程项目要求
一、 实现目标
一个在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所接收

 

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

 

linux 进程间通信之消息队列以及实现进程间聊天系统 什么是消息队列 消息队列实际上是操作系统在内核中为我们创建的一个队列,多个进程可以通过向队列中添加、获取存储有类型的数据块的结点来完成数据传输。 消息队列和管道对比 消息队列是全双工,可读可写的通信方式,而管道是半双工的,写的一方只能写,读的一方只能读。 消息队列和管道一样都受传输数据大小的限制。 管道生命周期随进程,消息队列生命周期随内核 使用消息队列 1.创建消息队列 ... 阅读详情

相关推荐

C++实现即时通信软件

1.1 实验内容 本实验使用 C++ 实现一个具备服务器端和客户端的即时通信聊天室。 这个项目会学习C++网络开发的基本概念,同时可以熟悉Linux下的C++程序编译方法及简单的Makefile编写。 1.2 实验知识点 C++语言基本语法 基本的Makefile C++面向对象程序设计 epoll 网络编程 1.3 实验环境 g++ Xfce 终端 2.1 需求分析 在这个聊天室软件中我...

e31006的博客 1万+

基于Linux的消息队列及多线程编程实现的聊天室(二)代码分析

先将代码贴出来,然后慢慢再解释. @Makefile OBJS := server client all: $(OBJS) server: msg_svr.c msg.h gcc -o $@ $^ -D_DEBUG client: msg_client.c msg.h gcc -o $@ $^ -lpthread clean: $(RM) $(OBJS) ...

mr_raptor的专栏 8123

Linux 下 c 语言 聊天软件 完整代码

这是我学C语言写的第一个软件,是一个完整的聊天软件,里面包括客户端,和服务器端,可以互现聊天,共享文件,有聊天室等,是一个有TCP和UDP协议的聊天软件,测试过很多次在CENTOS和UBUNTU下都通过,写的简单,但五脏俱全,全部贴出来不保留。运行不了,发信给我,还有个使用指南,我是法语写的,有时间我再写个中文的贴上去。废话不说了,代码来了。 #ifndef CHATHEAD_H_

邢玮的技术博客 4846

如何用消息队列实现多进程之间的互聊

思路分析:前边我们已经知道,当路径名和project id相同时,获取到的key值和消息队列的id号也是一致的,根据这个特点,我们只需要将发送消息和接收消息的两个进程设为相同的路进名和project id ,它们就能够实现相互通信。我们来看下代码:(代码过多,可看行号分清)结合图来分析:最终要实现的效果就是用户1里边的父进程发给用户2里边的子进程,用户2里边的父进程发给用户1里边的子进程。(只是其中一个例子,具体谁负责发和收,可根据自己的喜好选择)本篇分享到这里就结束了,感兴趣的朋友可以点个关注哦~

weixin_69460049的博客 471

Ubuntu 从 Pidgin 切换至 Empathy Linux 的默认聊天软件

在过去,Ubuntu 使用的默认聊天软件是 Pidgin,但最近的更新中,它已经被 Empathy Linux 取代。Empathy Linux 是一款开源的即时通讯客户端,它与 GNOME 桌面环境紧密集成,并提供了丰富的功能和用户友好的界面。下面是将 Ubuntu 的默认聊天软件从 Pidgin 更改为 Empathy Linux 的步骤。通过以上步骤,您已成功将 Ubuntu 的默认聊天软件从 Pidgin 更改为 Empathy Linux。选择 Empathy Linux 并记住其对应的编号。

YikPerl的博客 195

Linux环境UDP聊天软件开发指南

Linux网络编程是构建网络应用与服务的基础。它涉及使用套接字(sockets)来实现不同计算机间的通信。本章将概述Linux下网络编程的基本概念,以及其在C语言中的表现形式,为深入学习后续章节奠定基础。本章节深入解析了UDP协议的特点、应用场景,以及如何使用C语言进行UDP网络编程。通过代码示例与逻辑分析,我们了解了UDP通信的基础、客户端与服务器端编程的实现细节,以及性能优化和错误处理方法。在下一章节,我们将深入探讨局域网聊天软件的设计与实现,了解如何将这些基础知识点应用在构建实际应用软件中。

weixin_42607969的博客 1117

Linux消息队列实现进程间实时聊天

本文采用两个消息队列实现进程间的实时聊天。欢迎交流学习。在介绍代码之前,先作核心前置知识的介绍。int msgget(key_t key , int msgflg) //创建消息队列key:由ftok函数根据文件路径和传入参数共同生成;msgflg:访问权限和打开方式返回值:是一个唯一标识某消息队列的标识符。int msgsnd(int msqid , const void * msgp , size_t msgsz , int msgflg) //发送消息。

weixin_46269693的博客 289

linux 多进程聊天程序

ubuntu下通过多进程实现多太PC机的聊天程序

linux qq多进程客户端,基于多进程QQ聊天软件设计.doc

基于多进程QQ聊天软件设计基于多进程的QQ聊天程序设计功能需求描述用户名登陆聊天,人与人之间交流是必不可少的。私聊,与特定的用户聊天群聊,向所有的用户发送消息,大家一起聊欢乐多server端 输入:各客户端发来的消息。 输出:向各个客户端的私有消息队列发送消息Client端 输入:a,键盘输入。 b,server端发来的消息主要应用到了C语言里的 消息队列、管道、数据结构链表的建立、插入、遍历等...

weixin_39673303的博客 119

linux局域网聊天工具(附带文档和源码参考)

1 设计内容: 1.1 开发一个运行于LINUX平台局域网内部的通信软件,主要功能包括: 1、有方便实用的聊天界面; 2、可以实现对局域网内任意电脑的信息接受与发送。 1.2 开发环境 操作系统:Ubuntu 9.04 编译调试环境:VI+GCC+GDB 1.3 关键技术 C语言编程 Linux开发环境中开发工具的使用 Gnome图形设计——GTK+2.0 网络编程中socket“套接字”的使用 2 设计目标: 2.1 体会从需求理解出发,到软件概要设计,详细设计,编码,测试,发布的整个流程,熟悉软件开发的全部流程; 2.2 熟悉Linux操作系统下的C/C++应用程序开发环境,掌握linux系统下开发工具vi、gcc 和gdb的使用,以及makefile的写法; 2.3 掌握Linux环境下图形工具包GTK和网络编程中socket“套接字”的使用;

C语言版多进程聊天软件

1.进去server文件夹,在终端输入"./server"启动服务器程序 2.进入client文件夹,在终端输入"./client"启动客户端程序,在连接服务器窗口可不必输入端口号。(由于能力有限,未对ip地址的输入格式作限制,希望你们能够通过使用正则表达式来完善本程序) 声明:本人精力有限,暂做到这,肯定还不完善,希望大家一起更正

基于Linux和Qt的聊天软件

基于Qt和Linux的即时聊天软件,服务器用Linux设计,

征途开始的小窝(^_^) 3529

基于LinuxC和socket的多进程qq聊天程序(支持文件传输)

本程序实现了以server为主的并发服务器,可以同时和多个客户端聊天,并且支持文件传输的功能

嵌入式linux局域网聊天软件

嵌入式linux局域网聊天软件 设计内容概述 采用Client/Server架构,可以同时连接5个用户,每个用户最多可以拥有20个好友,最多可以加10个群聊,文件传输的限制字节为4096,缓存机制消息最大为50,在这些限制之内可以实现如下功能: 注册(用户名不重复):选择注册功能,输入账号密码,即可进行账号注册。 登录(单个用户登录,密码输入不可见):用户登录时,输入账号和密码,与服务端储存的信息进行匹配,匹配成功即可登录,匹配失败显示错误信息。 注销(无需再次运行程序便可登录):在功能菜单中可以退出登录

lzj_feifei的博客 2629

Linux下C实现的聊天室.rar

实现目标 一个在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 大侠,欢迎你来到咱们的聊天室” 附加功能: 文件传输

Blender学习笔记——制作向量场

#下载Blender - UE4 - FGA Vector Field Editor插件的压缩包,Github上的下载地址如下:https://github.com/isathar/Blender_UE4_VectorFieldEditor #下载并解压后将FGA VectorFields文件夹复制到C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\addons目录下,此步骤可根据blender安装的位置自行调整,只要放入addon

wychao2020的博客 1723

linux下基于socket的聊天软件

自己写的linux下聊天软件

Vinllen Chen 4338

物理专业书库模板(1):物理学书单(适合物理专业本科生、研究生、科研工作者、物理爱好者...)

物理专业书库模板(1):物理学书单(适合物理专业本科生、研究生、科研工作者、物理爱好者...) 求赞求喜欢求分享,建议关注,防止文章丢失。

hepyang的博客 2179

一个的linux聊天软件

Linux chat software : 一个linux聊天软件,使用了epoll和mysql数据库 (gitee.com)

weixin_46430043的博客 562

ais信号接收设备_国内首款基于软件无线电技术的AIS基站投入测试使用

星网宇达子公司北京凯盾环宇科技有限公司经过多年的潜心研发,成功研制出国内首台基于软件无线电(SDR)的CATON 9011 AIS基站。该基站采用软件无线电技术,利用软件数字技术代替传统的模拟电路,大大的降低了模拟器件的使用率,提高了基站的可靠性和使用年限。CATON 9011 AIS基站研制依据ITU-R M.1371-5、IEC 62320-1、IEC 61162-1等国际标准,兼容...

weixin_29057163的博客 2333

TCS34725颜色识别模块实战调校:从“一坨”例程到精准RGB

本文针对TCS34725颜色识别模块初始数据不准的问题,提供了从基础例程到精准调校的实战指南。核心在于通过白光校正与黑白光校正两种方法,补偿传感器偏差与环境光影响,将原始数据准确映射为标准RGB值。文章详细阐述了校准原理、操作步骤与代码实现,并分享了进阶调试技巧与避坑指南,帮助开发者显著提升颜色识别的准确性与项目可靠性。

weixin_29188043的博客 287

linux 开发设计软件,在Linux平台下开发和设计聊天软件

在网络无所不在的今天,在Internet上,有ICQ、MSN、Gtalk、OICQ等网络聊天软件,极大程度上方便了处于在世界各地的友人之间的相互联系,也使世界好像一下子缩小了,不管你在哪里,只要你上了网,打开这些软件,就可以给你的朋友发送信息,不管对方是否也同时在线,只要知道他有号码。Linux 操作系统作为一个开源的操作系统被越来越多的人所应用,它的好处在于操作系统源代码的公开化!只要是基于GN...

weixin_34616304的博客 263

2022年华为ICT大赛 全球总决赛!中文综合任务书-网络赛道真题!

2022年华为ICT大赛 全球总决赛!综合任务书-网络赛道真题-中文翻译版

君逍遥o 2402
上一篇: 在Linux下用C语言操作数据库sqlite3(查询)
下一篇: U-Boot直接引导zImage内核
zhouxf_cn
博客等级 码龄14年 54粉丝 24原创
评论 28
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值