一个在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端维护一个所有登陆用户的聊天会的记录文件,以便备查
首先,就是服务器与客户端之间的协议要如何选择,以及整个项目的一个大概框架。这里采用多路转接型架构,使用I/O多路机制。简单来说就是用Select函数去做服务器端。
然后要清楚这个项目要分成多少个子模块,这里我把它暂时先分成最简单的几个模块即能满足基本要求。每个模块都是一个选项,我在结构体里写了一个标志位,给各个子模块标号,比如注册是1,登录是2,那么每次结构体传到服务器时,服务器判断这个标志位来检测你需要服务器去处理什么功能,再发送结果给客户端做相应处理。
子模块设计
(一) 注册:
登录之前如果没有账号则需要注册账号。这里需要建立数据库来存放注册的账号,第一个界面就是登录注册的界面,如果登录成功就进入聊天的界面,没有账号则进入注册界面。数据库的建立要在服务器上进行,所以服务器一开始就要建立一个数据库,然后客户端去访问数据库。
(二) 登录
登录同注册其实很像,也要到数据库里遍历一下看是否重复ID,再判断账号密码是否一致才可允许登录。登录有些不一样的就是需要判断是否已经在线,并且登录以后要获得其用户的信息,要解决这个问题就需要一个链表,这个链表存储着所有登录用户的信息,以便服务器回馈给客户端一些信息,每次需要一些其他用户的信息时直接从链表获取就可以了,当然登录的账号信息还要保存在数据库里。
(三) 查看在线用户
这里就要用到登录里说的那个链表了,因为链表时存储已登录的用户信息,从这里可以获得其他在线用户的信息。
(四) 私聊
私聊也是通过链表的关系找到你想要聊天的对象,可以通过昵称或者账号去寻找,需要注意的点就是如何结束对话。
(五) 群聊
群聊的意义就是你发与一句话,参加群聊的人都能看到,就是说要给在线的并且参加群聊的用户发送消息,这里可以给结构体一个群聊标志位,这样似乎容易遍历时分辨出来群聊用户。
(六) 查看聊天记录
这里要在客户端的目录下建立一个文本文件用来存储聊天记录,关键在于如何去存储属于谁和谁的聊天记录,需要好好构思一下。
(七) 附加功能
附加了踢人与禁言的功能,在传数据包的结构体中添加了VIP的信号位,当VIP位置1时,该用户拥有VIP权限,即踢人和禁言功能。踢人我设计的是直接踢下线,而禁言就是私聊群聊都不会收到消息,通过禁言标志位实现, 踢人直接下线就行了。
这是学完C做的一个比较全面的项目了,肯定还有很多不足的地方,我会在日后改进这个项目并传上博客,估计会换个模式,比如TCP/IP,还有我觉得把登录的包和聊天的包放在一起效率太低了,可以分成两个种类的包,一个登录包,一个聊天包,这样实施起来会有效率些。第一次做比较大的项目,很多地方都很稚嫩,希望各位看客多多包涵~ 代码如下:
#ifndef _CHATHEAD_
#define _CHATHEAD_
#define PORT 8888
#define success 10000
#define failure 10001
struct login
{
char id[20];
char password[20];
char nickname[20];
char personalsign[50];
int like;
int vip;
int ban;
char onlineusr[10][20];
char toname[20];
char buf[100];
int admin;
int rdnum;
int tofd; //目标文件符
int option; //选项标志位
int flag; //状态标志位
struct login *next;
};
typedef struct login Node;
typedef struct login *List;
int ListInit (List *L);
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ChatHead.h"
#include <sqlite3.h>
int ListInit (List *L)
{
(*L) = (List)malloc(sizeof(Node));
if(NULL == (*L))
{
return failure;
}
(*L)->next = NULL;
return success;
}
#include
#include
#include
#include "ChatHead.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 8888
#define registerask 1001
#define loginask 1002
#define idinfo 1003
#define viewonline 1004
#define singlechat 1005
#define chatall 1006
#define filesend 1007
#define likeadd 1008
#define altersign 1009
#define bansay 1010
#define kickusr 1011
#define buyvip 1012
#define loginout 1013
int recordfd;
int main()
{
int sockfd, ret, fd[100] ={0}, i = 0, j, length, maxfd;
int m = 0, n = 1; //用来检索数据库里的id password
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
fd_set readfds, tmpfds;
struct login RecvBuf;
sqlite3 *ppdb;
int flag = 0;
char sql[150] = {0};
char **pResult;
int recordcount = 0;
int rowcount = 1;
List list;
List plist;
List tmplist;
List newlogin;
List freelist;
bzero(&newlogin, sizeof(newlogin));
int u = 0;
ret = ListInit(&list);
if(failure == ret)
{
printf("ListInit Failure!\n");
}
else
{
printf("ListInit Success!\n");
}
ret = sqlite3_open("Regiseter.db", &ppdb);//数据库
if(ret != SQLITE_OK)
{
perror("sqlite3_open");
exit(1);
}
sprintf(sql, "create table if not exists login(id text, password text, nickname text, personalsign text, like integer, vip integer, ban integer);");
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("sqlite3_exec");
exit(1);
}
bzero(&sql, sizeof(sql));
sockfd = socket(PF_INET, SOCK_STREAM, 0); //通道
if(sockfd < 0)
{
perror("socket");
exit(1);
}
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr("192.168.1.116");
int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if(ret < 0)
{
perror("bind");
exit(1);
}
ret = listen(sockfd, 50);
if(ret < 0)
{
perror("listen");
exit(1);
}
FD_ZERO(&readfds);
FD_ZERO(&tmpfds);
FD_SET(sockfd, &readfds);
maxfd = sockfd;
while(1)
{
bzero(&pResult, sizeof(pResult));
bzero(&sql, sizeof(sql));
m = 7;
n = 8;
u = 0;
flag = 0;
rowcount = 0;
plist = list->next; //链表指针每次一开始都指向链表的第一个元素
tmplist = list;
freelist = tmplist;
tmpfds = readfds;
ret = select(maxfd + 1, &tmpfds, NULL, NULL, NULL);
if(-1 == ret)
{
perror("select");
exit(1);
}
if(FD_ISSET(sockfd,&tmpfds))
{
length = sizeof(client_addr);
fd[i] = accept(sockfd, (struct sockaddr *)&client_addr, &length);
if(fd[i] < 0)
{
perror("accept");
exit(1);
}
printf("Accept %d Port: %d\n", fd[i], client_addr.sin_port);
FD_SET(fd[i], &readfds);
maxfd = fd[i];
i++;
}
else
{
for(j = 0; j < i; j++)
{
if(FD_ISSET(fd[j], &tmpfds))
{
bzero(&RecvBuf, sizeof(RecvBuf));
ret = recv(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("recv");
exit(1);
}
switch (RecvBuf.option)
{
case 1001:
while(plist != NULL) //如果登录链表里已存在此ID则重复登录
{
if(!strcmp(plist->id, RecvBuf.id))
{
flag = 2;
break;
}
plist = plist->next;
tmplist = tmplist->next;
}
if(flag == 2)
{
ret = send(fd[j], &flag, sizeof(flag), 0);
if(ret < 0)
{
perror("sendrelogin");
exit(1);
}
break;
}
sprintf(sql, "select * from login;");
ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("sqlite3_get_table1");
exit(1);
}
if(recordcount == 0)
{
flag = 0;
ret = send(fd[j], &flag, sizeof(int), 0);
if(ret < 0)
{
perror("sendflag3");
exit(1);
}
break;
}
while(rowcount < recordcount)
{
if((!strcmp(pResult[m], RecvBuf.id)) && (!strcmp(pResult[n], RecvBuf.password))) //若数据库有此ID则登录成功
{
flag = 1; //flag = 1表明可以登录
strcpy(RecvBuf.nickname, pResult[m + 2]);
RecvBuf.vip = *pResult[m + 5];
RecvBuf.ban = *pResult[m + 6];
break;
}
m += 7;
n += 7;
rowcount++;
}
ret = send(fd[j], &flag, sizeof(int), 0);
if(ret < 0)
{
perror("sendflag1");
exit(1);
}
if(flag == 1)
{
newlogin = (List)malloc(sizeof(Node));
strcpy(newlogin->id, RecvBuf.id);
strcpy(newlogin->password, RecvBuf.password);
strcpy(newlogin->nickname, RecvBuf.nickname);
newlogin->ban = RecvBuf.ban;
newlogin->vip = RecvBuf.vip;
newlogin->tofd = fd[j];
tmplist->next = newlogin;
newlogin->next = plist; //插入链表
/* recordfd = open("chatrecord.txt", O_WRONLY); //记录用户登录时间
if(-1 == recordfd)
{
perror("openrecordfd");
exit(1);
}*/
}
break;
case 1002 :
sprintf(sql, "select * from login;");
ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("sqlite3_get_table2");
exit(1);
}
if(recordcount == 0) //如果没有记录直接可以注册
{
flag = 1;
bzero(&sql, sizeof(sql));
sprintf(sql, "insert into login(id, password, nickname, personalsign, like, vip, ban) values('%s', '%s', '%s', '%s', %d, %d, %d);", RecvBuf.id, RecvBuf.password, RecvBuf.nickname, RecvBuf.personalsign, RecvBuf.like, RecvBuf.vip, RecvBuf.ban);
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL); //插入注册记录到数据库中
if(ret != SQLITE_OK)
{
perror("sqlite4_insert");
exit(1);
}
ret = send(fd[j], &flag, sizeof(int), 0);
if(ret < 0)
{
perror("sendflag2");
exit(1);
}
break;
}
while(rowcount < recordcount)
{
if(!strcmp(pResult[m], RecvBuf.id)) //若数据库有此ID则不可以注册
{
flag = 2; //flag = 2表明不可以注册
break;
}
m += 7;
n += 7;
rowcount++;
}
if(flag == 2)
{
ret = send(fd[j], &flag, sizeof(int), 0);
if(ret < 0)
{
perror("sendflag1");
exit(1);
}
break;
}
flag = 1; //flag = 1表示可以注册
bzero(&sql, sizeof(sql));
sprintf(sql, "insert into login(id, password, nickname, personalsign, like, vip, ban) values('%s', '%s', '%s', '%s', %d, %d, %d);", RecvBuf.id, RecvBuf.password, RecvBuf.nickname, RecvBuf.personalsign, RecvBuf.like, RecvBuf.vip, RecvBuf.ban);
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL); //插入注册记录到数据库中
if(ret != SQLITE_OK)
{
perror("sqlite3_insert");
exit(1);
}
ret = send(fd[j], &flag, sizeof(int), 0);
if(ret < 0)
{
perror("sendflag2");
exit(1);
}
break;
case 1003:
sprintf(sql, "select * from login;");
ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("sqlite3_get_table1003");
exit(1);
}
while(rowcount < recordcount)
{
if(!strcmp(pResult[m], RecvBuf.id))
{
strcpy(RecvBuf.nickname, pResult[m + 2]);
strcpy(RecvBuf.personalsign, pResult[m + 3]);
RecvBuf.like = (*pResult[m + 4] - 48);
}
m += 7;
rowcount++;
}
RecvBuf.flag = 3;
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("sendinfo1003");
exit(1);
}
break;
case 1004: //在线用户
tmplist = plist;
while(tmplist)
{
if(!strcmp(RecvBuf.id, tmplist->id))
{
strcpy(RecvBuf.nickname, tmplist->nickname);
break;
}
tmplist = tmplist->next;
}
plist = list->next;
while(plist != NULL)
{
strcpy(RecvBuf.onlineusr[u], plist->nickname);
plist = plist->next;
u++;
}
RecvBuf.flag = 0;
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("sendonline");
exit(1);
}
break;
case 1005: //私聊
tmplist = plist;
while(tmplist)
{
if(!strcmp(RecvBuf.id, tmplist->id))
{
strcpy(RecvBuf.nickname, tmplist->nickname);
break;
}
tmplist = tmplist->next;
}
plist = list->next;
while(plist != NULL)
{
if(!strcmp(RecvBuf.toname, plist->nickname))
{
if(tmplist->ban == 48)
{
RecvBuf.flag = 1;
ret = send(plist->tofd, &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("sendbuf1005");
exit(1);
}
}
else
{
RecvBuf.flag = 13;
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("send13");
exit(1);
}
}
break;
}
plist = plist->next;
}
if(RecvBuf.flag == 0)
{
RecvBuf.flag = 11;
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0); //考虑对方不在线
if(ret < 0)
{
perror("sendflag1005");
exit(1);
}
}
break;
case 1006: //群聊
tmplist = plist;
while(tmplist)
{
if(!strcmp(RecvBuf.id, tmplist->id))
{
strcpy(RecvBuf.nickname, tmplist->nickname);
break;
}
tmplist = tmplist->next;
}
plist = list->next;
if(tmplist->ban == 48)
{
while(plist)
{
RecvBuf.flag = 1;
ret = send(plist->tofd, &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("sendallserver");
exit(1);
}
plist = plist->next;
}
}
else
{
RecvBuf.flag = 13;
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("send13");
exit(1);
}
}
break;
case 1007: //文件传输
while(plist)
{
if(!strcmp(RecvBuf.toname, plist->nickname))
{
RecvBuf.flag = 2;
ret = send(plist->tofd, &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("sendfile1");
exit(1);
}
}
plist = plist->next;
}
break;
case 1008: //点赞
bzero(&sql, sizeof(sql));
sprintf(sql, "select * from login;");
ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("sqlite3_get_table1");
exit(1);
}
while(rowcount < recordcount)
{
if(!strcmp(pResult[m + 2], RecvBuf.toname))
{
RecvBuf.flag = 4;
break;
}
rowcount++;
m += 7;
}
if(RecvBuf.flag == 4)
{
RecvBuf.like = *pResult[m + 4];
RecvBuf.like += 1;
bzero(&sql, sizeof(sql));
sprintf(sql, "update login set like = %d where nickname = '%s';", RecvBuf.like, RecvBuf.toname);
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("updatelike");
exit(1);
}
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("sendlike1");
exit(1);
}
}
else
{
RecvBuf.flag = 12;
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("sendlike12");
exit(1);
}
}
break;
case 1009: //修改个性签名
bzero(&sql, sizeof(sql));
sprintf(sql, "select * from login;");
ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("sqlite3_get_table1");
exit(1);
}
while(rowcount < recordcount)
{
if(!strcmp(pResult[m], RecvBuf.id))
{
RecvBuf.flag = 5;
break;
}
rowcount++;
m += 7;
}
if(RecvBuf.flag == 5)
{
bzero(&sql, sizeof(sql));
sprintf(sql, "update login set personalsign = '%s' where id = '%s';", RecvBuf.personalsign, RecvBuf.id);
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("updatesign");
exit(1);
}
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("sendsign1");
exit(1);
}
}
else
{
RecvBuf.flag = 12;
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("sendsign12");
exit(1);
}
}
break;
case 1010: //禁言
bzero(&sql, sizeof(sql));
sprintf(sql, "select * from login;");
ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("sqlite3_get_table1");
exit(1);
}
while(rowcount < recordcount)
{
if(!strcmp(pResult[m], RecvBuf.id))
{
if(49 == *pResult[m + 5])
{
RecvBuf.flag = 6;
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("send6");
exit(1);
}
break;
}
else
{
RecvBuf.flag = 61;
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("send61");
exit(1);
}
break;
}
}
rowcount++;
m += 7;
}
if(RecvBuf.flag == 6)
{
bzero(&sql, sizeof(sql));
sprintf(sql, "update login set ban = 1 where id = '%s';", RecvBuf.toname);
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("updateban");
exit(1);
}
while(plist)
{
if(!strcmp(RecvBuf.toname, plist->nickname))
{
plist->ban = 49;
break;
}
plist = plist->next;
}
}
break;
case 1011: //踢人
bzero(&sql, sizeof(sql));
sprintf(sql, "select * from login;");
ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("sqlite3_get_table1");
exit(1);
}
while(rowcount < recordcount)
{
if(!strcmp(pResult[m], RecvBuf.id))
{
if(49 == *pResult[m + 5])
{
RecvBuf.flag = 7;
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("send6");
exit(1);
}
break;
}
else
{
RecvBuf.flag = 71;
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("send71");
exit(1);
}
break;
}
}
rowcount++;
m += 7;
}
tmplist = plist;
while(tmplist)
{
if(!strcmp(RecvBuf.id, tmplist->id))
{
strcpy(RecvBuf.nickname, tmplist->nickname);
break;
}
tmplist = tmplist->next;
}
plist = list->next;
tmplist = list;
if(RecvBuf.flag == 7)
{
while(plist)
{
if(!strcmp(RecvBuf.toname, plist->nickname))
{
RecvBuf.flag = 73;
ret = send(plist->tofd, &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("send73");
exit(1);
}
freelist = plist;
plist = plist->next;
tmplist->next = plist;
free(freelist);
freelist = NULL;
break;
}
plist = plist->next;
tmplist = tmplist->next;
}
}
break;
case 1012: //购买VIP
bzero(&sql, sizeof(sql));
sprintf(sql, "select * from login;");
ret = sqlite3_get_table(ppdb, sql, &pResult, &recordcount, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("sqlite3_get_table1");
exit(1);
}
while(rowcount < recordcount)
{
if(!strcmp(pResult[m], RecvBuf.id))
{
RecvBuf.flag = 8;
ret = send(fd[j], &RecvBuf, sizeof(RecvBuf), 0);
if(ret < 0)
{
perror("send8");
exit(1);
}
break;
}
rowcount++;
m += 7;
}
if(RecvBuf.flag == 8)
{
bzero(&sql, sizeof(sql));
sprintf(sql, "update login set vip = 1 where id = '%s';", RecvBuf.id);
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("updatevip");
exit(1);
}
}
break;
case 1013: //退出
tmplist = plist;
while(tmplist)
{
if(!strcmp(RecvBuf.id, tmplist->id))
{
strcpy(RecvBuf.nickname, tmplist->nickname);
break;
}
tmplist = tmplist->next;
}
tmplist = list;
plist = list->next;
while(plist)
{
if(!strcmp(RecvBuf.nickname, plist->nickname))
{
freelist = plist;
plist = plist->next;
tmplist->next = plist;
free(freelist);
freelist = NULL;
break;
}
plist = plist->next;
tmplist = tmplist->next;
}
break;
default :
break;
}
}
}
}
}
return 0;
}
#include
#include
#include
#include "ChatHead.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 8888
#define registerask 1001
#define loginask 1002
pthread_t tid;
pthread_t tid1;
int fd[3] = {0};
struct login SendChat;
void *returnchat(void *arg)
{
int ret, oldtype;
char buf[100] = {0};
char recordchat[4][100];
signal(SIGINT, SIG_IGN);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
while(1)
{
strcpy(SendChat.buf, buf);
scanf("%s", SendChat.buf);
ret = send(*(int *)arg, &SendChat, sizeof(SendChat), 0);
if(ret < 0)
{
perror("sendchatreturn");
exit(1);
}
bzero(&recordchat, sizeof(recordchat));
time_t timep;
time(&timep);
strcpy(recordchat[0], "To:"); //用结构体完善
strcpy(recordchat[1], SendChat.toname);
strcpy(recordchat[2], SendChat.buf);
strcpy(recordchat[3], asctime(gmtime(&timep)));
fd[2] = open("recordchat.txt", O_WRONLY);
if(-1 == fd[2])
{
perror("chatopen");
exit(1);
}
ret = write(fd[2], &recordchat, sizeof(recordchat));
if(-1 == ret)
{
perror("writechat");
exit(1);
}
close(fd[2]);
if(!strcmp(SendChat.buf, "bye"))
{
break;
}
}
pthread_exit(NULL);
}
void *Recv(void *arg)
{
int ret, oldtype;
struct login RecvChat;
char recordchat[4][100];
int g = 0;
int choice;
signal(SIGINT, SIG_IGN);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
while(1)
{
bzero(&RecvChat, sizeof(RecvChat));
ret = recv(*(int *)arg, &RecvChat, sizeof(RecvChat), 0);
if(ret < 0)
{
perror("recv");
exit(1);
}
switch (RecvChat.flag)
{
case 0:
while(*RecvChat.onlineusr[g] != 0)
{
printf("%s\n", RecvChat.onlineusr[g]);
g++;
}
break;
case 1: //收到的消息
printf("\nFrom %s:\n", RecvChat.nickname);
printf("%s\n", RecvChat.buf);
printf("\n是否回复?\n1.是 2.否\n");
scanf("%d", &choice);
if(choice == 1)
{
bzero(&SendChat, sizeof(SendChat));
strcpy(SendChat.id, RecvChat.id);
strcpy(SendChat.toname, RecvChat.nickname);
SendChat.tofd = RecvChat.tofd;
SendChat.option = RecvChat.option;
printf("\t\t\t\t\t聊天结束输入bye!\n");
ret = pthread_create(&tid1, NULL, returnchat, &arg);
if(ret != 0)
{
perror("pthread_create1");
exit(1);
}
}
else
{
usleep(50);
}
printf("\t\t\t\t\t\t\t\t\t\t\t\n");
break;
case 11: //不在线
printf("\n该用户不在线!\n");
break;
case 12:
printf("\n该用户不存在!\n");
break;
case 13:
printf("\n您已被禁言!\n");
break;
case 2: //文件接收
fd[1] = open("hellochat.txt", O_CREAT | O_EXCL | O_WRONLY, 755);
if(-1 == fd[1])
{
perror("opennew");
exit(1);
}
ret = write(fd[1], RecvChat.buf, RecvChat.rdnum);
if(-1 == ret)
{
perror("write");
exit(1);
}
break;
case 3:
printf("\t\t\t\t\t\t\t\t\t\t\t*********************************\n");
printf("\t\t\t\t\t\t\t\t\t\t\t*赞:%d *%s\n", RecvChat.like, RecvChat.personalsign);
printf("\t\t\t\t\t\t\t\t\t\t\t*********************************\n");
printf("\t\t\t\t\t\t\t\t\t\t\t*%s \n", RecvChat.nickname);
printf("\t\t\t\t\t\t\t\t\t\t\t* \n");
break;
case 4:
printf("\n\t点赞成功!\n");
break;
case 5:
printf("\n\t修改修改个性签名成功!\n");
break;
case 6:
printf("\n\t禁言成功!\n");
break;
case 61:
printf("\n\t禁言失败,您不是VIP,没有权限!\n");
break;
case 7:
printf("\n\t踢人成功!\n");
break;
case 71:
printf("\n\t踢人失败,您不是VIP,没有权限!\n");
break;
case 73:
printf("\n\t你已被踢出聊天室!\n");
exit(1);
break;
case 8:
printf("\n\tVIP购买成功!(若已是VIP则自动延期)\n");
break;
default:
sleep(1);
break;
}
}
}
int main()
{
int sockfd, ret, choice;
struct sockaddr_in server_addr;
struct login Login;
int flag;
char recordchat[4][100];
signal(SIGINT, SIG_IGN);
sockfd = socket(PF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("socket");
exit(1);
}
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons (PORT);
server_addr.sin_addr.s_addr = inet_addr("192.168.1.116");
ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if(ret < 0)
{
perror("connect");
exit(1);
}
while(1)
{
system("clear");
printf("\t\t\t\t\t _________________________________________________________ \n");
printf("\t\t\t\t\t|*|*********************************************************|*|\n");
printf("\t\t\t\t\t|*| Welcome to ChatHome |*|\n");
printf("\t\t\t\t\t|*| |*|\n");
printf("\t\t\t\t\t|*| |*|\n");
printf("\t\t\t\t\t|*| |*|\n");
printf("\t\t\t\t\t|*| |*|\n");
printf("\t\t\t\t\t|*| |*|\n");
printf("\t\t\t\t\t|*| 1.登录 |*|\n");
printf("\t\t\t\t\t|*| 2.注册 |*|\n");
printf("\t\t\t\t\t|*| |*|\n");
printf("\t\t\t\t\t|*|*********************************************************|*|\n");
flag = 0;
printf("Please input choice:\n");
scanf("%d", &choice);
if(1 == choice)
{
bzero(&Login, sizeof(Login));
printf("\t\t\t\t\t ********************************************************* \n");
printf("\t\t\t\t\t 请输入帐号密码: \n");
printf("\t\t\t\t\t 帐号:");
scanf("%s", Login.id);
printf("\n");
printf("\t\t\t\t\t 密码:");
scanf("%s", Login.password);
Login.option = 1001; //登录
ret = send(sockfd, &Login, sizeof(Login), 0); //发送登录请求
if(ret < 0)
{
perror("sendlogin");
}
else
{
printf("登录中...\n");
printf("loading...\n");
}
sleep(1);
ret = recv(sockfd, &flag, sizeof(flag), 0); //接收登录许可
if(ret < 0)
{
perror("recvflag1");
exit(1);
}
if(1 == flag)
{
printf("登录成功!正在跳转...\n");
system("clear");
break;
}
else if(flag == 2)
{
printf("该帐户已登录!\n");
sleep(1);
}
else
{
printf("登录失败!\n");
sleep(1);
}
}
else if(2 == choice)
{
bzero(&Login, sizeof(Login));
printf("\t\t\t\t\t *********************************************************\n");
printf("\t\t\t\t\t 请输入帐号密码: \n");
printf("\t\t\t\t\t 帐号:");
scanf("%s", Login.id);
printf("\n");
printf("\t\t\t\t\t 密码:");
scanf("%s", Login.password);
printf("\n");
printf("\t\t\t\t\t 昵称:");
scanf("%s", Login.nickname);
Login.option = 1002; //注册
ret = send(sockfd, &Login, sizeof(Login), 0);
if(ret < 0)
{
perror("sendregister");
}
else
{
printf("注册中...\n");
printf("loading...\n");
}
sleep(1);
ret = recv(sockfd, &flag, sizeof(flag), 0); //接收注册许可
if(ret < 0)
{
perror("recvflag2");
exit(1);
}
if(1 == flag)
{
printf("注册成功!正在跳转...\n");
sleep(2);
}
else if(flag == 2)
{
printf("该id已存在\n");
printf("注册失败!\n");
sleep(1);
}
}
else
{
sleep(1);
}
}
ret = pthread_create(&tid, NULL, Recv, (void *)&sockfd);
if(ret != 0)
{
perror("pthread_create");
exit(1);
}
while(1)
{
Login.option = 1003; //获取数据库用户信息
ret = send(sockfd, &Login, sizeof(Login), 0);
if(ret < 0)
{
perror("send1003");
exit(1);
}
usleep(900);
printf("\t\t\t\t\t\t\t\t\t\t\t*********************************\n");
printf("\t\t\t\t\t\t\t\t\t\t\t*1.查看在线好友 \n");
printf("\t\t\t\t\t\t\t\t\t\t\t*2.私聊 \n");
printf("\t\t\t\t\t\t\t\t\t\t\t*3.群聊 \n");
printf("\t\t\t\t\t\t\t\t\t\t\t*4.文件传输 \n");
printf("\t\t\t\t\t\t\t\t\t\t\t*5.点赞 \n");
printf("\t\t\t\t\t\t\t\t\t\t\t*6.修改个性签名 \n");
printf("\t\t\t\t\t\t\t\t\t\t\t*7.禁言(VIP) \n");
printf("\t\t\t\t\t\t\t\t\t\t\t*8.踢人(VIP) \n");
printf("\t\t\t\t\t\t\t\t\t\t\t*9.购买VIP \n");
printf("\t\t\t\t\t\t\t\t\t\t\t*10.刷新 \n");
printf("\t\t\t\t\t\t\t\t\t\t\t*11.退出 \n");
printf("\t\t\t\t\t\t\t\t\t\t\t* \n");
printf("\t\t\t\t\t\t\t\t\t\t\t*********************************\n");
printf("\t\t\t\t\t\t\t\t\t\t\t请输入您的选项: ");
scanf("%d", &choice);
switch (choice)
{
case 1:
Login.option = 1004; //查看在线好友
ret = send(sockfd, &Login, sizeof(Login), 0);
if(ret < 0)
{
perror("send1004");
exit(1);
}
printf("\t\t\t\t在线用户:\n");
sleep(1);
break;
case 2: //私聊
Login.option = 1005;
bzero(&Login.buf, sizeof(Login.buf));
printf("\tWho do you want to talk with?\n");
scanf("%s", Login.toname);
printf("\t是否使用常用短语?\n\t1.是 2.否\n");
scanf("%d", &choice);
if(choice == 1)
{
printf("\n\tInput Info:\t\t\t\t常用短语:\n");
printf("\t \t\t\t\t1.你好!\n");
printf("\t \t\t\t\t2.早上好!\n");
printf("\t \t\t\t\t3.中午好!\n");
printf("\t \t\t\t\t4.晚上好!\n");
printf("\t \t\t\t\t5.现在有事不方便联系!\n");
printf("\t \t\t\t\t6.哦\n");
printf("\t \t\t\t\t7.呵呵\n");
printf("\t \t\t\t\t8.知道了!\n");
scanf("%s", Login.buf);
switch(Login.buf[0])
{
case 49:
strcpy(Login.buf, "你好!");
break;
case 50:
strcpy(Login.buf, "早上好!");
break;
case 51:
strcpy(Login.buf, "中午好!");
break;
case 52:
strcpy(Login.buf, "晚上好!");
break;
case 53:
strcpy(Login.buf, "现在有事不方便联系!");
break;
case 54:
strcpy(Login.buf, "哦");
break;
case 55:
strcpy(Login.buf, "呵呵");
break;
case 56:
strcpy(Login.buf, "知道了!");
break;
default:
break;
}
}
else
{
printf("Input\n");
scanf("%s", Login.buf);
}
ret = send(sockfd, &Login, sizeof(Login), 0);
if(ret < 0)
{
perror("sendsigle");
exit(1);
}
bzero(&recordchat, sizeof(recordchat));
time_t timep;
time(&timep);
strcpy(recordchat[0], "To:"); //用结构体完善
strcpy(recordchat[1], Login.toname);
strcpy(recordchat[2], Login.buf);
strcpy(recordchat[3], asctime(gmtime(&timep)));
fd[2] = open("recordchat.txt", O_WRONLY);
if(-1 == fd[2])
{
perror("chatopen");
exit(1);
}
ret = write(fd[2], &recordchat, sizeof(recordchat));
if(-1 == ret)
{
perror("writechat");
exit(1);
}
close(fd[2]);
sleep(1);
printf("\t是否继续聊天?\n1.是 2.否\n");
scanf("%d", &choice);
if(choice == 1)
{
printf("\t\t\t\t\t聊天结束输入bye!\n");
while(1)
{
scanf("%s", Login.buf);
ret = send(sockfd, &Login, sizeof(Login), 0);
if(ret < 0)
{
perror("sendchat");
exit(1);
}
bzero(&recordchat, sizeof(recordchat));
time_t timep;
time(&timep);
strcpy(recordchat[0], "To:"); //用结构体完善
strcpy(recordchat[1], Login.toname);
strcpy(recordchat[2], Login.buf);
strcpy(recordchat[3], asctime(gmtime(&timep)));
fd[2] = open("recordchat.txt", O_WRONLY);
if(-1 == fd[2])
{
perror("chatopen");
exit(1);
}
ret = write(fd[2], &recordchat, sizeof(recordchat));
if(-1 == ret)
{
perror("writechat");
exit(1);
}
close(fd[2]);
if(!strcmp(Login.buf, "bye"))
{
break;
}
}
pthread_join(tid1, NULL);
}
sleep(1);
break;
case 3: //群聊
Login.option = 1006;
bzero(&Login.buf, sizeof(Login.buf));
printf("Input:\n");
scanf("%s", Login.buf);
ret = send(sockfd, &Login, sizeof(Login), 0);
if(ret < 0)
{
perror("sendall");
exit(1);
}
strcpy(recordchat[0], "To:");
strcpy(recordchat[1], "All");
strcpy(recordchat[2], Login.buf);
strcpy(recordchat[3], asctime(gmtime(&timep)));
fd[2] = open("recordchat.txt", O_WRONLY);
if(-1 == fd[2])
{
perror("chatopen");
exit(1);
}
ret = write(fd[2], &recordchat, sizeof(recordchat));
if(-1 == ret)
{
perror("writechat");
exit(1);
}
close(fd[2]);
sleep(1);
break;
case 4:
Login.option = 1007;
bzero(&Login.buf, sizeof(Login.buf));
printf("本地文件:\n");
system("ls");
// printf("请输入发送文件:\n"); //指定发送文件hello.txt
printf("请输入接收对象:\n");
scanf("%s", Login.toname);
fd[0] = open("hello.txt", O_RDONLY);
if(-1 == fd[0])
{
perror("open1");
exit(1);
}
while((Login.rdnum = read(fd[0], Login.buf, sizeof(Login.buf))) != 0)
{
ret = send(sockfd, &Login, sizeof(Login), 0);
if(ret < 0)
{
perror("sendfile");
exit(1);
}
bzero(&Login.buf, sizeof(Login.buf));
}
printf("传输中...\n");
sleep(1);
break;
case 5: //点赞
Login.option = 1008;
printf("你想给谁点赞?\n");
scanf("%s", Login.toname);
ret = send(sockfd, &Login, sizeof(Login), 0);
if(ret < 0)
{
perror("sendlike");
exit(1);
}
sleep(1);
break;
case 6: //修改个性签名
Login.option = 1009;
printf("输入新的签名:\n");
scanf("%s", Login.personalsign);
ret = send(sockfd, &Login, sizeof(Login), 0);
if(ret < 0)
{
perror("sendsign");
exit(1);
}
usleep(900);
break;
case 7: //禁言
Login.option = 1010;
printf("你想禁言谁?\n");
scanf("%s", Login.toname);
ret = send(sockfd, &Login, sizeof(Login), 0);
if(ret < 0)
{
perror("sendban");
exit(1);
}
usleep(900);
break;
case 8:
Login.option = 1011; //踢人
printf("你想踢走谁?\n");
scanf("%s", Login.toname);
ret = send(sockfd, &Login, sizeof(Login), 0);
if(ret < 0)
{
perror("sendkick");
exit(1);
}
sleep(1);
break;
case 9:
Login.option = 1012;
printf("是否购买VIP?\n1.是 2.否\n");
scanf("%d", &choice);
printf("购买中...\n");
sleep(1);
if(choice == 1)
{
ret = send(sockfd, &Login, sizeof(Login), 0);
if(ret < 0)
{
perror("send1012");
exit(1);
}
usleep(900);
break;
}
else
{
usleep(900);
break;
}
case 10: //刷新
printf("\t刷新成功!\n");
system("clear");
sleep(1);
break;
case 11: //退出
Login.option = 1013;
ret = send(sockfd, &Login, sizeof(Login), 0);
if(ret < 0)
{
perror("sendloginout");
exit(1);
}
pthread_cancel(tid);
flag = 9;
break;
default:
break;
}
if(9 == flag)
{
break;
}
}
close(sockfd);
return 0;
}