首先,这个项目仅仅只是一个模板,需要大家进行后续开发,我把此模板进行开源
我这个项目运用到的技术有TCP通信协议、IO多路复用、sqlite3数据库、循环服务器等,在进行编写之前,需要大家进行了解(当然,你们还可以根据自己的需求,把项目改写成多线程或者多进程进行编写)
- 在开始项目之前,需要大家对TCP通信进行大概的了解,嗯,TCP通信的话就像是在打电话,流程图如下:
- 也就是需要完成客户端和服务器端的连接操作,成功之后,方可进行项目内容的操作。
client.c文件
还可以将项目改写成嵌套makefile的形式,这里我是以子函数进行编写,没有拆分
client.c文件 这里的头文件我进行省略,因为太多,可以man一下进行添加
int tcp_connect(const char * ip,int port)
{
//1.创建套接子文件 socket
int tcp_socket = socket(AF_INET,SOCK_STREAM,0); //TCP
if(tcp_socket < 0)
{
perror("创建套接子失败!\n");
}
printf("创建套接子成功!\n");
//2.创建对方的IP和端口号
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port); //主机到网络字节序
server.sin_addr.s_addr = inet_addr(ip);//主机到网络字节序
//3.请求连接套接子
if(connect(tcp_socket,(struct sockaddr *)&server,sizeof(server))<0)
{
perror("连接套接字失败!\n");
return -1;
}
printf("连接套接字成功!\n");
return tcp_socket;
}
void tcp_com(int tcp_socket)
{
//tcp通信 这里的通信可以大家自由发挥
}
int main(int argc, const char *argv[])
{
if(argc<3)
{
printf("input appname ip port\n");
return -1;
}
//1.连接Tcp服务器
int tcp_socket=tcp_connect(argv[1],atoi(argv[2]));
//2.tcp通信 这里的通信可以大家自由发挥
tcp_com(tcp_socket);
//3.关闭套接子
close(tcp_socket);
return 0;
}
server.c文件
这里是服务器端
server.c文件
int tcp_server(const char * ip,int port)
{
//1.创建tcp_socket对象
int tcp_socket=0;
tcp_socket=socket(AF_INET,SOCK_STREAM,0);
if(tcp_socket<0)
{
perror("socket error");
return -1;
}
printf("socket ok\n");
//2.绑定自己的IP地址和端口号
struct sockaddr_in myaddr;
myaddr.sin_family=AF_INET;//协议
myaddr.sin_addr.s_addr=inet_addr(ip);//INADDR_ANY 自动获取一个什么我们就用什么
myaddr.sin_port=htons(port);
if(bind(tcp_socket,(struct sockaddr *)&myaddr,sizeof(myaddr))<0)
{
perror("bind error");
return -1;
}
printf("bind ok\n");
//3.监听是否有人连接
if(listen(tcp_socket,5)<0)
{
perror("listen error");
return -1;
}
printf("listen ok\n");
return tcp_socket;
}
int tcp_com(int newfd)
{
//这里也是大家自由发挥的地方,切记,连接成功,需要跟client.c进行一一对应。
}
//运用并发服务器的IO多路复用
int main(int argc, const char *argv[])
{
if(argc<3)
{
printf("input appname ip port\n");
return -1;
}
//1.获得监听好的socket对象
int tcp_socket=tcp_server(argv[1],atoi(argv[2]));
//2.接受连接生成新的socket对象
int newfd=0;
//newfd=accept(tcp_socket,NULL,NULL); //若不关心对方的IP地址和port
//参数2,3,设置为NULL //
//关心一下端的IP地址和端口号
struct sockaddr_in client;
//1.定义两个文件描述符
fd_set rdfs;
fd_set temp;
//清空两个文件的集合
FD_ZERO(&rdfs);
FD_ZERO(&temp);
//将监听的socket对象加入到文件描述符集合里
FD_SET(tcp_socket,&rdfs);
while(1)
{
temp = rdfs;//监控的是temp
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
int reval = select(FD_SETSIZE,&temp,NULL,NULL,&tv);
if(reval <0)
{
perror("select error");
return -1;
}
else if(reval ==0)
{
printf("select timeout\n"); //超时
}
else if(reval > 0)
{
int i=0;
for(i=0;i<FD_SETSIZE;i++)
{
//判断那个文件描述符有数据
if(FD_ISSET(i,&temp))
{
if(i == tcp_socket)
{//若是监听socket,表示客户端来连接
memset(&client,'\0',sizeof(client));
int len=sizeof(client);
newfd=accept(tcp_socket,(struct sockaddr *)&client,&len);
if(newfd<0)
{
perror("accept error");
return -1;
}
printf("client ip=%s port=%d accept ok\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
FD_SET(newfd,&rdfs);//将新生成的socket加入集合中
}
else
{
//通过套接字,表示客户端有数据可读
//3.TCP通信
int n = tcp_com(i);
if(n<0)
{
printf("%d断了\n",i);
FD_CLR(i,&rdfs);//将文件描述符从集合中删除
//4.关闭socket对象
close(i);
}
}
}
}
}
}
//4.关闭socket对象
close(tcp_socket);
return 0;
}
sqlite3文件
这里是sqlite3数据库的操作 需要在server.c中进行操作
typedef struct Student
{
int id;
char name[SIZE];
float score;
}stu;
//打开数据库
//参数:数据库名
sqlite3 *sql_open(char *dbname)
{
sqlite3 *db = NULL;
int ret = sqlite3_open(dbname,&db);
if(ret != SQLITE_OK)
{
perror("sqlite open error!\n");
return NULL;
}
printf("sqlite open ok\n");
return db;
}
//创建表
//参数1:数据库的连接对象 sqlite3 *
//参数2:表的名称 char *tbname
int create_table(sqlite3 *db,char *tbname)
{
char sql[N] = {'\0'};
sprintf(sql,"create table if not exists %s (id int primary key not null,name text,score real)",tbname);
printf("%s\n",sql);
char *errmsg = NULL;
int ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);
if(ret != 0)
{
fprintf(stderr,"%s\n",errmsg);
return -1;
}
else
{
printf("create table ok!\n");
}
return 0;
}
//增加内容
//参数1:数据库的连接对象
int data_insert(sqlite3 *dbname,char *tbname)
{
stu s1;
printf("请输入ID 姓名和成绩:");
scanf("%d%s%f",&s1.id,s1.name,&s1.score);
//增加
char *errmsg = NULL;
char sql[N] = {'\0'};
sprintf(sql,"insert into %s values (%d,'%s',%.1f)",tbname,s1.id,s1.name,s1.score);
printf("%s\n",sql);
int ret = sqlite3_exec(dbname,sql,NULL,NULL,&errmsg);
if(ret != SQLITE_OK)
{
fprintf(stderr,"%s\n",errmsg);
return -1;
}
else
{
printf("insert ok!\n");
}
return 0;
}
//删除
int data_delete(sqlite3 *dbname,char *tbname)
{
stu s1;
printf("请输入要删除的ID号:");
scanf("%d",&s1.id);
char sql[N] = {'\0'};
sprintf(sql,"delete from %s where id = %d",tbname,s1.id);
printf("%s\n",sql);
char *errmsg = NULL;
if(sqlite3_exec(dbname,sql,NULL,NULL,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"%s\n",errmsg);
return -1;
}
else
{
printf("delete ok!\n");
}
return 0;
}
//更新
int data_update(sqlite3 *dbname,char *tbname)
{
stu s1;
printf("请输入要修改的ID:");
scanf("%d",&s1.id);
printf("要修改的成绩为:");
scanf("%f",&s1.score);
char sql[N] = {'\0'};
sprintf(sql,"update %s set score = '%.1f' where id = %d",tbname,s1.score,s1.id);
printf("%s\n",sql);
char *errmsg = NULL;
if(sqlite3_exec(dbname,sql,NULL,NULL,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"%s\n",errmsg);
return -1;
}
else
{
printf("update ok!\n");
}
return 0;
}
//查询的回调函数
//参数1:void * data给回调函数传递参数
//参数2:int columnNum 记录中包含的段数目(几列就是几)
//参数3:char ** //char *columnValue[] 列值
//参数4:char ** //char *columnName[] 名字
int callback(void *data,int columnNum,char *columnValue[],char *columnName[])
{
int i = 0;
stu s1;
memset(&s1,'\0',sizeof(s1));
s1.id =atoi(columnValue[0]);
strcpy(s1.name,columnValue[1]);
s1.score =atof(columnValue[2]);
printf("%5d%10s%7.1f\n",s1.id,s1.name,s1.score);
printf("\n");
return 0;
}
//查询内容
//参数1:数据库的连接对象
//参数2:表名
int data_select(sqlite3 *dbname,char *tbname)
{
char sql[N]= {'\0'};
sprintf(sql,"select * from %s",tbname);
char *errmsg = NULL;
//参数1:连接对象sqlite3 *db
//参数2:sql语句
//参数3:回调函数 函数的指针 //select
//参数4:给回调函数传参
int ret = sqlite3_exec(dbname,sql,callback,NULL,&errmsg);
if(ret != SQLITE_OK)
{
fprintf(stderr,"%s\n",errmsg);
return -1;
}
else
{
printf("select ok!\n");
}
return 0;
}
- 这基本的框架就是这样,然后进行登陆注册的增删改查以及项目内容的增删改查,这里是大家自由发挥的时间,可以根据自己的需求进行项目完善。
最后,咋们可以根据这个框架进行升级