您的位置:首页 > 理论基础 > 计算机网络

Linux网络应用综合项目(6.15)

2015-06-16 20:24 711 查看
在线英语词典功能:

服务端源码:

seqlite.h

#ifndef __SQLITE_H__
#define __SQLITE_H__

int sqlite_find_user(sqlite3 *db, const char *name, char *passwd);
int sqlite_insert_user(sqlite3 *db, const char *name, const char *passwd);
int sqlite_find_word(sqlite3 *db, const char * word, char *explain);

#endif // __SQLITE_H__


server.h

#ifndef	__SERVER_H__
#define __SERVER_H__
#include <sqlite3.h>

int send_fix_len(int sockfd, const char *buf, int len);
int recv_fix_len(int sockfd, char *buf, int len);

int recv_proc(int sockfd, sqlite3 *db);
int do_register(sqlite3 *db, char *packet, int content_len);

#endif // __SERVER_H__


port.h

#ifndef	__PROT_H__
#define __PROT_H__

#define	LEN_PACKET_LEN	4
#define LEN_PACKET_FUNC	2
#define LEN_PACKET_HEAD	(LEN_PACKET_LEN + LEN_PACKET_FUNC)
#define LEN_PACKET_USER	32
#define LEN_PACKET_PASSWORD 32
#define LEN_PACKET_RET 2

#define RET_SUCCESS			0
#define RET_ERR_USER_EXSIT	1
#define	RET_ERR_DATABASE 2
#define RET_ERR_USER   3
#define	RET_ERR_WORD	4

enum {
FUNC_START = 0,
FUNC_REG = 1,
FUNC_LOGIN,
FUNC_WORD,
FUNC_EXIT,
FUNC_HEART,
FUNC_END
};

int atoi_len(const char *buf, int len);
void itoa_len(char *buf, int val, int len);
int packet_pack_head(char *packet, int content_len, int func);
int packet_unpack_head(const char *packet, int *func);
void packet_disp_err(int err);

// 2.2 客户端-->服务端协议的打包/解包
int packet_pack_reg_req(char *packet, const char *name, const char *password);
void packet_unpack_reg_req(char *packet, char *name, char *password);

// 2.3 服务端-->客户端协议的解包/打包
int packet_pack_reg_resp(char *packet, int ret);
int packet_unpack_reg_resp(char *packet);
#endif // __PROT_H__


load_word.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include <sqlite3.h>

void back_move(char *str)
{
char *p = str + strlen(str);

while (p != str){
*(p + 1) = *p;
p--;
}

*(p + 1) = *p;
}

void add_quote(char *str)
{
char *p = str + strlen(str);

while (p != str){
if (*p == '\''){
back_move(p);
}

p--;
}

// 第一个字符为单引号时
if (*p == '\''){
back_move(p);
}
}

void load_words(sqlite3 *db, FILE *fp)
{
char *word, *explain;
char *p;
char buf[1024];
char sql[1024];
int id = 0;
int ret;
char *errmsg;

// 读取文件中的一行
while (fgets(buf, sizeof(buf), fp) != NULL){

// 去掉回车和换行(\r\n)
buf[strlen(buf) - 2] = '\0';
#ifdef __DEBUG__
puts(buf);
#endif
// 转义单引号(' --> '')
add_quote(buf);

#ifdef __DEBUG__
puts(buf);
#endif

// 分解成单词和解释
p = buf;
word = buf;

// 寻找第一个空格
while (*p != ' ') p++;
*p = '\0';

// 寻找解释的第一个字符
p += 1;
while (*p == ' ') p++;
explain = p;
#ifdef __DEBUG__
printf("word = %s\n", word);
printf("explain = %s\n", explain);
#endif
// 插入数据库
sprintf(sql, "insert into word values(%d,'%s', '%s');", id++, word, explain);
#ifdef __DEBUG__
printf("sql = %s\n", sql);
#endif
ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK){
fprintf(stderr, "sqlite3_exec: %s\n", errmsg);
exit(EXIT_FAILURE);
}

}
}

