50.在线词典项目1--需求分析及软件框架搭建

1引言

1.1编写目的

经过一段时间的学习,终于要开始一个实战项目了,这个项目目的就是为了回顾之前的知识,主要用到了网络编程客户端及并发服务器的实现,父子进程,信号,sql数据库等,我们先详细理解需求然后对框架及相关进行梳理,思路清晰后,再开始编程。

1.2 开发环境

开发环境

版本

linux gcc工具链

 

LIB

sqlite3

 

1.3 项目需求

基于网络编程和数据库实现在线词典功能,客户端可以注册,登入,查询历史信息等操作,服务器基于多进程实现多客户端的并发访问,并使用sqlite数据库实现对用户信息的管理

需求分析,进行需求拆分:

  1. 使用tcp实现客户端和服务器
  2. 服务器使用并发服务器(多进程,多线程或者select多路复用)
  3. 建立一个数据库,数据库中包含两个表,一个表用于存储用户名及密码等信息,一个用于存 储历史信息(包含保存时间)
  4. 类似我们平时看到的功能,需要有二级菜单,一级菜单中包含用户的注册,用户登录,用户 退出。登录成功后,进入二级菜单,二级菜单包括单词查询,历史查询,退出。

注意:注意进程的回收防止僵尸进程(通过信号回收),用户注册不能出现重复的用户名等,确 保功能完整,性能优化可以之后慢慢进行。

2软件总体设计

1.结构框架(参考)

 

2.客户端概要框架:

3软件详细设计

3.1服务器端程序流程

 

3.2客户端程序流程

 

3.3参数数据结构

 

4.代码实例(框架)

4.1 net.h (包含需要用到的头文件及结构体等)

#include <sys/types.h>  /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sqlite3.h>
#include <signal.h>

#define SERV_PORT 5001
#define BACK_LOG  5
#define BUFSIZE 100
#define   N  32

#define  R  1   //register
#define  L  2   //login
#define  Q  3   //query
#define  H  4   //history

// 定义通信双方的信息结构体
typedef struct {
    int type;
    int root_flag;
    char name[N];
    char data[256];
}MSG;

4.2 client.c(客户端实现)

#include "net.h"

int  do_register(int sockfd, MSG *msg)
{
   return 0; 
}

int  do_login(int sockfd, MSG *msg)
{
   return 1; 
}
int do_query(int sockfd, MSG *msg)
{
    return 0;
}
int do_history(int sockfd, MSG *msg)
{
    return 0;
}

