关于聊天室的服务器
服务器用来接收客户端的请求与消息,并发送消息,服务器端不需要进行操作。
服务器通过文件描述符来检测不同的客户端发送的消息
大部分的服务器采用的是多线程的方式进行接收与发送,我采用了select来监听所有文件描述符
在开启客户端时需要输入本机IPv4地址
头文件
#ifndef _SERVER_H_
#define _SERVER_H_
#define color_none "\033[0m"
#define color_red "\033[1;5;31m"
#define color_purple "\033[1;5;35m"
#define color_green "\033[1;5;32m"
#define color_yellow "\033[1;5;33m"
#define PORT 8888
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
pthread_t tid;
struct info
{
int option;
int sign;
int num;
int chat;
char username[20];
char passward[20];
char name[20];
char question[20];
char key[20];
char buf[10000];
char n[10][10];
int online_num;
char recvname[20];
char filename[20];
char member[10]; //判断是否是会员
int speak; //是否被禁言
int offline;
char agree[10];
};
struct link
{
char id[20];
char name[20];
int fd;
int port;
char member[10]; //判断是否是会员
int speak; //是否被禁言
int offline;
struct link *next;
};
typedef struct link Link;
typedef Link *link_list;
void signalhandler(void);
int list_init(link_list *list);
int list_compare(link_list list, struct info buf);
int list_insert(link_list *list, struct info buf, int fd, int port);
int list_delete(link_list *list, int fd);
int find_name(link_list list, int fd, struct info *sendbuf);
int find_recvname(link_list list, int fd, struct info *buf);
int find_fd(link_list list, struct info recvbuf);
int on_line(link_list list, struct info *sendbuf, int fd);
int sendall(link_list list, struct info buf, int fd);
int member_set(link_list *list, struct info buf, int fd);
int find_speak(link_list list, struct info recvbuf);
int speak_set(link_list *list, struct info buf);
int speak_set1(link_list *list, int fd);
int offline_set(link_list *list, int fd);
#endif
主程序
#include "chatroom.h"
int compare(void* para, int columnCount, char **columnValue, char **columnName)
{
int i;
for(i = 0; i < columnCount; i++)
{
if(strcmp(*columnValue, para) == 0)
{
return 1;
}
}
return 0;
}
int print(void* para, int columnCount, char **columnValue, char **columnName)
{
strcpy(para, *columnValue);
return 0;
}
int main(int argc, char *argv[])
{
sqlite3 *ppdb;
struct info recvbuf;
struct info sendbuf;
int sockfd, ret, cfd, fd[100] = {0}, length, i = 0, j, m = 0;
pthread_t tid;
fd_set readfds;
fd_set tmpfds;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
char sql[100] = {0};
int tmpfd;
char name[10] = {0};
link_list list;
ret = list_init(&list);
if(ret == 0)
{
printf(color_red"\t\t\t\t\t1初始化失败!!!\n"color_none);
}
//signalhandler(); //按下ctrl+c或ctrl+\时不会退出
ret = sqlite3_open("ID_PASSWARD.db", &ppdb);
if(ret != SQLITE_OK)
{
perror("sqlite3_open");
exit(1);
}
sprintf(sql,"create table if not exists ID_PASSWARD(username text, passward text, name text, question text, key text, member text);");
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
if(ret != SQLITE_OK)
{
perror("create");
exit(1);
}
system("clear");
printf("开启服务器!\n");
sockfd = socket(PF_INET, SOCK_STREAM, 0);
if(-1 == sockfd)
{
perror("socket");
exit(1);
}
int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = PF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); //上述信息绑定到socket
if (-1 == ret)
{
perror("bind");
exit(1);
}
printf("等待连接...\n");
ret = listen(sockfd, 10); //监听 最多允许10个连接
if(-1 == ret)
{
perror("listen");
exit(1);
}
FD_ZERO(&readfds);
FD_ZERO(&tmpfds);
FD_SET(sockfd, &readfds);
int maxfd = sockfd;
while(1)
{
tmpfds = readfds;
sleep(1);
ret = select(maxfd + 1, &tmpfds, NULL, NULL, NULL);
if(-1 == ret)
{
perror("select");
exit(1);
}
if(FD_ISSET(sockfd, &tmpfds))
{
length = sizeof(client_addr);
cfd = accept(sockfd, (struct sockaddr*)&client_addr, &length);
if(-1 == ret)
{
perror("accept");
exit(1);
}
printf("\n\n客户端已连接!端口号 :%d \n", client_addr.sin_port);
for(i = 0; i < 100; i++)
{
if(fd[i] <= 0)
{
fd[i] = cfd;
break;
}
}
FD_SET(cfd, &readfds);
if(cfd > maxfd)
{
maxfd = cfd;
}
}
else
{
for(j = 0; j <= maxfd; j++)
{
if(FD_ISSET(fd[j], &tmpfds))
{
memset(&recvbuf, 0, sizeof(recvbuf));
memset(&sendbuf, 0, sizeof(sendbuf));
ret = recv(fd[j], &recvbuf, sizeof(recvbuf), 0);
if(-1 == ret)
{
perror("recv");
exit(1);
}
switch(recvbuf.num)
{
case (1): //登录
{
sprintf(sql, "select username from ID_PASSWARD;");
ret = sqlite3_exec(ppdb, sql, compare, recvbuf.username, NULL);
if(ret == SQLITE_OK) //判断账号是否错误
{
sendbuf.sign = 1;
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
}
else //如果账号正确,判断密码是否正确
{
sprintf(sql, "select passward from ID_PASSWARD where username = '%s';", recvbuf.username);
ret = sqlite3_exec(ppdb, sql, compare, recvbuf.passward, NULL);
if(ret == SQLITE_OK)
{
sendbuf.sign = 2;
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
}
else //判断该账号是否已登录
{
ret = list_compare(list, recvbuf);
if(0 == ret) //如果未登录,返回信号进入聊天界面
{
sprintf(sql, "select name from ID_PASSWARD where username = '%s';", recvbuf.username);
ret = sqlite3_exec(ppdb, sql, print, &recvbuf.name, NULL);
sprintf(sql, "select member from ID_PASSWARD where username = '%s';", recvbuf.username);
ret = sqlite3_exec(ppdb, sql, print, &recvbuf.member, NULL);
printf("\t用户名 %s 登陆成功\n", recvbuf.username);
ret = list_insert(&list, recvbuf, fd[j], client_addr.sin_port); //将登陆信息插入链表
if(1 == ret)
{
sendbuf.sign = 3;
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
}
}
else if(-1 == ret) //如果已登录,返回已登录信号
{
sendbuf.sign = 4;
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
}
}
}
break;
}
case (2): ///注册
{
sprintf(sql, "select username from ID_PASSWARD;");
ret = sqlite3_exec(ppdb, sql, compare, recvbuf.username, NULL); //判断账号是否已存在
if(ret != SQLITE_OK)
{
sendbuf.sign = 2;
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
break;
}
sprintf(sql, "insert into ID_PASSWARD(username, passward, name, question, key, member) values ('%s', '%s', '%s', '%s', '%s', '%s');", recvbuf.username, recvbuf.passward, recvbuf.name, recvbuf.question, recvbuf.key, recvbuf.member);
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL); //账号信息录入表
if(ret != SQLITE_OK)
{
perror("insert");
exit(1);
}
sendbuf.sign = 1;
send(fd[j], &sendbuf, sizeof(sendbuf), 0); //返回注册成功信号
break;
}
case (3): //找回密码
{
switch(recvbuf.sign)
{
case (1): //接收账号
{
sprintf(sql, "select username from ID_PASSWARD;");
ret = sqlite3_exec(ppdb, sql, compare, recvbuf.username, NULL);
if(ret != SQLITE_OK)
{
sendbuf.sign = 2;
sprintf(sql, "select question from ID_PASSWARD where username = '%s';", recvbuf.username);
ret = sqlite3_exec(ppdb, sql, print, sendbuf.question, NULL);
if(ret == SQLITE_OK)
{
send(fd[j], &sendbuf, sizeof(sendbuf), 0); //账号匹配成功,返回成功信号与密保问题
}
}
else
{
sendbuf.sign = 1;
send(fd[j], &sendbuf, sizeof(sendbuf), 0); //账号匹配失败,返回失败信号
}
break;
}
case (2): //接收密保答案
{
sprintf(sql, "select key from ID_PASSWARD where username = '%s';", recvbuf.username);
ret = sqlite3_exec(ppdb, sql, compare, recvbuf.key, NULL);
if(ret != SQLITE_OK)
{
sendbuf.sign = 2; //答案匹配成功,返回成功信号
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
}
else
{
sendbuf.sign = 1; //答案匹配失败,返回失败信号
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
}
break;
}
case(3): //接收新密码
{
sprintf(sql, "update ID_PASSWARD set passward = '%s' where username = '%s';", recvbuf.passward, recvbuf.username);
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
sendbuf.sign = 1;
send(fd[j], &sendbuf, sizeof(sendbuf), 0); //返回修改成功
break;
}
}
break;
}
case (4): //断开连接
{
FD_CLR(fd[j], &readfds);
printf("\t\t\t客户端已断开连接\n", fd[j]);
close(fd[j]);
break;
}
case (5):
{
switch(recvbuf.option)
{
case(1):
{
find_name(list, fd[j], &sendbuf);
tmpfd = find_fd(list, recvbuf);
if(recvbuf.chat == -1)
{
ret = on_line(list, &sendbuf, fd[j]);
ret = send(fd[j], &sendbuf, sizeof(sendbuf), 0);
printf(" \n");
break;
}
else if(recvbuf.chat == 10) //发起聊天
{
if(0 == tmpfd)
{
sendbuf.sign = 1;
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
break;
}
else
{
sendbuf.sign = 2;
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
sendbuf.sign = 1; //发送给聊天对象
send(tmpfd, &sendbuf, sizeof(sendbuf), 0);
break;
}
}
else if(recvbuf.chat == 1)
{
recvbuf.sign = 2;
ret = on_line(list, &recvbuf, tmpfd);
find_recvname(list, fd[j], &recvbuf);
send(tmpfd, &recvbuf, sizeof(recvbuf), 0);
break;
}
}
case (2):
{
link_list p = list->next;
while(p != NULL)
{
if(p->fd != fd[j])
{
ret = on_line(list, &recvbuf, p->fd);
find_recvname(list, fd[j], &recvbuf);
recvbuf.sign = 3;
send(p->fd, &recvbuf, sizeof(recvbuf), 0);
}
p = p->next;
}
break;
}
case (3):
{
find_recvname(list, fd[j], &recvbuf);
tmpfd = find_fd(list, recvbuf);
if(recvbuf.sign == 1)
{
recvbuf.sign = 4;
send(tmpfd, &recvbuf, sizeof(recvbuf), 0);
break;
}
else if(recvbuf.sign == 2)
{
recvbuf.sign = 5;
send(tmpfd, &recvbuf, sizeof(recvbuf), 0);
break;
}
else if(recvbuf.sign == 3)
{
recvbuf.sign = 6;
send(tmpfd, &recvbuf, sizeof(recvbuf), 0);
break;
}
}
case (4):
{
find_name(list, fd[j], &recvbuf);
sprintf(sql, "update ID_PASSWARD set member = '%s' where username = '%s';", recvbuf.member, recvbuf.username);
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
ret = member_set(&list, recvbuf, fd[j]);
break;
}
case (5):
{
ret = find_speak(list, recvbuf);
if(0 == ret)
{
sendbuf.sign = 1;
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
break;
}
else if(-1 == ret)
{
sendbuf.sign = 2;
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
break;
}
else
{
sendbuf.sign = -1;
speak_set(&list, recvbuf);
find_name(list, fd[j], &sendbuf);
send(ret, &sendbuf, sizeof(sendbuf), 0);
sendbuf.sign = 3;
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
break;
}
}
case (6):
{
speak_set1(&list, fd[j]);
break;
}
case (7):
{
ret = find_fd(list, recvbuf);
if(0 == ret)
{
sendbuf.sign = 1;
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
break;
}
else
{
sendbuf.sign = -2;
offline_set(&list, ret);
find_name(list, fd[j], &sendbuf);
send(ret, &sendbuf, sizeof(sendbuf), 0);
sendbuf.sign = 2;
send(fd[j], &sendbuf, sizeof(sendbuf), 0);
break;
}
}
case (-1):
{
find_name(list, fd[j], &sendbuf);
ret = list_delete(&list, fd[j]);
printf("\t\t用户名 %s 已下线\n", sendbuf.username);
break;
}
}
break;
}
}
}
}
}
}
close(sockfd);
return 0;
}
接口
#include "chatroom.h"
void signalhandler(void)
{
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset,SIGINT);
sigaddset(&sigset,SIGQUIT);
sigprocmask(SIG_BLOCK, &sigset, NULL);
}
int list_init(link_list *list)
{
(*list) = (link_list)malloc(sizeof(Link));
if(NULL == (*list))
{
return 0;
}
(*list)->next = NULL;
return 1;
}
int list_compare(link_list list, struct info buf)
{
link_list p = list->next;
if(p == NULL)
{
return 0;
}
while (p != NULL)
{
if(strcmp(p->id, buf.username) == 0)
{
return -1;
}
p = p->next;
}
return 0;
}
int list_insert(link_list *list, struct info buf, int fd, int port)
{
link_list p = *list;
link_list n = (link_list)malloc(sizeof(Link));
strcpy(n->id, buf.username);
strcpy(n->name, buf.name);
strcpy(n->member, buf.member);
n->port = port;
n->fd = fd;
while(p->next != NULL)
{
p = p->next;
}
p->next = n;
n->next = NULL;
return 1;
}
int list_delete(link_list *list, int fd)
{
link_list p = *list;
link_list tmp;
if(p->next == NULL)
{
return 0;
}
while(p->next != NULL)
{
if(p->next->fd == fd)
{
tmp = p->next;
p->next = tmp->next;
tmp->next = NULL;
free(tmp);
tmp = NULL;
return 1;
}
p = p->next;
}
return 0;
}
int find_name(link_list list, int fd, struct info *sendbuf)
{
link_list p = list->next;
while(p != NULL)
{
if(p->fd == fd)
{
strcpy((*sendbuf).name, p->name);
strcpy((*sendbuf).username, p->id);
return 1;
}
p = p->next;
}
return 0;
}
int find_recvname(link_list list, int fd, struct info *buf)
{
link_list p = list->next;
while(p != NULL)
{
if(p->fd == fd)
{
strcpy((*buf).recvname, p->name);
return 1;
}
p = p->next;
}
return 0;
}
int find_fd(link_list list, struct info recvbuf)
{
int fd;
link_list p = list->next;
while(p != NULL)
{
if(strcmp(p->name, recvbuf.name) == 0)
{
fd = p->fd;
return fd;
}
p = p->next;
}
return 0;
}
int on_line(link_list list, struct info *sendbuf, int fd)
{
link_list p = list->next;
int i = 0;
while(p != NULL)
{
if(fd == p->fd)
{
strcpy((*sendbuf).username, p->id);
strcpy((*sendbuf).member, p->member);
(*sendbuf).speak = p->speak;
(*sendbuf).offline = p->offline;
}
strcpy((*sendbuf).n[i], p->name);
(*sendbuf).online_num++;
p = p->next;
i++;
}
return 1;
}
int sendall(link_list list, struct info buf, int fd)
{
link_list p = list->next;
while(p != NULL)
{
if(p->fd != fd)
{
buf.sign = 3;
send(p->fd, &buf, sizeof(buf), 0);
}
p = p->next;
}
return 0;
}
int member_set(link_list *list, struct info buf, int fd)
{
link_list p = (*list)->next;
while(p != NULL)
{
if(fd == p->fd)
{
strcpy(p->member, buf.member);
return 0;
}
p = p->next;
}
return 1;
}
int find_speak(link_list list, struct info recvbuf)
{
int fd;
link_list p = list->next;
while(p != NULL)
{
if(strcmp(p->name, recvbuf.name) == 0)
{
if(p->speak == 1)
{
return -1;
}
fd = p->fd;
return fd;
}
p = p->next;
}
return 0;
}
int speak_set(link_list *list, struct info buf)
{
link_list p = (*list)->next;
while(p != NULL)
{
if(strcmp(p->name, buf.name) == 0)
{
p->speak = 1;
return 0;
}
p = p->next;
}
return 1;
}
int speak_set1(link_list *list, int fd)
{
link_list p = (*list)->next;
while(p != NULL)
{
if(p->fd == fd)
{
p->speak = -1;
return 0;
}
p = p->next;
}
return 1;
}
int offline_set(link_list *list, int fd)
{
link_list p = (*list)->next;
while(p != NULL)
{
if(p->fd == fd)
{
p->offline = 1;
return 0;
}
p = p->next;
}
return 1;
}