// load_words dict.db dict.txt
int main(int argc, const char *argv[])
{
int ret = 0;
sqlite3 *db;
FILE *fp;

if (argc < 3){
fprintf(stderr, "Usage: %s <db> <txt>\n", argv[0]);
exit(EXIT_FAILURE);
}

ret = sqlite3_open(argv[1], &db);
if (ret != SQLITE_OK){
fprintf(stderr, "sqlite3_open: %s\n", sqlite3_errmsg(db));
exit(EXIT_FAILURE);
}

fp = fopen(argv[2], "r");
if (NULL == fp){
perror("Fail to fopen.");
sqlite3_close(db);
exit(EXIT_FAILURE);
}

load_words(db, fp);

fclose(fp);
sqlite3_close(db);

return 0;
}


seqlite.c

/*
* 实现目标:
* 数据库操作
*
* 实现步骤
* 1. 获取用户信息
* 2. 插入用户
* 3. 找到单词,获得解释
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <sqlite3.h>

#ifdef __DEBUG__
void sqlite_disp_debug(char **resultp, int row, int column)
{
int i, j;

printf("----------------------------------------\n");
printf("%s: \n", __func__);

for (i = 0; i <= row; i++){
for (j = 0; j < column; j++){
printf("%s ", resultp[i * column + j]);
}
printf("\n");
}

printf("----------------------------------------\n");
printf("\n");
}
#endif // __DEBUG__

void back_move(char *s)
{
char *p = s + strlen(s);

while (p != s){
*(p + 1) = *p;
p--;
}

*(p + 1) = *p;
}

// ' --> ''
void add_quote(char *s)
{
char *p = s + strlen(s);

while (p != s){
if (*p == '\'') {
back_move(p);
}

p--;
}

// 第一个字符就是'\''
if (*s == '\''){
back_move(s);
}

}

// 1. 获取用户信息
int sqlite_find_user(sqlite3 *db, const char *name, char *passwd)
{
int ret = 0;
char sql[512];
char **resultp;
char *errmsg;
int row, column;

sprintf(sql, "select password from users where name='%s';", name);

#ifdef __DEBUG__
printf("----------------------------------------\n");
printf("%s: sql = %s\n", __func__, sql);
printf("----------------------------------------\n");
printf("\n");
#endif

ret = sqlite3_get_table(db, sql, &resultp, &row, &column, &errmsg);
if (ret != SQLITE_OK){
fprintf(stderr, "Fail to sqlite3_get_table : %s\n", errmsg);
ret = -1;
goto exit;
}

#ifdef __DEBUG__
sqlite_disp_debug(resultp, row, column);
#endif // __DEBUG__

if (row >= 1){
strcpy(passwd, resultp[1]);
}
ret = row;

sqlite3_free_table(resultp);

exit:
return ret;
}

// 2. 插入用户
int sqlite_insert_user(sqlite3 *db, const char *name, const char *passwd)
{
int ret = 0;
char password[128];
char sql[512];
char *errmsg;

ret = sqlite_find_user(db, name, password);
if (ret >= 1){
goto exit;
}

// 插入
sprintf(sql, "insert into users values('%s','%s');", name, passwd);

#ifdef __DEBUG__
printf("----------------------------------------\n");
printf("%s: sql = %s\n", __func__, sql);
printf("----------------------------------------\n");
printf("\n");
#endif

ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK){
fprintf(stderr, "Fail to sqlite3_exec : %s\n", errmsg);
ret = -1;
goto exit;
}
ret = 0;

exit:
return ret;
}

// 3. 找到单词,获得解释
int sqlite_find_word(sqlite3 *db, const char * word, char *explain)
{
int ret = 0;
char password[128];
char sql[512];
char word_quote[512];
char *errmsg;
char **resultp;
int row, column;

strcpy(word_quote, word);
add_quote(word_quote);
sprintf(sql, "select explain from word where word='%s';", word_quote);

#ifdef __DEBUG__
printf("----------------------------------------\n");
printf("%s: sql = %s\n", __func__, sql);
printf("----------------------------------------\n");
printf("\n");
#endif

ret = sqlite3_get_table(db, sql, &resultp, &row, &column, &errmsg);
if (ret != SQLITE_OK){
fprintf(stderr, "Fail to sqlite3_get_table : %s\n", errmsg);
ret = -1;
goto exit;
}

#ifdef __DEBUG__
sqlite_disp_debug(resultp, row, column);
#endif // __DEBUG__

// 获得解释
if (row >= 1){
strcpy(explain, resultp[1]);
}
ret = row;

sqlite3_free_table(resultp);
exit:
return ret;
}

#if 0
int main(void)
{
int ret = 0;
sqlite3 *db;
char password[128];
char explain[1024];

ret = sqlite3_open("dict.db", &db);
if (ret != SQLITE_OK){
fprintf(stderr, "Fail to sqlite3_get_table : %s\n", sqlite3_errmsg(db));
exit(EXIT_FAILURE);
}

// printf("%d\n", sqlite_find_user(db, "jacky", password));
printf("%d\n", sqlite_insert_user(db, "wangjie", "123"));
// printf("%d\n", sqlite_find_word(db, "a", explain));

sqlite3_close(db);
}
#endif


server.c

/*
* 实现目标:
* 在线电子词典,功能如下:
* 1. 注册
* 2. 登录
* 3. 查单词
* 4. 退出
* 5. 网络异常(扩展)
*
* 实现步骤:
* 1. 网络(TCP)
*    1.1 socket
*    1.2 bind
*    1.3 listen
*    1.4 accept(必须放入循环)
*    1.5 创子进程为客户端服务
*    1.6 recv_fix_len/send_fix_len
*    1.7 close
*
* 2. sqlite
*    sqlite.c sqlite.h
*
* 3. 协议
*    prot.c prot.h
*
* 4. 业务处理
*    4.1 协议框架接收框架
*    4.2 注册
*    4.3 登录
*    4.4 查单词
*    4.5 退出
*    4.6 心跳(选做)
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <errno.h>

#include <signal.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>

#include "server.h"
#include "prot.h"
#include "sqlite.h"

void do_client(int sockfd, sqlite3 *db)
{
int ret = 0;
int len = 0;
char packet[1024];

while (1){
recv_proc(sockfd, db);
}

// 1.7 close
close(sockfd);
exit(EXIT_SUCCESS);
}

// ./server 192.168.1.106 8887
int main(int argc, const char *argv[])
{
int ret = 0;
int len = 0;
sqlite3 *db;

int sockfd;
int clientfd;
struct sockaddr_in server_addr;
struct sockaddr_in peer_addr;
socklen_t addrlen = sizeof(struct sockaddr_in);

pid_t pid;

if (argc < 3){
printf("Usage: %s <ip> <port>\n", argv[0]);
exit(EXIT_FAILURE);
}

// 告诉Linux内核, 子进程结束的时候, 系统自动回收资源
if (signal(SIGCHLD, SIG_IGN) == SIG_ERR){
perror("Fail to signal");
exit(EXIT_FAILURE);
}

// 1.1 socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd){
perror("Fail to socket.");
exit(EXIT_FAILURE);
}

// 1.2 bind
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);

ret = bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (-1 == ret){
perror("Fail to bind.");
exit(EXIT_FAILURE);
}

// 1.3 listen
ret = listen(sockfd, 10);
if (-1 == ret){
perror("Fail to listen.");
exit(EXIT_FAILURE);
}

// 打开数据库
ret = sqlite3_open("dict.db", &db);
if (ret != SQLITE_OK){
fprintf(stderr, "Fail to sqlite3_get_table : %s\n", sqlite3_errmsg(db));
exit(EXIT_FAILURE);
}

while (1) {
// 1.4 accept
clientfd = accept(sockfd, (struct sockaddr *)&peer_addr, &addrlen);
if (-1 == clientfd){
perror("Fail to accept.");
ret = EXIT_FAILURE;
break;
}

#ifdef __DEBUG__
printf("-----------------------------------------\n");
printf("ip   : %s\n", inet_ntoa(peer_addr.sin_addr));
printf("port : %d\n", ntohs(peer_addr.sin_port));
printf("-----------------------------------------\n");
#endif // __DEBUG__

// 1.5 创子进程为客户端服务
pid = fork();
if (pid < 0){
ret = EXIT_FAILURE;
break;
}

if (0 == pid){
// 子进程
do_client(clientfd, db);
}

}

exit:
sqlite3_close(db);
close(sockfd);

return ret;
}

// 1.6 recv_fix_len/send_fix_len
int send_fix_len(int sockfd, const char *buf, int len)
{
int ret = 0;
int send_len = 0;

while (send_len < len){
ret = send(sockfd, buf + send_len, len - send_len, 0);
if (-1 == ret){
perror("Fail to send");
goto exit;
}

send_len += ret;
}
ret = send_len;

exit:
return ret;
}

int recv_fix_len(int sockfd, char *buf, int len)
{
int ret = 0;
int recv_len = 0;

while (recv_len < len){
ret = recv(sockfd, buf + recv_len, len - recv_len, 0);
if (-1 == ret){
perror("Fail to recv");
goto exit;
}

recv_len += ret;
}
ret = recv_len;

exit:
return ret;
}

//  4.1 协议框架接收框架
int recv_proc(int sockfd, sqlite3 *db)
{
char packet[1024];
int func;
int content_len;
int ret;
int len;

// 1. 接收包头
ret = recv_fix_len(sockfd, packet, LEN_PACKET_HEAD);
if (-1 == ret){
return -1;
}

// 2. 解析包头
content_len = packet_unpack_head(packet, &func);
if (content_len < 0){
return -1;
}

// 3. 接收包体
ret = recv_fix_len(sockfd, packet + LEN_PACKET_HEAD, content_len);
if (-1 == ret){
return -1;
}

#ifdef	__DEBUG__
printf("----------------------------------------\n");
printf("recv packet = %s\n", packet);
printf("----------------------------------------\n");
#endif

// 4. 各业自己处理
switch (func){
case FUNC_REG:
len = do_register(db, packet, content_len);
break;

case FUNC_LOGIN:
break;
}

#ifdef	__DEBUG__
printf("----------------------------------------\n");
printf("send packet = %s\n", packet);
printf("----------------------------------------\n");
#endif
// 5. 发送包
ret = send_fix_len(sockfd, packet, len);

return ret;
}

//  4.2 注册
// packet-输入进来注册包,输出-结果包
int do_register(sqlite3 *db, char *packet, int content_len)
{
char name[128];
char password[128];
int ret = 0;
int len = 0;

// 解析
packet_unpack_reg_req(packet, name, password);
#ifdef	__DEBUG__
printf("name = %s, password = %s\n", name, password);
#endif

//  插入用户到数据库,得到结构(每个功能自己的业务)
ret = sqlite_insert_user(db, name, password);
if (ret < 0){
ret = RET_ERR_DATABASE;
} else if (0 == ret){
ret = RET_SUCCESS;
} else if (ret > 0){
ret = RET_ERR_USER_EXSIT;
}

// 结果打包
len = packet_pack_reg_resp(packet, ret);

return len;
}


port.c

/*
* 实现目标:
* 交换协议
*
* 实现步骤:
* 1. 制定协议
*    客户端-->服务端
*    +------------+------------+-----------------------------+------+
*    | 包体长(4B) | 功能号(2B) |             包体            | 功能 |
*    +------------+------------+----------------+------------+------+
*    |  "0064"    |    "01"    |    用户名(32B) | 密码(32B)  | 注册 |
*    +------------+------------+----------------+------------+------+
*    |  "0064"    |    "02"    |    用户名(32B) | 密码(32B)  | 登录 |
*    +------------+------------+----------------+------------+------+
*    |            |    "03"    |              单词           | 查询 |
*    +------------+------------+-----------------------------+------+
*    |   "0000"   |    "04"    |              无             | 退出 |
*    +------------+------------+-----------------------------+------+
*    |   "0000"   |    "05"    |              无             | 心跳 |
*    +------------+------------+-----------------------------+------+
*
*    服务端-->客户端
*    +------------+------------+-----------------------------+------+
*    | 包体长(4B) | 功能号(2B) |             包体            | 功能 |
*    +------------+------------+-----------------------------+------+
*    |  "0002"    |    "01"    |             结果(2B)        | 注册 |
*    +------------+------------+-----------------------------+------+
*    |  "0002"    |    "02"    |             结果(2B)        | 登录 |
*    +------------+------------+----------+------------------+------+
*    |            |    "03"    | 结果(2B) |     单词解释     | 查询 |
*    +------------+------------+----------+------------------+------+
*    |  "0002"    |    "04"    |             结果(2B)        | 退出 |
*    +------------+------------+-----------------------------+------+
*    |   "0000"   |    "05"    |              无             | 心跳 |
*    +------------+------------+-----------------------------+------+
*
*    1. 结果
*       "00"-成功  "01"-已经注册 "02"-数据库异常  "03"-用户异常 "04"-单词不存在
*
* 2. 实现协议
*    2.1 包头打包和解包
*    2.2 客户端-->服务端协议的打包/解包
*    2.3 服务端-->客户端协议的解包/打包
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <errno.h>

#include "prot.h"

/*
* @brief						将指定长度的多个字符转换成一个整数
* @param[in]				buf													带整数字符的缓冲
* @param[in]				len													指定要转换的字符数
* return						转换出来的整数
* "0064" --> 64 调用方法: val = atoi_len(buf, 4);
*/
int atoi_len(const char *buf, int len)
{
char tmp[128];

memcpy(tmp, buf, len);
tmp[len] = '\0';

return atoi(tmp);
}

