电子词典的服务器端程序

程序功能简述:程序实现了多用户的注册,登录,查寻,历史查寻,退出功能,在对数据库读写和修改的时候设计只有登录的账号可以读写和修改与该登录账号相关的信息,每个账号的信息相对独立,这避免了多用户操作是对数据库访问的冲突。

用了最少整整四天才完成这个工作,但是还是有点bug,记录下自己的代码。

整个电子词典是分块做的:包含的Dic_Server.c,Dic_Client.c,db.c,query.c,xprtcl.c,dict.h,xprtcl.h,dict.txt(单词文件)

下面是Dic_Server.c代码:主要用创建子进程实现服务器端的并发操作

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <errno.h>
#include <sqlite3.h>
#include <time.h>

#include "dict.h"
#include "xprtcl.h"

#define MY_PORT 8888
#define DATABASE "dict.db"
#define T_USERS "users" /* table name for login users */
#define T_HISTORY "history" /* table name for query history */
#define MAXLEN_SQL 256

extern int db_init();
extern void db_close();
extern int dictd_init();
extern int tbl_query_history(int, const char *);
extern int tbl_insert_users(const char *, const char *);
extern int login_users(const char *, const char *);
extern int query_dictionary(const char * ,const char *,char *);


void do_register(int connfd, xprotocol_t *packet)
{
uint16_t rc = RC_SUCCESS;

if (tbl_insert_users(packet->hdr.usrname, packet->data.req_register.passwd) < 0)
{
rc = RC_EREGISTER;
}
/* reuse the input packet buffer */
packet->hdr.cmd_type = RPL_Register;
packet->hdr.ret_code = rc;

/* send reply back to the client */
if (xp_send(connfd, packet) < 0) {
dprintf("xp_send error");
}
return;
}

void do_login(int connfd, xprotocol_t *packet)
{
uint16_t rc = RC_SUCCESS;
int login;
login = login_users(packet->hdr.usrname, packet->data.req_login.passwd);

if ( login < 0)
{
rc = RC_EREGISTER;
}
if(login == 1)
{
rc = RC_LOGINED;
}

/* reuse the input packet buffer */
packet->hdr.cmd_type = RPL_Login;
packet->hdr.ret_code = rc;

/* send reply back to the client */
if (xp_send(connfd, packet) < 0) {
dprintf("xp_send error");
}
return;
}

void do_query(int connfd, xprotocol_t *packet)

{
uint16_t rc = RC_SUCCESS;
char sqlstr[MAXLEN_SQL];
char *errmsg;
sqlite3 *g_db = NULL;
int result;

time_t timep;
struct tm *p;
char buf[100];
if(query_dictionary(packet->word_data.req_query.word,
"/mnt/hgfs/source test/Dictionary/dict.txt",packet->data.rpl_query.text) < 0)
{
rc = RC_EREGISTER;
}

time(&timep);
p = gmtime(&timep);
bzero(buf,100);
sprintf(buf,"%4d-%02d-%02d %02d:%02d:%02d",1900+p->tm_year, 1+p->tm_mon,p->tm_mday,
p->tm_hour,p->tm_min,p->tm_sec);
buf[sizeof(buf)] = '';

result = sqlite3_open(DATABASE, &g_db);
if (result != SQLITE_OK)
{
if (NULL != g_db)
{
fprintf(stderr, "error: open database: %s ",
sqlite3_errmsg(g_db));
}
else
{
fprintf(stderr, "error: failed to allocate memory! ");
}
exit(-1);
}

if (snprintf(sqlstr, MAXLEN_SQL, "insert into history values('%s', '%s', '%s')",
packet->hdr.usrname, packet->word_data.req_query.word,buf)< 0)
{
perror("snprintf error");
exit(-1);
}

if (SQLITE_OK != sqlite3_exec(g_db, sqlstr, NULL, NULL, &errmsg))
{
fprintf(stderr, "error: insert users: %s ", errmsg);
sqlite3_free(errmsg);
}


/* reuse the input packet buffer */
packet->hdr.cmd_type = RPL_Query;
packet->hdr.ret_code = rc;

/* send reply back to the client */
if (xp_send(connfd, packet) < 0) {
dprintf("xp_send error");
}
return;
}

int do_quit(int connfd, xprotocol_t *packet)
{
char sqlstr[MAXLEN_SQL];
char *errmsg;
sqlite3 *g_db = NULL;
int result;

result = sqlite3_open(DATABASE, &g_db);
if (result != SQLITE_OK)
{
if (NULL != g_db)
{
fprintf(stderr, "error: open database: %s ",
sqlite3_errmsg(g_db));
}
else
{
fprintf(stderr, "error: failed to allocate memory! ");
}
return -1;
}
printf("%s have withdrawn from the account ",packet->hdr.usrname);
if (snprintf(sqlstr, MAXLEN_SQL,
"update users set state='0' where usrname='%s'",
packet->hdr.usrname)< 0)
{
perror("snprintf error");
return -1;
}

if (SQLITE_OK != sqlite3_exec(g_db, sqlstr, NULL, NULL, &errmsg))
{
fprintf(stderr, "error: insert users: %s ", errmsg);
sqlite3_free(errmsg);
return -1;
}

return 0;
}


int main(int argc ,char **argv)
{
int listen_fd,accept_fd;
struct sockaddr_in client_addr;
int n;

xprotocol_t packet;
ssize_t nr;

if (db_init() < 0)
{
fprintf(stderr, "database init failed ");
exit(EXIT_FAILURE);
}

if((listen_fd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("Socket Error:%s a",strerror(errno));
exit(1);
}

bzero(&client_addr,sizeof(struct sockaddr_in));
client_addr.sin_family=AF_INET;
client_addr.sin_port=htons(MY_PORT);
client_addr.sin_addr.s_addr=htonl(INADDR_ANY);
n=1;
/* 如果服务器终止后,服务器可以第二次快速启动而不用等待一段时间 */
setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(int));
if(bind(listen_fd,(struct sockaddr *)&client_addr,sizeof(client_addr))<0)
{
printf("Bind Error:%s a",strerror(errno));
exit(1);
}
listen(listen_fd,5);
while(1)
{
accept_fd=accept(listen_fd,NULL,NULL);
if((accept_fd<0)&&(errno==EINTR))
continue;
else if(accept_fd<0)
{
printf("Accept Error:%s a",strerror(errno));
continue;
}

dprintf("accept() connection fd = %d from %s, port %d ",
accept_fd, inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));

packet.hdr.cmd_type = 0; /* quick clear */

if((n=fork())==0) /* 子进程处理客户端的连接 */
{

printf("wait client input: ");
close(listen_fd);

while ((nr = xp_recv(accept_fd, &packet)) != 0)
{
switch ( packet.hdr.cmd_type )
{
case REQ_Register:
do_register(accept_fd, &packet);
break;
case REQ_Login:
do_login(accept_fd, &packet);
break;
case REQ_Query:
do_query(accept_fd, &packet);
break;
case REQ_QUIT:
do_quit(accept_fd, &packet);
break;
case REQ_ERR_QUIT:
printf("User no login and quit ");
break;
default:
dprintf("unknown cmd type or recv failed ");
/* just continue recv */
break;
}

}
packet.hdr.cmd_type = 0; /* quick clear */
close(accept_fd);
exit(0);
}
else if(n<0)
{
printf("Fork Error:%s a",strerror(errno));
}

close(accept_fd);
}

return 0;
}

原文地址:https://www.cnblogs.com/cnlg/p/4366900.html