目前所学的东西依然很有限,难以完成项目,目前只编写了部分代码加以参考。
test.c
#ifndef __TEST_H__
#define SER_PORT 8888 //服务器端口号
#define SER_IP "192.168.?.?" //服务器IP地址
#endif
//定义一个用于向线程体函数传参的结构体类型
struct MsgInfo
{
int newfd;
struct sockaddr_in cin;
};
//定义线程处理函数
void *deal_cli_msg(void *arg)
{
//解析传进来的参数
int newfd = ((struct MsgInfo*)arg)->newfd;
struct sockaddr_in cin = ((struct MsgInfo*)arg)->cin;
//5、跟客户端进行消息通信
char buf[128] = "";
while(1)
{
//将数组清空
bzero(buf, sizeof(buf));
//读取客户端发来的消息
//int res = read(newfd, buf, sizeof(buf));
int res = recv(newfd, buf, sizeof(buf), 0);
if(res == 0)
{
printf("客户端已经下线\n");
break;
}
printf("[%s:%d] : %s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);
//给客户端发消息
strcat(buf, "*_*");
//write(newfd, buf, sizeof(buf));
send(newfd, buf, sizeof(buf), 0);
printf("发送成功\n");
}
//关闭当前通信的套接字
close(newfd);
//退出线程
pthread_exit(NULL);
}
//注册操作
int do_register()
{
//追加读写操作,注册内容追加写入
char access[20];
char password[20];
//创建或打开数据库
sqlite3 *ppDb = NULL;
if(sqlite3_open("./MYSQL.db", &ppDb) != SQLITE_OK)
{
printf("sqlite3_open error,errcode = %d, errmsg = %s\n", sqlite3_errcode(ppDb), sqlite3_errmsg(ppDb));
return -1;
}
printf("sqlite3_open success\n");
//通过fgets获取终端输入字符串,存入access和password中
printf("输入账号:");
fgets(access,sizeof(access),stdin);
printf("输入密码:");
fgets(password,sizeof(password),stdin);
//将字符数组变为字符串
access[strlen(access)-1]='\0';
password[strlen(password)-1]='\0';
char sql[128] = "";
sprintf(sql, "insert into Users values(%s,%s);",access,password);
printf("sql = %s\n", sql);
//向数据库写入数据
char* errmsg = NULL;
if(sqlite3_exec(ppDb,sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
printf("sqlite3_exec error: %s\n", errmsg);
//释放errmsg的空间
sqlite3_free(errmsg);
errmsg = NULL;
return -1;
}
puts("注册成功");
//关闭数据库
sqlite3_close(ppDb);
return 0;
}
int callback(void *arg, int cols, char**value_text, char **value_name)
{
if((*(int *)arg) == 0)
{
//输出表头
for(int i=0; i<cols; i++)
{
printf("%-10s", value_name[i]);
}
printf("\n");
*((int *)arg) = 1; //更改标识位
}
//输出当前记录的信息
for(int i=0; i<cols; i++)
{
printf("%-10s", value_text[i]);
}
printf("\n");
return 0;
}
//登录操作
int do_login()
{
char access[20];
char password[20];
//存入终端输入的账号密码
printf("输入账号:");
fgets(access,sizeof(access),stdin);
printf("输入密码:");
fgets(password,sizeof(password),stdin);
access[strlen(access)-1]='\0';
password[strlen(password)-1]='\0';
//创建或打开数据库
sqlite3 *ppDb = NULL;
if(sqlite3_open("./MYSQL.db", &ppDb) != SQLITE_OK)
{
printf("sqlite3_open error,errcode = %d, errmsg = %s\n", sqlite3_errcode(ppDb), sqlite3_errmsg(ppDb));
return -1;
}
printf("sqlite3_open success\n");
// 查看数剧库是否有这个名字存在
char sql_access[128]="select count(*) from Users where username=access";
char sql_password[128]="select count(*) from Users where userpass=password";
int count1 = 0,count2 = 0;
char* errmsg1 = NULL;
char* errmsg2 = NULL;
sqlite3_exec(ppDb, sql_access, callback,&count1, &errmsg1);
sqlite3_exec(ppDb, sql_password, callback,&count1, &errmsg2);
if(count1 != 0&& count2!=0)
{
puts("登录成功");
}
//关闭数据库
sqlite3_close(ppDb);
return 0;
}
main.c
#define SER_PORT 8888 //服务器端口号
#define SER_IP "192.168.?.?" //服务器IP地址
int main(int argc, const char *argv[])
{
//创建或打开数据库
sqlite3 *ppDb = NULL;
if(sqlite3_open("./MYSQL.db", &ppDb) != SQLITE_OK)
{
printf("sqlite3_open error,errcode = %d, errmsg = %s\n", sqlite3_errcode(ppDb), sqlite3_errmsg(ppDb));
return -1;
}
printf("sqlite3_open success\n");
//创建表格,表名Users
char sql[128]="create table Users(username char,userpass char)";
char* errmsg = NULL;
sqlite3_exec(ppDb, sql, NULL, NULL, &errmsg);
//关闭数据库
sqlite3_close(ppDb);
//登陆
char num;
//循环打印选项
while(1)
{
printf("-----1.注册-----\n");
printf("-----2.登录-----\n");
printf("-----0.退出-----\n");
printf("请输入选项:");
scanf("%c",&num);
//通过循环吃掉从第二个字符开始的所有垃圾字符
while(getchar()!='\n');
switch(num)
{
case '1':
do_register();
break;
case '2':
do_login();
break;
case '0':
exit(EXIT_SUCCESS);
break;
default:
puts("error");
}
printf("按回车清屏");
//吃掉回车前所有垃圾字符
while(getchar()!='\n');
system("clear");
}
//1、创建用于连接的套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
printf("sfd = %d\n", sfd);
//对套接字设置接收超时时间
struct timeval tv;
tv.tv_sec = 10; //10秒
tv.tv_usec = 0;
//将端口号快速重用函数
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &tv, sizeof(reuse)) == -1)
{
perror("setsockopt error");
return -1;
}
printf("端口号快速重用成功\n");
//2、给当前套接字绑定IP地址和端口号
//2.1填充要绑定的地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET; //通信域
sin.sin_port = htons(SER_PORT); //端口号
sin.sin_addr.s_addr = inet_addr(SER_IP); //ip地址
//2.2 绑定
if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
{
perror("bind error");
return -1;
}
printf("bind success %s %s %d\n", __FILE__, __func__, __LINE__);
//3、将套接字设置成监听状态
if(listen(sfd, 128) == -1)
{
perror("listen error");
return -1;
}
printf("listen success %s %s %d\n", __FILE__, __func__, __LINE__);
//4、阻塞等待客户端的链接请求
//4.1定义容器接收客户端的地址信息
struct sockaddr_in cin; //用于接收地址信息
socklen_t socklen = sizeof(cin); //用于接收地址信息的大小
int newfd = -1;
pthread_t tid; //线程id号
while(1)
{
//将程序执行到accept处时,系统会给accept函数预选一个文件描述符,按最小未分配原则
//4.2 接收客户端的链接
newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);
if(newfd == -1)
{
perror("accept error");
return -1;
}
printf("[%s:%d:%d]发来链接请求 %s %s %d\n", \
inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, __FILE__, __func__, __LINE__);
//定义用于传参的结构体变量
struct MsgInfo info;
info.newfd = newfd;
info.cin = cin;
//创建分支线程,用于跟新连接的客户端进行交互
if(pthread_create(&tid, NULL, deal_cli_msg, &info) != 0)
{
printf("分支线程创建失败\n");
return -1;
}
//回收线程资源
pthread_detach(tid); //将线程分离,后期退出后,由系统回收资源
}
//6、关闭套接字
close(sfd);
return 0;
}
以上是本次项目是服务器端的代码,由于遇到了一堆问题,无法得出运行结果,gcc编译阶段都过不了,主要过程是创建数据库,创建表格,表格存储登陆数据,提供登陆/注册选项等,之后创建套接字,端口重用,绑定IP等,使用多线程接收客户端的连接。但是遇到了很多问题。
遇到的问题:
1.采用数据库实现信息存储时,不知道如何查看表中是否存在某种信息,例如希望实现查看用于记录注册的信息,登陆时,想判断表中是否存在该用户名和密码时,不知道怎么判断,网上搜索结果使用的select count(*)......的代码,但是使用时出现了报错信息,并且返回值不知道怎么获取,如果使用
sqlite3_exec(ppDb, sql_password, callback,&count1, &errmsg1);
显然是错误的,目前不知道如何改进。
2.题目中所说的使用Sock编程,所有客户端发送数据采用协议的形式,协议包含两部分:包头+数据,给出了包头等信息,但是我完全没有思路,不知道该怎么搞,客户端发送数据的话直接发送信息就能达到客户端与服务器的通信,进行封装,然后区分数据类型进行不同的操作,这一点不知道如何实现。
3.题目中需要实现多个终端,手持终端发送信息给服务器端,但是不知道如何实现服务器端将接受的信息区分开,选出所点的菜单,然后菜单又是如何不发送给吧台/手持终端的客户端,只发送给多个厨房的客户端?对于这些几乎完全没有思路。
4.正常情况下,服务器的IP地址应该是192.168.?.?,但是连上家里这边的网络之后,使用ifconfig指令得到的结果却是这样的
似乎无法得到正确的IP地址,不知道是什么原因。
5、厨房客户端,设置每桌订单的状态,分三个状态,初始状态为等待,其次正在准备中,第三个已经完成,实现这个的话,思路大致是,向服务器端发送桌号,然后每隔一段时间向服务器发送等待的状态,那么这个状态怎么到准备中的状态?又怎么到已完成的状态?
总结:这次项目对我目前的水平来说太难了,不管是哪一个部分都难以完全实现,日后结果学习希望能有更好的思路。