/*
* @brief						将整数转换成指定长度的多个字符
* @param[out]			buf													多个字符表示的整数
* @param[in]				val													要转换的整数
* @param[in]				len													字符个数
* return						转换出来的整数
* 64 --> "0064"  调用方法: itoa_len(buf, 64, 4);
*/
void itoa_len(char *buf, int val, int len)
{
char fmt[128];
char tmp[128];

// "%04d"
sprintf(fmt, "%%0%dd", len);
sprintf(tmp, fmt, val);
memcpy(buf, tmp, len);
}

// 2.1 包头打包和解包
int packet_pack_head(char *packet, int content_len, int func)
{
int len = 0;

// 包体长
itoa_len(packet + len, content_len, LEN_PACKET_LEN);
len += LEN_PACKET_LEN;

// 功能号
itoa_len(packet + len, func, LEN_PACKET_FUNC);
len += LEN_PACKET_FUNC;

return len;
}

int packet_unpack_head(const char *packet, int *func)
{
int len = 0;
int content_len;

// 长度
content_len = atoi_len(packet + len, LEN_PACKET_LEN);
len += LEN_PACKET_LEN;

// 功能号
*func = atoi_len(packet + len, LEN_PACKET_FUNC);
len += LEN_PACKET_FUNC;

// 检查功能号是否合法
if (*func <= FUNC_START || *func >= FUNC_END){
return -1;
}

return content_len;
}

