Linux网络应用综合项目(6.15)
2015-06-16 20:24
711 查看
在线英语词典功能:
服务端源码:
seqlite.h
server.h
port.h
load_word.c
seqlite.c
server.c
port.c
客户端源码:
client.h
port.h
client.c
port.c
服务端源码:
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; }
相关文章推荐
- http请求利器: 今天配置出了RESTClient,用MAVEN构建了UI运行包
- Android中activity、service、broadcast、单例模式http请求的综合使用
- Linux网络异常处理(6.12)
- 301、302、200、206、304、404等HTTP状态引见
- 黑马程序员学习日记--网络编程
- HttpClient使用详解
- AxisFault faultCode: {http://schemas.xmlsoap.org/soap/envelope/}Server.userException
- Android程序:使用HttpClient进行Get方式通信
- HTTPie 工具使用入门
- Dart的HTTP请求和响应(2)
- 【HTTP】HTTP报文详解
- 前端面试 - Http Session问题,记录我愚蠢的回答=。=
- XMLHttpRequest().readyState的五种状态详解 .
- HTTP2 帧基础知识以及Header、CONTINUATION、DATA帧相关资料:
- 各国虚拟主机大盘点
- Android程序:使用Http的Post方式与网络交互通信
- 高并发网络编程之epoll详解
- python切换网络连接
- GRE作文备考——网络审查的必要性
- 转载和积累系列 - TCP协议详解