網絡編程項目要求
一、 實現目標
一個在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所接收
後面做了所有的出錯處理和註釋,概要設計什麼的,發上來太麻煩了,就不發了啊