void packet_disp_err(int err)
{
switch (err){
case RET_SUCCESS:
printf("successfully.\n");
break;

case RET_ERR_USER_EXSIT:
printf("User is exsit.\n");
break;

case RET_ERR_DATABASE:
printf("Database is error.\n");
break;

case RET_ERR_USER:
printf("User name or password is error.\n");
break;

case RET_ERR_WORD:
printf("Word is not exsit.\n");
break;

default:
break;
}
}

// 2.2 客户端-->服务端协议的打包/解包
int packet_pack_reg_req(char *packet, const char *name, const char *password)
{
int len = 0;

len = packet_pack_head(packet, LEN_PACKET_USER + LEN_PACKET_PASSWORD, FUNC_REG);

// 用户名
strcpy(packet + len, name);
len += LEN_PACKET_USER;

// 密码
strcpy(packet + len, password);
len += LEN_PACKET_PASSWORD;

return len;
}

void packet_unpack_reg_req(char *packet, char *name, char *password)
{
int len = LEN_PACKET_HEAD;

// 用户名
strcpy(name, packet + len);
len += LEN_PACKET_USER;

// 密码
strcpy(password, packet + len);
len += LEN_PACKET_PASSWORD;
}

// 2.3 服务端-->客户端协议的解包/打包
// 打包结果(2B)
int packet_pack_reg_resp(char *packet, int ret)
{
int len = 0;

len = packet_pack_head(packet, LEN_PACKET_RET, FUNC_REG);

// 结果
itoa_len(packet + len, ret, LEN_PACKET_RET);
len += LEN_PACKET_RET;

return len;
}

