使用poll函数
登录注册功能,不能重复登录,重复注册
单词查询功能
历史记录功能,存储单词,意思,以及查询时间
基于TCP,支持多客户端连接
采用数据库保存用户信息与历史记录
将dict.txt的数据导入到数据库中保存。
客户端
#include "client.h"
int main(int argc, char const *argv[])
{
if (argc != 3)
{
printf("please input %s <ip> <port>\n", argv[0]);
return -1;
}
//1.创建套接字 socket
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket err.");
return -1;
}
printf("socket ok %d\n", sockfd);
//填充ipv4的通信结构体 服务器端ip和端口
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
//&serveraddr -->struct sockaddr_in *
//2.请求链接 connect
if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{
perror("connect err.");
return -1;
}
printf("connect ok.\n");
MSG msg;
//3.循环收发消息
while (1)
{
int sum = 0;
printf("******************************\n");
printf("*1:注册\t2:登录\t3:退出*\n");
printf("******************************\n");
printf("请选择你所要执行的操作选项\n");
scanf("%d", &sum);
getchar();
switch (sum)
{
case 1:
do_regisyer(sockfd, &msg);
break;
case 2:
if (do_login(sockfd, &msg) == 0)
{
while (1)
{
int n = 0;
printf("******************************\n");
printf("*1:查询\t2:历史\t3:退出*\n");
printf("******************************\n");
printf("请选择你所要执行的操作选项\n");
scanf("%d", &n);
getchar();
switch (n)
{
case 1:
do_query(sockfd, &msg);
break;
case 2:
do_history(sockfd, &msg);
break;
case 3:
close(sockfd);
exit(0);
break;
default:
printf("输入错误,没有该操作\n");
break;
}
}
}
break;
case 3:
close(sockfd);
exit(0);
break;
default:
printf("输入错误,没有该操作\n");
break;
}
}
return 0;
}
int do_regisyer(int sockfd, MSG *msg)
{
msg->type = 'R';
MSG msg_t;
printf("请输入你要注册的用户名\n");
scanf("%s", msg->name);
getchar();
printf("请输入你要设置的密码\n");
scanf("%s", msg->data);
getchar();
if (send(sockfd, msg, sizeof(MSG), 0) < 0)
{
perror("send err");
return -1;
}
if (recv(sockfd, &msg_t, sizeof(MSG), 0) < 0)
{
perror("recv err");
return -1;
}
printf("%s\n", msg_t.data);
return 0;
}
int do_login(int sockfd, MSG *msg)
{
msg->type = 'L';
MSG msg_t;
printf("请输入用户名\n");
scanf("%s", msg->name);
getchar();
printf("请输入密码\n");
scanf("%s", msg->data);
getchar();
if (send(sockfd, msg, sizeof(MSG), 0) < 0)
{
perror("send err");
return -1;
}
if (recv(sockfd, &msg_t, sizeof(MSG), 0) < 0)
{
perror("recv err");
return -1;
}
printf("%s\n", msg_t.data);
if (strncmp(msg_t.data, "登录成功", 4) == 0)
{
return 0;
}
else
{
return -1;
}
}
int do_query(int sockfd, MSG *msg) //查询单词
{
printf("输入quit!!!返回上一步\n");
MSG msg_t;
while (1)
{
msg->type = 'Q';
printf("请输入你要查询的单词\n");
scanf("%s", msg->data);
if (strcmp(msg->data, "quit!!!") == 0)
{
break;
}
else
{
if (send(sockfd, msg, sizeof(MSG), 0) < 0)
{
perror("send err");
return -1;
}
if (recv(sockfd, &msg_t, sizeof(msg_t), 0) < 0)
{
perror("recv err");
return -1;
}
printf("%s\n", msg_t.data);
}
}
return 0;
}
int do_history(int sockfd, MSG *msg) //查询历史
{
msg->type = 'H';
MSG msg_t;
if (send(sockfd, msg, sizeof(MSG), 0) < 0)
{
perror("send err");
return -1;
}
while (1)
{
if (recv(sockfd, &msg_t, sizeof(msg_t), 0) < 0)
{
perror("recv err");
return -1;
}
if (strcmp(msg_t.data, "quit!!!") == 0)
{
break;
}
printf("%s\t%s\n", msg_t.name, msg_t.data);
}
return 0;
}
服务器端
#include "server.h"
sqlite3 *db;
int main(int argc, char const *argv[])
{
if (argc != 2)
{
printf("please input %s <port>\n", argv[0]);
return -1;
}
char *errmsg = NULL;
if (sqlite3_open("./dictionary.db", &db) != 0)
{
fprintf(stderr, "err:%s\n", sqlite3_errmsg(db));
return -1;
}
printf("sqlite_open ok\n");
//创建表
if (sqlite3_exec(db, "create table user(type char,name char primary key,data char);", NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "err:%s\n", errmsg);
//return -1;
}
//词典
if (sqlite3_exec(db, "create table dir(word char,paraphrase char);", NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "err:%s\n", errmsg);
//return -1;
}
//历史
if (sqlite3_exec(db, "create table history(name char,time char,word char);", NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "err:%s\n", errmsg);
//return -1;
}
//1.创建套接字 socket
int sockfd, acceptfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket err.");
return -1;
}
printf("socket ok %d\n", sockfd);
//填充ipv4的通信结构体
struct sockaddr_in serveraddr, caddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[1]));
serveraddr.sin_addr.s_addr = INADDR_ANY;
socklen_t len = sizeof(caddr);
//2.绑定套接字 bind (绑定自己的ip和端口,便于别人找到自己)
if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{
perror("bind err.");
return -1;
}
printf("bind ok.\n");
//3.listen 监听 将主动套接子变为被动等待
if (listen(sockfd, 5) < 0)
{
perror("listen err.");
return -1;
}
printf("listen ok.\n");
dictionary(); //字典
//功能:poll 服务器响应多个客户端请求且进行通信
//1.创建表 -创建一个结构体数组
struct pollfd fds[500] = {}; //大小自己确定,没有限定个数
//2.添加关心的文件描述符
fds[0].fd = sockfd;
fds[0].events = POLLIN;
int n = 1, ret;
char buf[256];
MSG msg;
int recvbyte;
//3.循环调用poll检测 ,poll阻塞等待有事件产生返回 -1
while (1)
{
ret = poll(fds, n, -1);
if (ret < 0)
{
perror("poll err.");
return -1;
}
//处理事件
for (int i = 0; i < n; i++)
{
if (fds[i].revents == POLLIN)
{
if (fds[i].fd == sockfd)
{
//4.阻塞等待客户端链接,链接成功返回一个通信文件描述符 accept
acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
if (acceptfd < 0)
{
perror("accept err.");
return -1;
}
printf("accept ok.\n");
//输出查看链接的客户端的ip和端口
printf("ip:%s ,port:%d\n",
inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
//需要将产生通信文件描述符添加到表中进行检测
fds[n].fd = acceptfd;
fds[n].events = POLLIN;
n++;
}
else
{
//接收消息
recvbyte = recv(fds[i].fd, &msg, sizeof(msg), 0);
if (recvbyte < 0)
{
perror("recv err.");
return -1;
}
else if (recvbyte == 0)
{
printf("%d client exit.\n", fds[i].fd);
close(fds[i].fd);
fds[i] = fds[n - 1];
n--;
i--;
break;
}
else
{
printf("%s %s\n", msg.name, msg.data);
//将其他客户端输入的数据作为通知的消息发送给除自身外的所有链接的客户端
switch (msg.type)
{
case 'R':
do_regisyer(fds[i].fd, &msg);
break;
case 'L':
do_login(fds[i].fd, &msg);
break;
case 'Q':
do_query(fds[i].fd, &msg);
break;
case 'H':
do_history(fds[i].fd, &msg);
break;
default:
break;
}
}
}
}
}
}
close(sockfd);
return 0;
}
int do_regisyer(int sockfd, MSG *msg) //插入用户
{
MSG msg_t;
char sql[512];
char *errmsg = NULL;
sprintf(sql, "insert into user values(%d,\"%s\",\"%s\");", msg->type, msg->name, msg->data);
printf("sql:%s\n", sql);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
strcpy(msg_t.data, "该用户已存在");
fprintf(stderr, "err:%s\n", errmsg);
return -1;
}
else
{
strcpy(msg->data, "注册成功");
if (send(sockfd, &msg_t, sizeof(msg_t), 0) < 0)
{
perror("send err");
}
printf("%s:插入成功\n", msg->name);
return 0;
}
}
int do_login(int sockfd, MSG *msg) //登录
{
char **sp = NULL;
int nrow, lie;
MSG msg_t;
char *errmsg = NULL;
char sql1[128];
printf("name:%s data:%s\n", msg->name, msg->data);
sprintf(sql1, "select * from user where name=\"%s\";", msg->name);
if (sqlite3_get_table(db, sql1, &sp, &nrow, &lie, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "err:%s\n", errmsg);
return -1;
}
if (nrow == 1)
{
if (strcmp(sp[5], msg->data) == 0)
{
printf("%s:登录成功\n", msg->name);
strcpy(msg_t.data, "登录成功");
send(sockfd, &msg_t, sizeof(msg_t), 0);
return 0;
}
else
{
strcpy(msg_t.data, "密码错误");
printf("%s:密码错误\n", msg->name);
send(sockfd, &msg_t, sizeof(msg_t), 0);
}
}
else
{
strcpy(msg_t.data, "该用户不存在");
printf("%s:该用户不存在\n", msg->name);
send(sockfd, &msg_t, sizeof(msg_t), 0);
}
}
int do_query(int sockfd, MSG *msg) //查询
{
char **sp = NULL;
int nrow, lie;
MSG msg_t;
char *errmsg = NULL;
char sql[128];
sprintf(sql, "select * from dir where word=\"%s\";", msg->data);
if (sqlite3_get_table(db, sql, &sp, &nrow, &lie, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "err:%s\n", errmsg);
return -1;
}
else
{
if (nrow == 1)
{
history_t(msg);
strcpy(msg_t.data, sp[3]);
send(sockfd, &msg_t, sizeof(msg_t), 0);
}
else
{
history_t(msg);
strcpy(msg_t.data, "没有查询到该单词");
send(sockfd, &msg_t, sizeof(msg_t), 0);
}
return 0;
}
}
int do_history(int sockfd, MSG *msg) //历史
{
char **sp = NULL;
int nrow, lie;
MSG msg_t;
char *errmsg = NULL;
char sql[128];
sprintf(sql, "select * from history where name=\"%s\";", msg->name);
if (sqlite3_get_table(db, sql, &sp, &nrow, &lie, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "err:%s\n", errmsg);
return -1;
}
else
{
if (nrow != 0)
{
int i, j, k = 1;
for (i = 0; i < nrow + 1; i++)
{
for (j = 1; j < lie; j++)
{
strcpy(msg_t.name, sp[k]);
strcpy(msg_t.data, sp[k + 1]);
}
send(sockfd, &msg_t, sizeof(msg_t), 0);
k = k + 3;
}
strcpy(msg_t.data, "quit!!!");
send(sockfd, &msg_t, sizeof(msg_t), 0);
return 0;
}
}
}
int history_t(MSG *msg)
{
char sql[512];
time_t timep;
struct tm *p;
char time1[28];
time(&timep);
p = gmtime(&timep);
sprintf(time1, "%d年%d月%d日 %d:%d:%d", 1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday, 8 + p->tm_hour, p->tm_min, p->tm_sec);
char *errmsg = NULL;
sprintf(sql, "insert into history values(\"%s\",\"%s\",\"%s\");", msg->name, time1, msg->data);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "err:%s\n", errmsg);
return -1;
}
return 0;
}
int dictionary(void) //字典
{
FILE *fd = fopen("./dict.txt", "r");
char buf[1024];
char sum[128];
char sun[1024];
char *p = NULL, *q = NULL;
MSG msg_t;
char sql[1024];
char *errmsg = NULL;
while (fgets(buf, sizeof(buf), fd) != NULL)
{
p = buf;
q = p + 1;
while (1)
{
if (*q == '\n')
{
strcpy(sum, buf);
strcpy(sun, "0");
sprintf(sql, "insert into dir values(\"%s\",\"%s\");", sum, sun);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "err:%s\n", errmsg);
return -1;
}
break;
}
else if (*p == ' ' && *q == ' ')
{
*p = '\0';
strcpy(sum, buf);
strcpy(sun, q);
sprintf(sql, "insert into dir values(\"%s\",\"%s\");", sum, sun);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "err:%s\n", errmsg);
return -1;
}
break;
}
p++;
q++;
}
}
fclose(fd);
}