数码相框(十五、电子书支持远程打印)

注:本人已购买韦东山老师第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。

这一节我们继续修改电子书的源码,让电子书既能够通过标准输出打印,也能通过网络远程打印,代码框架如下图所示:

如上图所示:

  • 我们仿照之前的代码框架,使用 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;
    
  • 要实现的功能:

    • ① 其他文件调用DebugPrint函数打印;
    • ② DebugPrint 函数调用底层的结构体来打印,即调用 stdout.c 和 netprint.c提供的打印函数来打印;
    • ③ 可以选择是否从某个渠道(stdout.c或者netprint.c)打印;
    • ④ 对于netprint.c数据应该先存入一个buffer,然后当客户端连接之后,再用网络通信传出;
    • ⑤ 每条信息都有打印级别,可以设置要打印的级别。

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 的代码:

  • ① 对于 netprint.c,在网路通信中,我们把它作为服务端,所以初始化的时候,需要进行服务器相关的初始化操作;
  • ② 为了更快的打印速度,这里我们使用 UDP 的传输方式,避免 TCP 在传输出错时进行重传等操作导致打印速度慢的问题;
  • ③ 使用环形缓冲区存放需要发送的数据,如果有客户端连接了,就把数据通过网络发送给客户端 ;(关于环形缓冲区后面额外补充)
  • ④ 创建两个线程:发送线程、接收线程。其中,发送线程用于发送打印信息给客户端,接收线程用于接受控制信息,比如修改打印级别、打开或关闭打印;
  • ⑤ 创建后的发送线程和接收线程都处于休眠的状态,当接收线程接收到 “setclient” 表示已经连接上(实际上是获得了客户端的IP地址、端口等信息);连接上后,当有数据需要打印时,通过线程的条件变量唤醒发送线程,然后发送线程每次从环形缓冲区取出512字节的数据发送给客户端;
  • ⑥ 对于接收到的控制信息的处理, 需要在 debug_manager.c 添加如下函数:其中 SetDebugLevel 函数用于设置打印级别,SetDebugChannel 函数用于设置打印的渠道:打开/关闭标准输出,打开/关闭网络打印,DebugPrint 用于调用 T_DebugOpr 结构体的打印函数进行打印,InitDebugChannel 用于启动打印(初始化),前面的DebugInit只是注册T_DebugOpr结构体。
    #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;	
    }
    

额外补充: 环形缓冲区,其实我们可以理解为它是数组,如下图图所示:
在这里插入图片描述

  • ① 假设我们有一个长度为 10 的 buf,用于存放我们的数据,开始时读的位置 r 和写的位置 w 都是 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 打印接收到的信息。
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Louis@L.M.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值