// 返回结果(2B)
int packet_unpack_reg_resp(char *packet)
{
int ret;
int len = LEN_PACKET_HEAD;

// 结果
ret = atoi_len(packet + len, LEN_PACKET_RET);

return ret;
}


客户端源码:

client.h

#ifndef	__CLIENT_H__
#define __CLIENT_H__

void DispMenu(void);
void register_input(char *name, char *password);
int do_register(int sockfd);

int send_fix_len(int sockfd, const char *buf, int len);
int recv_fix_len(int sockfd, char *buf, int len);
int recv_packet(int sockfd, char *packet);

#endif // __CLIENT_H__


port.h

#ifndef	__PROT_H__
#define __PROT_H__

#define	LEN_PACKET_LEN	4
#define LEN_PACKET_FUNC	2
#define LEN_PACKET_HEAD	(LEN_PACKET_LEN + LEN_PACKET_FUNC)
#define LEN_PACKET_USER	32
#define LEN_PACKET_PASSWORD 32
#define LEN_PACKET_RET 2

#define RET_SUCCESS			0
#define RET_ERR_USER_EXSIT	1
#define	RET_ERR_DATABASE 2
#define RET_ERR_USER   3
#define	RET_ERR_WORD	4

enum {
FUNC_START = 0,
FUNC_REG = 1,
FUNC_LOGIN,
FUNC_WORD,
FUNC_EXIT,
FUNC_HEART,
FUNC_END
};

