/************************************************* File name : pal_tcpclient.h Module : Author : amir Version : 0.1 Created on : 2022-02-07 Description : Data structure and function definitions required by the socket interface Modify History: 1. Date: Author: Modification: *************************************************/ #include "hthread.h" #include "mw_tcpclient.h" #include "hlog.h" #include "hbase.h" #include "hsocket.h" #include "hloop.h" #include "hevent.h" #define dump_addr(io){\ char localaddrstr[SOCKADDR_STRLEN] = {0};\ char peeraddrstr[SOCKADDR_STRLEN] = {0};\ SOCKADDR_STR(io->localaddr, localaddrstr);\ SOCKADDR_STR(io->peeraddr, peeraddrstr);\ hlogw("%s %s,%s\n", __func__, localaddrstr, peeraddrstr);} static void on_close(hio_t* io) { tcpclient *tclient = io->loop->userdata; if (tclient->hio == io){ tclient->hio = NULL; } dump_addr(io); } static void on_recv(hio_t* io, void* buf, int readbytes) { tcpclient *tclient = io->loop->userdata; iobuffer_appendData(&tclient->read_iobuf, buf, readbytes); tclient->handler.on_unpacket(tclient, &tclient->read_iobuf); } static void on_connect(hio_t* io) { tcpclient *tclient = io->loop->userdata; if (tclient->hio) { /*最快的已连接上了, 关闭后面的*/ hio_close(io); return; } dump_addr(io); tclient->hio = io; hio_setcb_read(io, on_recv); hio_read_start(io); TLV tlv = {.T = CONN_CONNECTED}; tclient->handler.on_packet(tclient, &tlv); } void tcpclient_conn(tcpclient *tclient, const char *domain_or_ip, const int port) { hio_t* io = hio_create_socket(tclient->loop, domain_or_ip, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE); if (io == NULL) { perror("socket"); exit(1); } dump_addr(io); tcp_nodelay(hio_fd(io), 1); hio_setcb_connect(io, on_connect); hio_setcb_close(io, on_close); hio_connect(io); TLV tlv = {.T = CONN_STARTED}; tclient->handler.on_packet(tclient, &tlv); } int32_t tcpclient_send(tcpclient *tclient, const char *data, const uint32_t sz){ char out_data[MAX_PATH]; uint32_t cnt = tclient->handler.on_pack(out_data, sizeof(out_data), data, sz); if ( cnt > 0 ){ if (tclient->status != 'E'){ hlogd(">>> %s size:%u", __func__, cnt); hio_write(tclient->hio, out_data, cnt); return OK; } } return NG; } static void *tcpclient_tast(void *param) { tcpclient *tclient = (tcpclient *)param; hloge("%s tcpclient:%p loop:%p\n",__func__, param, tclient->loop); tclient->status = 'R'; while(tclient->status != 'E'){ hloop_run(tclient->loop); } hlogw("%s exit:%p\n",__func__, param); //tclient->handler.on_funcpacket(tclient, CONN_DESTROY); tclient->th = 0; return(NULL); } tcpclient *tcpclient_init(int stack_size, packet_handler handler){ tcpclient *tclient = (tcpclient*)calloc(1, sizeof(tcpclient)); tclient->loop = hloop_new(0); tclient->loop->userdata = tclient; tclient->status = 'I';//idle tclient->stack_size = stack_size; tclient->handler = handler; iobuffer_init(&tclient->read_iobuf); tclient->th = hthread_create(tcpclient_tast, (void*)tclient); return tclient; } int32_t tcpclient_deinit(tcpclient *tclient){ if (tclient) { tclient->status = 'E'; hloop_wakeup(tclient->loop); hlogw("%s hloop_free\n",__func__); hloop_free(&tclient->loop); free(tclient); return OK; } return NG; } /**********************应用层demo***********************/ #define DEMO_UART_JSON (1) /* 1. 为了测试string在串口工具显示, 实际要发收TLV*/ /* 应用层四步 ** ** 一, 声明枚举指令 和 回调函数 ** 二, 填写 packet_cmd_s 的 s_cmd_list ** 三, 发数on_pack, 收数on_unpacket后回调 **/ /* 第一步 */ typedef enum { EV_LOGIN = 1000,//=>Server }EV_CMD; typedef struct{ EV_CMD cmd; uint32_t (*on_cmd)(tcpclient *client, TLV *tlv); }packet_cmd_s; static uint32_t on_login(tcpclient *client, TLV *tlv){ hloge("%s tlv->T:%u L:%u V:%s", __func__, tlv->T, tlv->L, tlv->V); return OK; } /* 第二步 */ static packet_cmd_s s_cmd_list[]={ {EV_LOGIN, on_login}, }; static int32_t on_packet(tcpclient *client, void *data){ json_pkt packet = {0}; char buf[MAX_PATH] = {0}; TLV *recv_tlv = (TLV *)data; switch(recv_tlv->T){ case CONN_STARTED: break; case CONN_CONNECTING: break; case CONN_CONNECTED: json_pkt_add_int(&packet, "cmd", EV_LOGIN); json_pkt_add_str(&packet, "udid", "my_uuid_is_123456789"); json_pkt_add_str(&packet, "wakeup_type", "pir"); break; case CONN_SECOND_TICK: break; case CONN_CLOSED: break; case CONN_DESTROY: break; default: for ( uint16_t i=0; i< sizeof(s_cmd_list)/sizeof(s_cmd_list[0]); i++ ){ if ( s_cmd_list[i].cmd == recv_tlv->T ){ s_cmd_list[i].on_cmd(client, recv_tlv); return OK; } } hloge("unknown cmd:%u", recv_tlv->T); break; } if (packet.json){ char *str = json_pkt_toJson_string(&packet); uint32_t len = 0; if (str && ( len = strlen(str)) > 0 && len + sizeof(TLV) < sizeof(buf)){ TLV *send_tlv =(TLV*)buf; send_tlv->T = packet.cmd; send_tlv->L = len; memcpy(send_tlv->V, str, len); tcpclient_send(client, (const char *)send_tlv, sizeof(TLV) + send_tlv->L); free(str); }else { hloge("error json:%p, len:%u", str, len); } json_pkt_reset(&packet); } return OK; } /* 第三步 */ int32_t on_pack(char *out_data, const uint32_t max_sz, const char *in_data, const uint32_t sz){ /* 1. 比如可以在这里对data进行加密 */ /* 2. in_data 是TLV */ uint32_t cnt = 0; if ( sz < max_sz ){ #if DEMO_UART_JSON TLV *tlv =(TLV*)in_data; memcpy(out_data, tlv->V, tlv->L); cnt = tlv->L; #else memcpy(out_data, in_data, sz); cnt = sz; #endif } return cnt; } int32_t on_unpacket(tcpclient *client, iobuffer *iobuf){ /*TLV*/ json_pkt packet = {0}; #if DEMO_UART_JSON if (json_pkt_parse(&packet, iobuf->iodata, "cmd") ){ char buf[MAX_PATH] = {0}; TLV *recv_tlv = (TLV *)buf; recv_tlv->T = packet.cmd; recv_tlv->L = iobuf->size; memcpy(recv_tlv->V, iobuf->iodata, iobuf->size); on_packet(client, recv_tlv); iobuffer_eraseall(iobuf); } #else TLV *tlv =(TLV*)iobuf->iodata; if (json_pkt_parse(&packet, tlv->V, "cmd") ){ iobuffer_erase(iobuf, 0, sizeof(TLV)+tlv->L); on_packet(client, &packet); } #endif if (packet.json){ json_pkt_deinit(&packet); }else{ hloge("<< unpack error \\n cannot be found: %s", iobuf->iodata); } return OK; } void tcpclient_test(){ packet_handler handler = {on_packet, on_unpacket, on_pack}; tcpclient *client = tcpclient_init(1024, handler); tcpclient_conn(client, "192.168.1.98", 60000); //tcpclient_conn(client, "192.168.1.238", 80); //tcpclient_conn(client, "www.baidu.com", 80); hv_sleep(3); }