网络编程项目要求
一、 实现目标
一个在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所接收
后面做了所有的出错处理和注释,概要设计什么的,发上来太麻烦了,就不发了啊