int atoi_len(const char *buf, int len);
void itoa_len(char *buf, int val, int len);
int packet_pack_head(char *packet, int content_len, int func);
int packet_unpack_head(const char *packet, int *func);
void packet_disp_err(int err);

// 2.2 客户端-->服务端协议的打包/解包
int packet_pack_reg_req(char *packet, const char *name, const char *password);
void packet_unpack_reg_req(char *packet, char *name, char *password);

// 2.3 服务端-->客户端协议的解包/打包
int packet_pack_reg_resp(char *packet, int ret);
int packet_unpack_reg_resp(char *packet);
#endif // __PROT_H__


client.c

/*
* 实现目标:
* 在线电子词典,功能如下:
* 1. 注册
* 2. 登录
* 3. 查单词
* 4. 退出
* 5. 网络异常(扩展)
*
* 实现步骤:
* 1. 网络(TCP)
*    1.1 socket
*    1.2 connect
*    1.3 recv_fix_len/send_fix_len
*    1.4 close(整个程序退出时调用)
*
* 2. 用户输入
*    2.1 主界面(进入下一界面和退出)
*    2.2 注册界面(输入用户名和密码)
*    2.3 登录界面(输入用户名和密码)
*    2.4 查单词界面(输入单词)
*
* 3. 协议
*    prot.c prot.h
*
* 4. 业务处理
*    4.1 协议框架接收框架
*    4.2 注册
*    4.3 登录
*    4.4 查单词
*    4.5 退出
*    4.6 心跳(选做)
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "client.h"
#include "prot.h"

//  2.1 主界面
void DispMenu(void)
{
printf("1. register\n");
printf("2. login\n");
printf("3. quit\n");
putchar('>');
}

// 2.2 注册界面
void register_input(char *name, char *password)
{
char buf[1024];

do {
printf("user: ");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0';
strcpy(name, buf);

} while (strlen(name) == 0);

do {
printf("password: ");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0';
strcpy(password, buf);
} while (strlen(password) == 0);

#ifdef __DEBUG__
printf("name = %s\n", name);
printf("password = %s\n", password);
#endif

}

int do_register(int sockfd)
{
char name[128];
char password[128];
char packet[1024];
int len;
int ret;

//  1.获取用户名和密码
register_input(name, password);

// 2.将用户名和密码打包发送
len = packet_pack_reg_req(packet, name, password);
ret = send_fix_len(sockfd, packet, len);
if (-1 == ret){
return -1;
}

// 接收服务端返回
len = recv_packet(sockfd, packet);
if (len < 0){
return -1;
}

// 解析打印结果
ret = packet_unpack_reg_resp(packet);
packet_disp_err(ret);
}

// ./client 192.168.1.106 8887
int main(int argc, const char *argv[])
{
int ret = 0;
int len = 0;

int select;
int login = 0;

int sockfd;
struct sockaddr_in server_addr;

if (argc < 3){
printf("Usage: %s <server ip> <port>\n", argv[0]);
exit(EXIT_FAILURE);
}

// 1.1 socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd){
perror("Fail to socket.");
exit(EXIT_FAILURE);
}

// 1.2 connect
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);

ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (-1 == ret){
perror("Fail to connect.");
exit(EXIT_FAILURE);
}

while (1){

DispMenu();
scanf("%d", &select);

// 处理行缓存中的'\n'
while (getchar() != '\n');

switch (select){
case 1:
ret = do_register(sockfd);
break;
}

}

// 1.4 close
close(sockfd);

return 0;
}

// 1.3 recv_fix_len/send_fix_len
int send_fix_len(int sockfd, const char *buf, int len)
{
int ret = 0;
int send_len = 0;

while (send_len < len){
ret = send(sockfd, buf + send_len, len - send_len, 0);
if (-1 == ret){
perror("Fail to send");
goto exit;
}

send_len += ret;
}
ret = send_len;

exit:
return ret;
}

int recv_fix_len(int sockfd, char *buf, int len)
{
int ret = 0;
int recv_len = 0;

while (recv_len < len){
ret = recv(sockfd, buf + recv_len, len - recv_len, 0);
if (-1 == ret){
perror("Fail to recv");
goto exit;
} else if (0 == ret){
printf("recv timeout!\n");
goto exit;
}

recv_len += ret;
}
ret = recv_len;

exit:
return ret;
}

//  4.1 协议框架接收框架
// packet -- 包的内容
// return -- 包长度
int recv_packet(int sockfd, char *packet)
{
int ret;
int func;
int content_len;

// 接收包头
ret = recv_fix_len(sockfd, packet, LEN_PACKET_HEAD);
if (-1 == ret){
return -1;
}

// 解析包头
content_len = packet_unpack_head(packet, &func);
if (content_len < 0){
return -1;
}

// 接收包体
ret = recv_fix_len(sockfd, packet + LEN_PACKET_HEAD, content_len);
if (-1 == ret){
return -1;
}

return LEN_PACKET_HEAD + content_len;
}


port.c

/*
* 实现目标:
* 交换协议
*
* 实现步骤:
* 1. 制定协议
*    客户端-->服务端
*    +------------+------------+-----------------------------+------+
*    | 包体长(4B) | 功能号(2B) |             包体            | 功能 |
*    +------------+------------+----------------+------------+------+
*    |  "0064"    |    "01"    |    用户名(32B) | 密码(32B)  | 注册 |
*    +------------+------------+----------------+------------+------+
*    |  "0064"    |    "02"    |    用户名(32B) | 密码(32B)  | 登录 |
*    +------------+------------+----------------+------------+------+
*    |            |    "03"    |              单词           | 查询 |
*    +------------+------------+-----------------------------+------+
*    |   "0000"   |    "04"    |              无             | 退出 |
*    +------------+------------+-----------------------------+------+
*    |   "0000"   |    "05"    |              无             | 心跳 |
*    +------------+------------+-----------------------------+------+
*
*    服务端-->客户端
*    +------------+------------+-----------------------------+------+
*    | 包体长(4B) | 功能号(2B) |             包体            | 功能 |
*    +------------+------------+-----------------------------+------+
*    |  "0002"    |    "01"    |             结果(2B)        | 注册 |
*    +------------+------------+-----------------------------+------+
*    |  "0002"    |    "02"    |             结果(2B)        | 登录 |
*    +------------+------------+----------+------------------+------+
*    |            |    "03"    | 结果(2B) |     单词解释     | 查询 |
*    +------------+------------+----------+------------------+------+
*    |  "0002"    |    "04"    |             结果(2B)        | 退出 |
*    +------------+------------+-----------------------------+------+
*    |   "0000"   |    "05"    |              无             | 心跳 |
*    +------------+------------+-----------------------------+------+
*
*    1. 结果
*       "00"-成功  "01"-已经注册 "02"-数据库异常  "03"-用户异常 "04"-单词不存在
*
* 2. 实现协议
*    2.1 包头打包和解包
*    2.2 客户端-->服务端协议的打包/解包
*    2.3 服务端-->客户端协议的解包/打包
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <errno.h>

#include "prot.h"

/*
* @brief						将指定长度的多个字符转换成一个整数
* @param[in]				buf													带整数字符的缓冲
* @param[in]				len													指定要转换的字符数
* return						转换出来的整数
* "0064" --> 64 调用方法: val = atoi_len(buf, 4);
*/
int atoi_len(const char *buf, int len)
{
char tmp[128];

memcpy(tmp, buf, len);
tmp[len] = '\0';

return atoi(tmp);
}