int main(int argc,char *argv[])
{
    struct sockaddr_in sin;
    int temp;
    int fd;
    MSG  msg;
 
 /**************************TCP client操作*****************************/
    if(argc < 2){
        printf("please input %s + ip 192.168.x.xxx\n",argv[0]);
        exit(1);  
    }
    //socket
    if((fd = socket(AF_INET,SOCK_STREAM, 0) )< 0){
        perror ("socket");
        exit (1);
    }
    //connect
    bzero(&sin,sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons (SERV_PORT);   
    sin.sin_addr.s_addr = inet_addr (argv[1]);

    if( connect (fd, (struct sockaddr *)&sin,sizeof(sin)) < 0){
        perror ("connect ");
        exit (1);
    }
    printf("connect success\n");
/********************************************************************/
    while(1){
        //一级菜单,提示用户输入
        printf("*****************************************************************\n");
        printf("* 1.register          2.login              3.quit               *\n");
        printf("*****************************************************************\n");
        printf("Please choose:");
        
        //等待用户选择
        scanf("%d", &temp);
        getchar();
        
        switch(temp){
            case 1:
                do_register(fd,&msg);  
            break;
            case 2:
                if(do_login(fd,&msg)){
                    
                    goto next;
                }
            break;
            case 3:
                close(fd);
                exit(0);
            break;
            default:
                printf("Please enter the correct command\n");
                
        }
    }
next:
    //进入二级菜单
    while(1){
        printf("*****************************************************\n");
        printf("* 1.query_word       2.history      3.quit          *\n");
        printf("*****************************************************\n");
        printf("Please choose:");
        
        scanf("%d", &temp);
        getchar();
        switch(temp){
            case 1:
                do_query(fd, &msg);  
            break;
            case 2:
                do_history(fd, &msg);
            break;
            case 3:
                close(fd);
                exit(0);
            break;
            default:
                printf("Please enter the correct command\n");
                
        }          
        
    }

    return 0;
}

4.3 server.c

#include "net.h"

#define DATABASE "my.db"

void do_register(int fd, MSG *msg, sqlite3 *db)
{
    ;
}
int do_login(int fd, MSG *msg, sqlite3 *db)
{
    return 0;
}
int do_query(int fd, MSG *msg, sqlite3 *db)
{
    return 0;
}
int do_history(int fd, MSG *msg, sqlite3 *db)
{
    return 0;
}

int do_client(int fd, sqlite3 *db)
{
    MSG msg;
    while(recv(fd, &msg, sizeof(msg), 0) > 0)
    {
       switch(msg.type)
       {
         case R:
             do_register(fd, &msg, db);
             break;
         case L:
             do_login(fd, &msg, db);
             break;
         case Q:
             do_query(fd, &msg, db);
             break;
         case H:
             do_history(fd, &msg, db);
             break;
         default:
             printf("Please enter the correct command\n");
       }

    }
    close(fd);
    exit(0);

    return 0;
}

int main(int argc,char *argv[])
{
    struct sockaddr_in sin;
    int fd,connfd;
    MSG  msg;
    sqlite3 *db;
    char *errmsg;
    pid_t pid;
    char sql[128] = {0};
/***************************数据库操作***********************************/    
    //打开数据库
    if(sqlite3_open(DATABASE, &db) != SQLITE_OK){
         printf("%s",sqlite3_errmsg(db)); //打印出错信息
         return -1;
    }
    //创建表 user
    if(sqlite3_exec(db, "create table if not exists user(name text primary key, data text);",NULL,NULL,&errmsg) != SQLITE_OK){
            printf("%s\n",errmsg); //打印出错信息
            return -1;
    }
    //在表中加入超级用户
    sprintf(sql,"insert into user(name,data) values('%s','%s');","root","1");
    if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK){
        printf("%s\n", errmsg);
    }
    else
    {
        printf("Insert success.\n");
    }
 /**************************TCP server操作*****************************/
    //socket
    if((fd = socket(AF_INET,SOCK_STREAM, 0) )< 0){
        perror ("socket");
        exit (1);
    }


    //bind
    bzero(&sin,sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons (SERV_PORT);   
    sin.sin_addr.s_addr = htonl(INADDR_ANY);

    if( bind(fd, (struct sockaddr *)&sin,sizeof(sin)) < 0){
            perror ("bind");
            exit (1);
    }
    //更改属性,使ip能够快速重用
    int b_reuse =1;
    setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
    //listen
    if( listen(fd,BACK_LOG) < 0){
        perror ("listen");
        exit (1);
    }
    printf("listen success\n");
    
    //处理僵尸进程
    signal(SIGCHLD, SIG_IGN);
/********************************************************************/
    struct sockaddr_in cin;
    socklen_t addrlen = sizeof(cin);
    while(1){
/**********************************************************************************/
       //accept
       if ((connfd = accept(fd, (struct sockaddr *) &cin, &addrlen)) < 0){
           perror ("accept");
           exit (1);
       }
       char ipv4_addr[16];
       if (!inet_ntop(AF_INET, (void *)&cin.sin_addr, ipv4_addr, sizeof(cin))) {
           perror("inet_ntop");
           exit(1);
       }

       printf("Clinet(%s:%d) is connected!\n", ipv4_addr, htons(cin.sin_port));
/**********************************************************************************/ 
       if((pid = fork()) < 0)
       {
           perror("fork");
           return -1;
       }
       else if(pid == 0)  // 子进程
       {
           close(connfd);
           do_client(connfd, db);//处理客户端具体的消息
       }
       else  // 父进程,用来接受客户端的请求的
       {
           close(connfd);
       }
       
    }

    return 0;
}

 

4.4 Makefile

all:client server
.PHONY : all
CC = gcc
 
client : client.c
	$(CC) -o client client.c 
server : server.c
	$(CC) -o server server.c -lsqlite3

 
.PHONY : clean
clean: 
	rm client server my.db

因为是搭建的框架,具体的功能后期填充,目前通过编译可以看到数据库的生成及TCP的正常连接。

 

 

 

  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在线词典项目描述: 版本号:v1.1 升级描述:1. 同时支持管理员(用户名:root,密码:1)和普通用户 2. 管理员可以查询所有用户的使用记录 服务器: 1. 支持并发服务器,每有一个客户端connect成功后,提示某某客户端已连接并打印客户端的ip和端口号。 2. 服务器程序可在任意IP地址上运行,并且允许IP地址快速重用 3. 接收到客户端的信息后,可以执行相应的操作:注册,登陆,退出 注册:接收到注册新用户指令后,可以创建sqlite3数据库,将用户名和密码存储到数据库的user表中(用户名name为primary key)。 登陆:接收到登陆命令,可以查询客户端输入的用户名和密码数据库中有没有,有的话跳到下一个菜单(查询单词,历史纪录,退出),没有的话打印错误信息。 查询单词:用户输入单词,服务器从dict.txt文件中遍历有无该单词,有的话打印释义,没有的话打印错误信息,并将用(户名,时间,单词)存储到数据库的history_record表中。(‘#’返回上一级菜单) 历史纪录:用户选择历史记录查询,服务器从数据库的history_record表查询相同name的记录,每查询到一条,调用一次callback将信息发送到客户端,查询完毕后通知客户端。 退出:客户端退出,服务器打印"client exit!" 退出:客户端退出,服务器打印"client exit!" 客户端: 1. 客户端输入./client 192.168.23.128(服务器IP地址) 10000(端口号),参数格式不对或少报错,端口号不能小于5000,小于5000报错 2. 客户端支持注册,登陆,退出 注册:向服务器发送用户名和密码,接收服务器返回来的信息,注册成功/当前用户已存在 登陆:用户输入用户名和密码,客服端将用户名和密码发送给服务器,接收服务器返回的信息,如果OK,打印Login OK! 进入下一菜单(查询单词,历史纪录,退出),否则打印错误信息 查询单词:用户输入单词,客户端将单词发送给服务器,服务器将释义返回给客户端,客户端将释义打印出来 历史纪录:用户选择历史记录查询,客户端将信息发送给服务器,服务器循环把该用户的历史查询记录发送给客户端,客户端循环将其打印出来。 退出:客户端关闭套接字后结束进程 退出:客户端关闭套接字后结束进程
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值