多进程实现linux 下即时聊天软件
2012-07-31 11:21
176 查看
网络编程项目要求
一、 实现目标
一个在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
package.c
client.c
clientlogin.c
urrfunction.c
rootfunction.c
服务器
server.c
severlogin.c
initlib.c
seachfunction.c
transit.c
尚未解决的问题:
客户端在接收文件时,接收文件的客户端发送消息会出错
原因:服务器返回的消息被客户端接收文件的read所接收
后面做了所有的出错处理和注释,概要设计什么的,发上来太麻烦了,就不发了啊
一、 实现目标
一个在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中select实现高性能服务器以及与多进程服务器对比
- linux网络编程:使用多进程实现socket同时收发数据
- Linux下实现PHP多进程的方法分享
- Linux网络编程之多进程模型编程与一个使用进程池实现的CGI服务器
- Linux多进程之间LOG模块的实现方式讨论
- linux网络编程:使用多进程实现socket同时收发数据
- 多进程与多线程(八)--Linux 线程实现机制分析 (转)
- Linux网络编程之TCP编程,多进程实现多客户端编程
- Linux:多进程、多线程服务器的实现解析(有图有代码有真相!!!)
- linux下 socket tcp Server c语言编写(分别实现单进程,多进程,多线程)
- Linux下实现PHP多进程的方法分享
- PHP 在linux环境下实现多进程,如何子类不产生defunct
- 在linux-2.6.26内核中有关于ARM中断实现的详解
- 用Shell脚本实现自动化完成属于自己的微型Linux!(二)
- Linux中Apache+Tomcat+JK实现负载均衡和群集的完整过程
- linux使用rsync+inotify-tools+ssh实现文件实时同步
- linux下如何实现mysql数据库每天定时自动备份
- java程序实现Linux下的服务的启停、注册、异常
- 一个Linux下C线程池的实现
- [linux 0.11]写时复制的实现