/*
* @brief						将整数转换成指定长度的多个字符
* @param[out]			buf													多个字符表示的整数
* @param[in]				val													要转换的整数
* @param[in]				len													字符个数
* return						转换出来的整数
* 64 --> "0064"  调用方法: itoa_len(buf, 64, 4);
*/
void itoa_len(char *buf, int val, int len)
{
char fmt[128];
char tmp[128];

// "%04d"
sprintf(fmt, "%%0%dd", len);
sprintf(tmp, fmt, val);
memcpy(buf, tmp, len);
}

// 2.1 包头打包和解包
int packet_pack_head(char *packet, int content_len, int func)
{
int len = 0;

// 包体长
itoa_len(packet + len, content_len, LEN_PACKET_LEN);
len += LEN_PACKET_LEN;

// 功能号
itoa_len(packet + len, func, LEN_PACKET_FUNC);
len += LEN_PACKET_FUNC;

return len;
}

int packet_unpack_head(const char *packet, int *func)
{
int len = 0;
int content_len;

// 长度
content_len = atoi_len(packet + len, LEN_PACKET_LEN);
len += LEN_PACKET_LEN;

// 功能号
*func = atoi_len(packet + len, LEN_PACKET_FUNC);
len += LEN_PACKET_FUNC;

// 检查功能号是否合法
if (*func <= FUNC_START || *func >= FUNC_END){
return -1;
}

return content_len;
}

