赞
踩
注:本人已购买韦东山老师第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。
这一节我们继续修改电子书的源码,让电子书既能够通过标准输出打印,也能通过网络远程打印,代码框架如下图所示:
如上图所示:
我们仿照之前的代码框架,使用 debug_manager.c 管理 stdout.c 和 netprint.c,在 stdout.c 和 netprint.c 中分别使用 RegisterDebugOpr 函数向上注册 T_DebugOpr 结构体,T_DebugOpr 结构体的代码如下:
typedef struct DebugOpr{
char *name;
int isCanUsed;
int (*DebugInit)(void);
int (*DebugExit)(void);
int (*DebugPrint)(char *strData);
struct DebugOpr *ptNext;
}T_DebugOpr, *PT_DebugOpr;
要实现的功能:
1.接下来,我们仿照之前的 display_manager.c 编写 debug_manager.c,debug_manager.c 的代码如下:
#include <debug_manager.h> #include <config.h> #include <string.h> #include <stdio.h> static PT_DebugOpr g_ptDebugOprHead = NULL; int RegisterDebugOpr(PT_DebugOpr ptDebugOpr) { PT_DebugOpr ptCur; if (!g_ptDebugOprHead) { g_ptDebugOprHead = ptDebugOpr; ptDebugOpr->ptNext = NULL; } else { ptCur = g_ptDebugOprHead; while (ptCur->ptNext) { ptCur = ptCur->ptNext; } ptCur->ptNext = ptDebugOpr; ptDebugOpr->ptNext = NULL; } return 0; } void ShowDebugOpr(void) { PT_DebugOpr ptCur; int i = 0; if (!g_ptDebugOprHead) { printf("don't have InputOpr\n"); return; } else { ptCur = g_ptDebugOprHead; do{ printf("%02d %s\n", i++, ptCur->name); ptCur = ptCur->ptNext; }while(ptCur); } } PT_DebugOpr GetDebugOpr(char *pcName) { PT_DebugOpr ptCur; if (!g_ptDebugOprHead) { return NULL; } else { ptCur = g_ptDebugOprHead; do { if (strcmp(ptCur->name, pcName) == 0) return ptCur; else ptCur = ptCur->ptNext; } while (ptCur); } return NULL; } int DebugInit(void) { int iError; iError = StdoutInit(); iError |= NetPrintInit(); return iError; }
2.stdout.c 的代码如下:
#include <debug_manager.h> #include <stdio.h> #include <string.h> static int StdoutDebugPrint(char *strData) { /* 直接把输入信息使用printf打印出来 */ printf("%s\n", strData); return strlen(strData); } static int StdoutDebugInit(void) { return 0; } static int StdoutDebugExit(void) { return 0; } static T_DebugOpr g_tStdoutDebugOpr = { .name = "stdout", .isCanUsed = 1, .DebugInit = StdoutDebugInit, .DebugExit = StdoutDebugExit, .DebugPrint = StdoutDebugPrint, }; int StdoutInit(void) { return RegisterDebugOpr(&g_tStdoutDebugOpr); }
3.netprint.c 的代码如下:
#include <debug_manager.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <pthread.h> #define SERVER_PORT 5678 #define PRINT_BUF_SIZE (16*1024) static int g_iSocketServer; static struct sockaddr_in g_tSocketServerAddr; static struct sockaddr_in g_tSocketClientAddr; static int g_iHaveConnected = 0; /* 标记是否连接上,0:没连接上;1:已经连接上 */ static char *g_pcNetPrintBuf; static int g_iReadPos = 0; static int g_iWritePos = 0; static pthread_t g_tSendThreadID; static pthread_t g_tRecvThreadID; static pthread_mutex_t g_tNetDebugSendMutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t g_tNetDebugSendConVar = PTHREAD_COND_INITIALIZER; /* 判断环形缓冲区是否满 * 返回值:1:满;0:不满 */ static int isFull(void) { return (((g_iWritePos + 1) % PRINT_BUF_SIZE) == g_iReadPos); } /* 判断环形缓冲区是为空 * 返回值:1:为空;0:不为空 */ static int isEmpty(void) { return (g_iWritePos == g_iReadPos); } static int PutData(char cVal) { if (isFull()) { return -1; } else { g_pcNetPrintBuf[g_iWritePos] = cVal; g_iWritePos = (g_iWritePos + 1) % PRINT_BUF_SIZE; return 0; } } static int GetData(char *pcVal) { if (isEmpty()) { return -1; } else { *pcVal = g_pcNetPrintBuf[g_iReadPos]; g_iReadPos = (g_iReadPos + 1) % PRINT_BUF_SIZE; return 0; } } static void *NetDebugSendThreadFunction (void *pVoid) { char strTmpBuf[512]; char cVal; int i; int iAddrLen; int iSendLen; while (1) { /* 平时处于休眠状态 */ pthread_mutex_lock(&g_tNetDebugSendMutex); pthread_cond_wait(&g_tNetDebugSendConVar, &g_tNetDebugSendMutex); pthread_mutex_unlock(&g_tNetDebugSendMutex); while ((g_iHaveConnected == 1) && (!isEmpty())) { i = 0; /* 把环形缓冲区的数据取出来,最多取512字节 */ while ((i < 512) && (GetData(&cVal) == 0)) { strTmpBuf[i] = cVal; i++; } strTmpBuf[i] = '\0'; /* 执行到这里表示被唤醒 */ /* 把环形缓冲区的数据取出来用sendto函数发送打印信息给客户端 */ iAddrLen = sizeof(struct sockaddr); iSendLen = sendto(g_iSocketServer, strTmpBuf, i, 0, (const struct sockaddr *)&g_tSocketClientAddr, iAddrLen); } } return NULL; } static void *NetDebugRecvThreadFunction (void *pVoid) { int iRecvLen; unsigned char ucRecvBuf[1000]; socklen_t iAddrLen; struct sockaddr_in tSocketClientAddr; while (1) { /* recvfrom 如果没有数据就处于休眠状态 */ iAddrLen = sizeof(struct sockaddr); iRecvLen = recvfrom(g_iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen); if (iRecvLen > 0) { ucRecvBuf[iRecvLen] = '\0'; /* 解析数据: * debuglevel=0, 1, 2, ... : 修改打印级别 * sdtout = 0 : 关闭stdout打印 * sdtout = 1 : 打开stdout打印 * netprint = 0 :关闭netprint打印 * netprint = 1 :打开netprint打印 * setclient : 设置接受打印信息的客户端 */ if (strcmp((char *)ucRecvBuf, "setclient") == 0) { g_tSocketClientAddr = tSocketClientAddr; g_iHaveConnected = 1; } else if (strncmp((char *)ucRecvBuf, "debuglevel=", 11) == 0) { SetDebugLevel((char *)ucRecvBuf); } else { SetDebugChannel((char *)ucRecvBuf); } } } return NULL; } static int NetDebugInit(void) { int iRet; /* socket 初始化 */ g_iSocketServer = socket(AF_INET, SOCK_DGRAM, 0); /* SOCK_DGRAM:UDP数据报 */ if (g_iSocketServer == -1) { printf("socket error\n"); return -1; } g_tSocketServerAddr.sin_family = AF_INET; g_tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */ g_tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; /* INADDR_ANY表示可以和任何的主机通信 */ memset((unsigned char*)g_tSocketServerAddr.sin_zero, 0, sizeof(g_tSocketServerAddr.sin_zero)); iRet = bind(g_iSocketServer, (const struct sockaddr *)&g_tSocketServerAddr, sizeof(struct sockaddr)); if (iRet == -1) { printf("bind error\n"); return -1; } g_pcNetPrintBuf = (char *)malloc(PRINT_BUF_SIZE); if (g_pcNetPrintBuf == NULL) { close(g_iSocketServer); return -1; } /* 创建netprint发送线程:用来发送打印信息给客户端 */ pthread_create(&g_tSendThreadID, NULL, NetDebugSendThreadFunction, NULL); /* 创建netprint接受线程:用来接受控制信息,比如修改打印级别、打开或关闭打印 */ pthread_create(&g_tRecvThreadID, NULL, NetDebugRecvThreadFunction, NULL); return 0; } static int NetDebugExit(void) { /* 关闭socket,... */ close(g_iSocketServer); free(g_pcNetPrintBuf); return 0; } static int NetDebugPrint(char *strData) { /* 把数据放入环形缓冲区 */ int i; for (i = 0; i < strlen(strData); i++) { if (0 != PutData(strData[i])) break; } /* 如果已经有客户端连接了,就把数据通过网络发送给客户端 */ /* 可以使用sendto();或 唤醒线程(这里使用唤醒线程)唤醒netprint的发送线程 */ pthread_mutex_lock(&g_tNetDebugSendMutex); pthread_cond_signal(&g_tNetDebugSendConVar); pthread_mutex_unlock(&g_tNetDebugSendMutex); return 0; } static T_DebugOpr g_tNetDebugOpr = { .name = "netprint", .isCanUsed = 1, .DebugInit = NetDebugInit, .DebugExit = NetDebugExit, .DebugPrint = NetDebugPrint, }; int NetPrintInit(void) { return RegisterDebugOpr(&g_tNetDebugOpr); }
对于上述 netprint.c 的代码:
#include <stdarg.h> static int g_iDebugLevelLimit = 8; /* strBuf = "debuglevel=<0~9>" */ int SetDebugLevel(char *strBuf) { g_iDebugLevelLimit = strBuf[11] - '0'; return 0; } /* sdtout = 0 : 关闭stdout打印 * sdtout = 1 : 打开stdout打印 * netprint = 0 :关闭netprint打印 * netprint = 1 :打开netprint打印 */ int SetDebugChannel(char *strBuf) { char *pStrTmp; char strName[100]; PT_DebugOpr ptTmp; pStrTmp = strchr(strBuf, '='); if (!pStrTmp) { return -1; } else { strncpy(strName, strBuf, (pStrTmp - strBuf)); strName[pStrTmp - strBuf] = '\0'; ptTmp = GetDebugOpr(strName); if (!ptTmp) { return -1; } if (pStrTmp[1] == '0') { ptTmp->isCanUsed = 0; } else { ptTmp->isCanUsed = 1; } return 0; } } /* 需要在debug_manager.c向外提供打印函数 */ int DebugPrint(const char *pcFormat, ...) { /* 调用链表中所有isCanUsed为1的结构体的 DebugPrint函数 */ char strTmpBuf[1000]; char *pcTmp; va_list tArg; int iNum; PT_DebugOpr ptTmp = g_ptDebugOprHead; int debuglevel = DEFAULT_DBGLEVEL; va_start(tArg, pcFormat); iNum = vsprintf(strTmpBuf, pcFormat, tArg); va_end(tArg); strTmpBuf[iNum] = '\0'; pcTmp = strTmpBuf; /*根据打印级别决定是否打印 */ if ((strTmpBuf[0] == '<') && (strTmpBuf[2] == '>')) { debuglevel = strTmpBuf[1] - '0'; if (debuglevel >= 0 && debuglevel <= 9) { pcTmp = strTmpBuf + 3; } else { debuglevel = DEFAULT_DBGLEVEL; } } if (debuglevel > g_iDebugLevelLimit) { return -1; } while (ptTmp) { if (ptTmp->isCanUsed) { ptTmp->DebugPrint(pcTmp); } ptTmp = ptTmp->ptNext; } return 0; } /* 启动打印,前面的DebugInit只是注册T_DebugOpr结构体 */ int InitDebugChannel(void) { PT_DebugOpr ptTmp = g_ptDebugOprHead; while (ptTmp) { if (ptTmp->isCanUsed && ptTmp->DebugInit) { ptTmp->DebugInit(); } ptTmp = ptTmp->ptNext; } return 0; }
额外补充: 环形缓冲区,其实我们可以理解为它是数组,如下图图所示:
buf[w] = val;
更新写的位置:w = (w + 1) % Len;
(buf 的长度为 Len);val = buf[r];
更新读的位置:r = (r + 1) % Len;
r == w;
即当读的位置和写的位置相等时,环形缓冲区为空;(w + 1) % Len == r;
即当写的下一个位置等于读的位置时,环形缓冲区已满;#define BUF_SIZE (16*1024) /* 环形缓冲区的最大空间为16K */ static char g_cLoopBuf[BUF_SIZE]; /* 定义一个空间为16k的环形缓冲区 */ /* 定义环形缓冲区的读写位置,初始值为0 */ static int g_iReadPos = 0; static int g_iWritePos = 0; /* 判断环形缓冲区是为空 * 返回值:1:为空;0:不为空 */ static int isEmpty(void) { return (g_iWritePos == g_iReadPos); } /* 判断环形缓冲区是否满 * 返回值:1:满;0:不满 */ static int isFull(void) { return (((g_iWritePos + 1) % BUF_SIZE) == g_iReadPos); } /* 往环形缓冲区存放一个数据 * 返回值:0: 成功; -1:失败 */ static int PutData(char cVal) { if (isFull()) { return -1; } else { g_cLoopBuf[g_iWritePos] = cVal; g_iWritePos = (g_iWritePos + 1) % BUF_SIZE; return 0; } } /* 从环形缓冲区读一个数据 * 返回值:0: 成功; -1:失败 */ static int GetData(char *pcVal) { if (isEmpty()) { return -1; } else { *pcVal = g_cLoopBuf[g_iReadPos]; g_iReadPos = (g_iReadPos + 1) % BUF_SIZE; return 0; } }
4.把config.h的#define DBG_PRINTF printf
修改为 #define DBG_PRINTF DebugPrint
。
5.main.c的修改,在 main.c 中我们需要对 T_DebugOpr 结构体进行初始化,并启动 DebugPrint,同时添加响应的打印信息,main.c 的代码如下:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <config.h> #include <draw.h> #include <encoding_manager.h> #include <fonts_manager.h> #include <disp_manager.h> #include <input_manager.h> #include <debug_manager.h> #include <string.h> /* ./show_file [-s Size] [-d display] [-f font_file] [-h HZK] <text_file> */ int main(int argc, char **argv) { int iError; unsigned int dwFontSize = 16; char acHzkFile[128]; char acFreetypeFile[128]; char acTextFile[128]; char acDisplay[128]; int bList = 0; T_InputEvent tInputEvent; acHzkFile[0] = '\0'; acFreetypeFile[0] = '\0'; acTextFile[0] = '\0'; strcpy(acDisplay, "fb"); iError = DebugInit(); if (iError) { DBG_PRINTF("DebugInit error!\n"); return -1; } InitDebugChannel(); while ((iError = getopt(argc, argv, "ls:f:h:d:")) != -1) { switch(iError) { case 'l': { bList = 1; break; } case 's': { dwFontSize = strtoul(optarg, NULL, 0); break; } case 'f': { strncpy(acFreetypeFile, optarg, 128); acFreetypeFile[127] = '\0'; break; } case 'h': { strncpy(acHzkFile, optarg, 128); acHzkFile[127] = '\0'; break; } case 'd': { strncpy(acDisplay, optarg, 128); acDisplay[127] = '\0'; break; } default: { DBG_PRINTF("Usage: %s [-s Size] [-d display] [-f font_file] [-h HZK] <text_file>\n", argv[0]); DBG_PRINTF("Usage: %s -l\n", argv[0]); return -1; break; } } } if (!bList && optind >= argc) { DBG_PRINTF("Usage: %s [-s Size] [-d display] [-f font_file] [-h HZK] <text_file>\n", argv[0]); DBG_PRINTF("Usage: %s -l\n", argv[0]); return -1; } iError = DisplayInit(); if (iError) { DBG_PRINTF("DisplayInit error!\n"); return -1; } iError = FontsInit(); if (iError) { DBG_PRINTF("FontsInit error!\n"); return -1; } iError = EncodingInit(); if (iError) { DBG_PRINTF("EncodingInit error!\n"); return -1; } iError = InputInit(); if (iError) { DBG_PRINTF("InputInit error!\n"); return -1; } if (bList) { DBG_PRINTF("supported display:\n"); ShowDispOpr(); DBG_PRINTF("supported font:\n"); ShowFontOpr(); DBG_PRINTF("supported encoding:\n"); ShowEncodingOpr(); DBG_PRINTF("supported input:\n"); ShowInputOpr(); DBG_PRINTF("supported debug chanel:\n"); ShowDebugOpr(); return 0; } strncpy(acTextFile, argv[optind], 128); acTextFile[127] = '\0'; iError = OpenTextFile(acTextFile); if (iError) { DBG_PRINTF("OpenTextFile error!\n"); return -1; } iError = SetTextDetail(acHzkFile, acFreetypeFile, dwFontSize); if (iError) { DBG_PRINTF("SetTextDetail error!\n"); return -1; } iError = SelectAndInitDisplay(acDisplay); if (iError) { DBG_PRINTF("SelectAndInitDisplay error!\n"); return -1; } iError = AllInputDevicesInit(); if (iError) { DBG_PRINTF("AllInputDevicesInit error!\n"); return -1; } iError = ShowNextPage(); if (iError) { DBG_PRINTF("Error to show first page\n"); return -1; } DBG_PRINTF("Enter 'n' to show next page, 'u' to show previous page, 'q' to exit:\n"); while (1) { //printf("Enter 'n' to show next page, 'u' to show previous page, 'q' to exit: "); if (0 == GetInputEvent(&tInputEvent)) { if (tInputEvent.iVal == INPUT_VALUE_DOWN) { ShowNextPage(); } else if (tInputEvent.iVal == INPUT_VALUE_UP) { ShowPrePage(); } else if (tInputEvent.iVal == INPUT_VALUE_EXIT) { return 0; } } } return 0; }
6.客户端程序 netprint_client 的编写:改程序的主要功能是:① 接收服务器的打印信息,并通过 printf 从终端打印出来;② 向服务器发送控制信息。
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> /* socket * connect * send/recv */ #define SERVER_PORT 5678 /* * ./netprint_client <server_ip> debuglevel=<0-9> * ./netprint_client <server_ip> stdout=0|1 * ./netprint_client <server_ip> netprint=0|1 * ./netprint_client <server_ip> show // setclient,并且接收打印信息 */ int main(int argc, char **argv) { int iSocketClient; struct sockaddr_in tSocketServerAddr; int iRet; unsigned char ucRecvBuf[1000]; int iSendLen; int iRecvLen; int iAddrLen; if (argc != 3) { printf("Usage:\n"); printf("%s <server_ip> debuglevel=<0-9>\n", argv[0]); printf("%s <server_ip> stdout=0|1\n", argv[0]); printf("%s <server_ip> netprint=0|1\n", argv[0]); printf("%s <server_ip> show\n", argv[0]); return -1; } iSocketClient = socket(AF_INET, SOCK_DGRAM, 0); tSocketServerAddr.sin_family = AF_INET; tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */ if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr)) { printf("invalid server_ip\n"); return -1; } memset(tSocketServerAddr.sin_zero, 0, 8); if (strcmp(argv[2], "show") == 0) { /* 发送数据 */ iAddrLen = sizeof(struct sockaddr); iSendLen = sendto(iSocketClient, "setclient", 9, 0, (const struct sockaddr *)&tSocketServerAddr, iAddrLen); while (1) { /* 循环: 从网络读数据, 打印出来 */ iAddrLen = sizeof(struct sockaddr); iRecvLen = recvfrom(iSocketClient, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketServerAddr, &iAddrLen); if (iRecvLen > 0) { ucRecvBuf[iRecvLen] = '\0'; printf("%s\n", ucRecvBuf); } } } else { /* 发送数据 */ iAddrLen = sizeof(struct sockaddr); iSendLen = sendto(iSocketClient, argv[2], strlen(argv[2]), 0, (const struct sockaddr *)&tSocketServerAddr, iAddrLen); } return 0; }
7.测试:
./show_file -s 24 -d fb -f MSYH.TTF -h HZK16 utf8.txt
./netprint_client <server_ip> debuglevel=<0-9>
设置打印级别:1~9;./netprint_client <server_ip> stdout=0|1
关闭(0)、打开(1)标准输出;/netprint_client <server_ip> netprint=0|1
关闭(0)、打开(1)网络打印;/netprint_client <server_ip> show
打印接收到的信息。Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。