fusion/mw/tcpclient/mw_tcpclient.c

263 lines
7.4 KiB
C
Executable File

/*************************************************
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);
}