void packet_disp_err(int err)
{
switch (err){
case RET_SUCCESS:
printf("successfully.\n");
break;

case RET_ERR_USER_EXSIT:
printf("User is exsit.\n");
break;

case RET_ERR_DATABASE:
printf("Database is error.\n");
break;

case RET_ERR_USER:
printf("User name or password is error.\n");
break;

case RET_ERR_WORD:
printf("Word is not exsit.\n");
break;

default:
break;
}
}

// 2.2 客户端-->服务端协议的打包/解包
int packet_pack_reg_req(char *packet, const char *name, const char *password)
{
int len = 0;

len = packet_pack_head(packet, LEN_PACKET_USER + LEN_PACKET_PASSWORD, FUNC_REG);

// 用户名
strcpy(packet + len, name);
len += LEN_PACKET_USER;

// 密码
strcpy(packet + len, password);
len += LEN_PACKET_PASSWORD;

return len;
}

void packet_unpack_reg_req(char *packet, char *name, char *password)
{
int len = LEN_PACKET_HEAD;

// 用户名
strcpy(name, packet + len);
len += LEN_PACKET_USER;

// 密码
strcpy(password, packet + len);
len += LEN_PACKET_PASSWORD;
}

// 2.3 服务端-->客户端协议的解包/打包
// 打包结果(2B)
int packet_pack_reg_resp(char *packet, int ret)
{
int len = 0;

len = packet_pack_head(packet, LEN_PACKET_RET, FUNC_REG);

// 结果
itoa_len(packet + len, ret, LEN_PACKET_RET);
len += LEN_PACKET_RET;

return len;
}

// 返回结果(2B)
int packet_unpack_reg_resp(char *packet)
{
int ret;
int len = LEN_PACKET_HEAD;

// 结果
ret = atoi_len(packet + len, LEN_PACKET_RET);

return ret;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: