客户端服务端交互实现

时间:2022-12-25 17:01:00

问题

客户端业务逻辑如何实现?

与服务设备具体交互细节如何设计?

客户端业务逻辑实现

用户输入处理

  • 字符串空格处理,分割获取命令与参数

服务信息处理

  • 字符串预处理,分割获取服务命令
  • 存储服务命令与设备地址之间的映射(命令字符串 => 地址字符串)

客户端业务逻辑实现 - 用户输入处理

客户端服务端交互实现

 

服务端逻辑实现

查询消息处理

  • 接收广播,并回复UDP消息

服务命令处理

  • 接收 TCP 连接,通过 请求-响应 的模式进行服务

UDP响应模块设计

客户端服务端交互实现

 

服务模块设计

客户端服务端交互实现

TCP 响应模块设计

客户端服务端交互实现

客户端响应接收

 客户端服务端交互实现

客户端服务端交互实现

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include "utility.h"
#include "addr_mgr.h"
#include "udp_point.h"
#include "tcp_client.h"
#include "type_def.h"

#define BUF_SIZE    64
#define DESC_SIZE    32
#define ADDR_SIZE    16
#define USAGE_SIZE   256

#define DIM(a)      sizeof((a)) / sizeof((*a))

typedef struct 
{
    const char* cmd;
    void (*handler)(const char*);    
} Handler;

static int GetCharCount(const char* s, int c)
{
    int ret = 0;
    
    if(s)
    {
        while(*s)
        {
            if(*s == c)
            {
                ret++;
            }
            
            s++;
        }
    }
    
    return ret;
}

static void ParseCommand(const char* s)
{
    const char* desc = s;
    const char* addr = desc + DESC_SIZE;
    const char* usage = addr + ADDR_SIZE;
    int count = 0;
    int cnt = 0;
    char** arg = NULL;
    char** cmd = NULL;
    int r = 0;
    
    printf("desc: %s\n", desc);
    printf("addr: %s\n", addr);
    
    count = GetCharCount(usage, '\n');
    arg = Malloc2d(char, count, BUF_SIZE);
    
    if(arg && (count > 0))
    {
        r = DivideByChar(usage, '\n', arg, count, BUF_SIZE);
        
        for(int i = 0; i < r; i++)
        {   
            count = GetCharCount(arg[i], ' ') + 1;
            cmd = Malloc2d(char, count, BUF_SIZE);
            
            if(cmd && (count > 0))
            {
                cnt = DivideByChar(arg[i], ' ', cmd, count, BUF_SIZE);

                for(int j = 1; j < cnt; j++)
                {
                    AddrMgr_Add(cmd[j], addr);
                    printf("%s %s\n", cmd[j], addr);
                }
            }
            
            Free2d(cmd);
        }
    }
    
    Free2d(arg);
}

void Query_Handler(const char* s)
{   
    UdpPoint* point = UdpPoint_New(8888);
    Message* msg = NULL;
    int brd = 1;
    char* remote = "255.255.255.255";
    int port = 9999;
    int i = 0;
    
    if(point)
    {
        UdpPoint_SetOpt(point, SOL_SOCKET, SO_BROADCAST, &brd, sizeof(brd));
        msg = Message_New(TYPE_QUERY, 0, 0, 0, NULL, 0);
        
        if(msg)
        {
            UdpPoint_SendMsg(point, msg, remote, port);
            
            free(msg);
            msg = NULL;
        }
        
        while(i < 3)
        {
            if(UdpPoint_Available(point) > 0)
            {
                msg = UdpPoint_RecvMsg(point, NULL, NULL);
                
                if(msg)
                {
                    if(msg->type == TYPE_RESPONSE)
                    {
                        printf("Find service!\n");
                        
                        ParseCommand(msg->payload);
                        
                        i = 0;
                        free(msg);
                        msg = NULL;
                    }
                    else if(msg->type == TYPE_ERROR)
                    {
                        printf("Can NOT find service!\n");
                    }
                }
                
            }
            else
            {
                sleep(1);
                i++;
            }
        }
    }
    
    UdpPoint_Del(point);
}

void Touch_Handler(const char* s)
{   
    if(s && *s)
    {
        TcpClient* client = TcpClient_New();
        char* addr = AddrMgr_Find(s);
        Message* msg = NULL;
        
        if(client && addr && TcpClient_Connect(client, addr, 8888))
        {
            msg = Message_New(TYPE_TOUCH, 0, 0, 0, s, strlen(s) + 1);
            
            if(msg)
            {
                TcpClient_SendMsg(client, msg);
                
                free(msg);
                msg = NULL;
                
                msg = TcpClient_RecvMsg(client);
                
                if(msg && (msg->type == TYPE_RESPONSE))
                {
                    printf("%s\n", msg->payload);
                    free(msg);
                    msg = NULL;
                }
            }   
        }
        
        TcpClient_Del(client);
    }
}

Handler g_handler[] = {
    {"query", Query_Handler}, 
    {"touch", Touch_Handler},
};

int main(void)
{
    char line[BUF_SIZE] = {0};
    char** arg = NULL;
    int r = 0;
    
    printf("<<<< This is client demo >>>>\n");
    
    arg = Malloc2d(char, 2, BUF_SIZE);
    
    while(arg)
    {
        fgets(line, sizeof(line), stdin);
        line[strlen(line) - 1] = 0; 
        
        if(*line)
        {
            r = DivideByChar(line, ' ', arg, 2, BUF_SIZE);
            
            for(int i = 0; (i < DIM(g_handler) && (r > 0)); i++)
            {
                if(strcmp(arg[0], g_handler[i].cmd) == 0)
                {
                    g_handler[i].handler(arg[1]);
                    break;
                }
            }
        }
    }
    
    Free2d(arg);
    
    return 0;    
}

response_task.c

#include <stdio.h>
#include "response_task.h"
#include "udp_point.h"
#include "type_def.h"

#define DESC_SIZE    32
#define ADDR_SIZE    16
#define USAGE_SIZE   256

void* Response_Task(const char* arg)
{
    UdpPoint* point = NULL;
    Message* msg = NULL;
    char remote[16] = {0};
    int port = 0;

    point = UdpPoint_New(9999);

    if(point)
    {
        printf("point = 0x%X\n", point);

        while(1)
        {
            msg = UdpPoint_RecvMsg(point, remote, &port);

            if(msg && msg->type == TYPE_QUERY)
            {
                free(msg);
                msg = NULL;
                
                msg = Message_New(TYPE_RESPONSE, 0, 0, 0, NULL, DESC_SIZE + ADDR_SIZE + USAGE_SIZE);

                if(msg)
                {
                    strncpy(msg->payload, Service_GetDesc(), DESC_SIZE);
                    strncpy(msg->payload + DESC_SIZE, Wifi_IpAddr(), ADDR_SIZE);
                    strncpy(msg->payload + DESC_SIZE + ADDR_SIZE, Service_GetUsage(), USAGE_SIZE);
                    
                    UdpPoint_SendMsg(point, msg, remote, port);
                    
                    free(msg);
                    msg = NULL;
                }
                else
                {
                    Message m = {TYPE_ERROR};
                    
                    UdpPoint_SendMsg(point, &m, remote, port);
                }
                
            }
            else
            {
                Message m = {TYPE_ERROR};
                    
                UdpPoint_SendMsg(point, &m, remote, port);
            }
        }

        UdpPoint_Del(point);
    }

    return NULL;
}

local_service.h

#ifndef LOCAL_SERVICE_H
#define LOCAL_SERVICE_H

typedef struct
{
	float    illumination;
	float    humidity;
	float    temperature;
	int      light;
} SvrData;

void Service_Init(void);
const char* Service_GetDesc(void);
const char* Service_GetUsage(void);
SvrData Service_GetData(void);
int Service_SetLight(int on);

#endif

local_service.c

#include "local_service.h"

void Service_Init(void)
{
    
}

const char* Service_GetDesc(void)
{
    return "Environment Service";
}

const char* Service_GetUsage(void)
{
    return "Illumination: Ill_Get\n"
           "Temperature: Tem_Get\n"
           "Humidity: Hum_Get\n"
           "Light: Lig_Get Lig_Set_On Lig_Set_Off\n";
}

SvrData Service_GetData(void)
{
    SvrData ret = {188, 0.33, 35.6, 1};

    return ret;
}

int Service_SetLight(int on)
{
    int ret = 1;

    printf("set light: %d\n", on);

    return ret;
}

service_task.c

#include <stdio.h>
#include "stdlib.h"
#include <string.h>
#include "service_task.h"
#include "tcp_client.h"
#include "tcp_server.h"
#include "local_service.h"
#include "type_def.h"

typedef struct 
{
    const char* cmd;
    void* data;
    char* (*handler)(void*);    
} Handler;

static char* FormatNumber(float num)
{
    char* ret = (char*)malloc(16);
    
    snprintf(ret, 16, "%.2f", num);
    
    return ret;
}

static char* Ill_Get_Handler(void* data)
{
    return FormatNumber(Service_GetData().illumination);
}

static char* Tem_Get_Handler(void* data)
{
    return FormatNumber(Service_GetData().temperature);
}

static char* Hum_Get_Handler(void* data)
{
    return FormatNumber(Service_GetData().humidity);
}

static char* Lig_Get_Handler(void* data)
{
    char* ret = (char*)malloc(4);
    
    if(Service_GetData().light)
    {
        strcpy(ret, "on");
    }
    else
    {
        strcpy(ret, "off");
    }
    
    return ret;
}

static char* Lig_Set_Handler(void* data)
{
    char* ret = (char*)malloc(4);
    
    Service_SetLight((int)data);
    
    if(data)
    {
        strcpy(ret, "on");
    }
    else
    {
        strcpy(ret, "off");
    }
    
    return ret;
}

static Handler g_handler[] = 
{
    {"Ill_Get", NULL, Ill_Get_Handler},
    {"Tem_Get", NULL, Tem_Get_Handler},
    {"Hum_Get", NULL, Hum_Get_Handler},
    {"Lig_Get", NULL, Lig_Get_Handler},
    {"Lig_Set_On", (void*)1, Lig_Set_Handler},
    {"Lig_Set_Off", (void*)0, Lig_Set_Handler},
};

static int g_handler_size = sizeof(g_handler) / sizeof(*g_handler);

static void Server_Listener_Handler(TcpClient* client, int evt)
{
    if(evt == EVT_COON)
    {
        printf("a client connect\n");
    }
    else if(evt == EVT_DATA)
    {
        Message* msg = NULL;
        char* s = NULL;

        msg = TcpClient_RecvMsg(client);

        if(msg && (msg->type == TYPE_TOUCH))
        {
            printf("service type = %s\n", msg->payload);
        
            for(int i = 0; i < g_handler_size; i++)
            {
                if(strcmp(msg->payload, g_handler[i].cmd) == 0)
                {
                    s = g_handler[i].handler(g_handler[i].data);
                    
                    break;
                }
            }

            free(msg);
            msg = NULL;
            
            if(s)
            {
                msg = Message_New(TYPE_RESPONSE, 0, 0, 0, s, strlen(s) + 1);
                free(s);
                
                if(msg)
                {
                    TcpClient_SendMsg(client, msg);
                    free(msg);
                }
                else
                {
                    Message m = {TYPE_ERROR};
                    
                    TcpClient_SendMsg(client, &m);
                }
                
                free(s);
            }
        }
        else
        {
            const char* message = "Invalid touch request";
            
            free(msg);
            
            msg = Message_New(TYPE_RESPONSE, 0, 0, 0, message, strlen(message) + 1);
            TcpClient_SendMsg(client, &m);
        }
    }
    else if(evt == EVT_CLOSE)
    {
        printf("a client left\n");
    }

    return NULL;
}

void* Service_Task(const char* arg)
{
    TcpServer* server = NULL;

    server = TcpServer_New();

    if(server)
    {
        printf("server = 0x%X\n", server);

        Service_Init();
        TcpServer_SetListener(server, Server_Listener_Handler);
        TcpServer_Start(server, 8888, 5);
        TcpServer_DoWork(server);

        TcpServer_Del(server);
    }

    return NULL;
}

实验结果如下图所示

客户端服务端交互实现

客户端服务端交互实现 

课后思考

服务模块如何获取真实环境信息?