项目中使用网络实现高速收发文件,经过验证,决定采用libuv库搭建tcpserver和tcpclient。libuv库是第三方库,其大量采用回调实现。同时,其官方关于server端以及client的demo都是十分简单,无法满足实际需求,因此在采用libuv的基础上,自行封装成类作为中间件,方便应用层调用。
一、封装svuv类
svuv类做为最顶层的类,该类将包含tcpclient以及tcpserver两个子功能。
svuv类允许添加一个服务端以及N个客户端;添加服务端后,会自动在库中申请200M发送缓存区和200M的接收缓存区;每个客户端同样会申请200M的发送缓存区以及200M的接收缓存区。(缓存区的大小根据实际清空修改一个宏即可)
其对外接口如下所述:
api | 参数 | 功能 |
---|---|---|
svuv() | svuv类的构造函数 | |
~svuv() | svuv类的析构函数 | |
void gStartLoop() | 启动uv库 | |
int32_t gaddTcpClient(std::string server_ip, uint16_t server_port, std::string local_ip, uint16_t local_port) | server_ip:服务器IP;server_port:服务器端口;local_ip:本地IP;local_port:本地端口 | 添加client端 |
int32_t gTcpClientSend(std::string local_ip, uint16_t local_port, uint8_t *sdata, int32_t sdata_len) | local_ip:本地IP;local_port:本地端口;sdata:发送数据;sdata_len:数据长度 | client端发送函数 |
int32_t gTcpClientRecv(std::string local_ip, uint16_t local_port, uint8_t *buff, int32_t buff_len) | local_ip:本地IP;local_port:本地端口;buff:接收区;buff_len:接收区长度 | client接收函数 |
bool gTcpClientGetConnectStatus(std::string local_ip, uint16_t local_port) | local_ip:本地IP;local_port:本地端口 | 获取客户端的连接状态 |
int32_t gaddTcpServer(std::string server_ip, uint16_t server_port) | server_ip:服务器IP;server_port:服务器端口 | 添加服务器 |
int32_t gTcpServerRecv(std::string client_ip, uint16_t client_port, uint8_t *buff, int32_t buff_len) | client_ip:客户端IP;client_port:客户端端口;buff:接收区;buff_len:接收区长度 | 获取客户端数据 |
int32_t gTcpServerSend(std::string client_ip, uint16_t client_port, uint8_t *sdata, int32_t sdata_len); | client_ip:客户端IP;client_port:客户端端口;sdata:发送数据;sdata_len:数据长度 | 向客户端发送数据 |
int32_t gTcpServerGetRemainSpace(std::string client_ip, uint16_t client_port) | client_ip:客户端IP;client_port:客户端端口 | 获取客户端接收缓存区的剩余空间 |
整个svuv的使用流程将会比原来的libuv库大大简化:
svuv做服务端时:
1、创建svuv类的对象;
2、调用gaddTcpServer添加服务端
3、调用gStartLoop启动libuv
4、在线程中调用gTcpServerRecv或者gTcpServerSend
svuv做客户端时:
1、创建svuv类的对象;
2、调用gaddTcpClient添加客户端
3、调用gStartLoop启动libuv
4、在线程中调用gTcpClientRecv或者gTcpClientSend
详细的使用demo见后面链接。
二、封装tcpclient
tcpclient类用于生成一个客户端对象;每一个client都拥有各自的发送缓存区sbuff和接收缓存区rbuff,都具备断线重连功能。利用构造函数生成client时,只需要传入所要连接的server端地址以及本地的地址即可。client生成后,会在底层自动创建200M发送缓存区和200M接收缓存区,同时创建两个定时器reconnectTimer以及sendTimer,定时器sendTimer用于将发送缓存区的数据经libuv发送出去,定时器reconnectTimer则会定时检查与server端的连接状态。
tcpclient的主要功能如下表所述:
API | 参数 | 功能 |
---|---|---|
tcpclient(uv_loop_t *ploop, std::string server_ip, uint16_t server_port, std::string local_ip, uint16_t local_port) | server_ip:服务器IP;server_port:服务器端口;local_ip:本地IP;local_port:本地端口 | 申请发送缓存区和接收缓存区;调用libuv进行tcp初始化配置;申请reconnectTimer以及sendT imer |
int32_t tcSendData(uint8_t *sdata, int32_t sdata_len) | sdata:发送数据;sdata_len:数据长度 | 将数据存入发送缓存区(保证不溢出) |
int32_t tcRecvData(uint8_t *buff, int32_t buff_len) | buff:接收缓存区;buff_len:缓存区长度 | 对数据进行初步解析,满足帧格式才将整帧数据从底层缓存区取出传给调用者 |
整个tcpclient的实现过程如下所述:
1、调用tcpclient进行初始化配置,包括申请发送和接收缓存区;配置定时器;绑定本地IP地址;绑定连接回调函数sTCAfterConnect;启动断线重连机制;
tcpclient::tcpclient(uv_loop_t *ploop, std::string server_ip, uint16_t server_port, std::string local_ip, uint16_t local_port)
{
int32_t iret = 0;
this->serverIP = server_ip;
this->serverPort = server_port;
this->localIP = local_ip;
this->localPort = local_port;
this->loop = ploop;
this->uvRbuf.base = new char [TCP_RECV_UVBUFF_SIZE];
this->uvRbuf.len = TCP_RECV_UVBUFF_SIZE;
this->rbuff = new uint8_t [TCP_RECV_BUFF_SIZE];
this->rbuffIn = 0;
this->rbuffOut = 0;
this->sbuff = new uint8_t [TCP_SEND_BUFF_SIZE];
this->sbuffIn = 0;
this->sbuffOut = 0;
this->repeatSendTime = 1;
this->connectStatus = false;
this->isReconnect = true;
this->isClosed = false;
do
{
iret = uv_mutex_init(&this->rMutex);
if (iret)
{
PlayerInfo("addTcpClient uv_mutex_init rMutex", iret);
break;
}
iret = uv_mutex_init(&this->sMutex);
if (iret)
{
PlayerInfo("addTcpClient uv_mutex_init sMutex", iret);
break;
}
iret = uv_timer_init(this->loop, &this->sendTimer); // 发送定时器
if (iret)
{
PlayerInfo("addTcpClient uv_timer_init sendTimer", iret);
break;
}
iret = uv_async_init(this->loop, &this->sendAsync, sTCSendAsync);
if (iret)
{
PlayerInfo("addTcpClient uv_async_init sendAsync", iret);
break;
}
iret = uv_timer_init(this->loop, &this->reconnectTimer); // 重连定时器
if (iret)
{
PlayerInfo("addTcpClient uv_timer_init reconnectTimer", iret);
break;
}
iret = uv_tcp_init(this->loop, &this->clientHandle);
if (iret)
{
PlayerInfo("addTcpClient uv_tcp_init", iret);
break;
}
iret = uv_tcp_nodelay(&this->clientHandle, 1);
if (iret)
{
PlayerInfo("addTcpClient uv_tcp_nodelay", iret);
break;
}
printf("uv_tcp_nodelay\n");
iret = uv_tcp_keepalive(&this->clientHandle, 1, 60);
if (iret)
{
PlayerInfo("addTcpClient uv_tcp_keepalive", iret);
break;
}
printf("uv_tcp_keepalive\n");
iret = uv_ip4_addr(server_ip.c_str(), server_port, &this->serverAddr);
if (iret)
{
PlayerInfo("addTcpClient uv_ip4_addr serverAddr", iret);
break;
}
iret = uv_ip4_addr(local_ip.c_str(), local_port, &this->localAddr); // 绑定本地IP和端口,这样server端才能获取到固定的IP和端口
if (iret)
{
PlayerInfo("addTcpClient uv_ip4_addr localAddr", iret);
break;
}
iret = uv_tcp_bind(&this->clientHandle, (const struct sockaddr*)&this->localAddr, 0);
if (iret)
{
PlayerInfo("addTcpClient uv_tcp_bind localAddr", iret);
break;
}
iret = uv_tcp_connect(&this->connectReq, &this->clientHandle, (const sockaddr*)&this->serverAddr, sTCAfterConnect);
if (iret)
{
PlayerInfo("addTcpClient uv_tcp_connect", iret);
break;
}
this->sendTimer.data = this;
this->sendAsync.data = this;
this->clientHandle.data = this;
this->connectReq.data = this;
this->reconnectTimer.data = this;
} while (0);
}
2、client连接成功后,启动接收回调函数sTCAfterRecv,当有数据进入,会自动存入接收缓存区rbuff(保证不溢出);
void tcpclient::sTCAfterConnect(uv_connect_t* handle, int status)
{
class tcpclient *tmpClient = (class tcpclient *)handle->data;
int32_t iret = 0;
if (status)
{
tmpClient->connectStatus = false;
PlayerInfo("sTCAfterConnect", status);
tmpClient->TCStopSend();
tmpClient->TCStartReconnect();
uv_close((uv_handle_t *)&tmpClient->clientHandle, tcpclient::sTCAfterClientClose);
return;
}
iret = uv_read_start(handle->handle, sTCAllocBufferForRecv, sTCAfterRecv); // 正常连接,开始接收数据
if (iret)
{
PlayerInfo("sTCAfterConnect uv_read_start", iret);
}
else
{
tmpClient->connectStatus = true;
tmpClient->TCStartSend();
}
if (tmpClient->isReconnect)
{
tmpClient->TCStopReconnect();
}
}
void tcpclient::sTCAllocBufferForRecv(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
class tcpclient *tmpClient = (class tcpclient *)handle->data;
assert(tmpClient);
*buf = tmpClient->uvRbuf;
}
void tcpclient::sTCAfterRecv(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf)
{
class tcpclient *tmpClient = (class tcpclient *)handle->data;
if (nread > 0)
{
uv_mutex_lock(&tmpClient->rMutex);
if ((tmpClient->rbuffIn + nread) <= TCP_RECV_BUFF_SIZE) // 在不溢出的清空下,将数据存入接收缓存区
{
memset(tmpClient->rbuff + tmpClient->rbuffIn, 0x00, nread);
memcpy(tmpClient->rbuff + tmpClient->rbuffIn, buf->base, nread);
tmpClient->rbuffIn += nread;
}
uv_mutex_unlock(&tmpClient->rMutex);
}
else if (nread < 0)
{
PlayerInfo("sTCAfterRecv ", nread);
tmpClient->TCStopSend();
tmpClient->TCStartReconnect();
uv_close((uv_handle_t *)handle, tcpclient::sTCAfterClientClose);
}
}
同时启动定时发送机制,每隔10ms将发送缓存区的数据sbuff经libuv库发送出去;
void tcpclient::sTCSendTimer(uv_timer_t* handle)
{
class tcpclient *tmpClient = (class tcpclient *)handle->data;
uv_async_send(&tmpClient->sendAsync); // 触发发送函数
}
void tcpclient::sTCSendAsync(uv_async_t *handle)
{
class tcpclient *tmpClient = (class tcpclient *)handle->data;
uv_buf_t bufs[CHUNKS_PER_WRITE];
uv_write_t *write_reqs = &tmpClient->write_reqs;
int32_t i = 0;
char *ptr = (char *)tmpClient->sbuff + tmpClient->sbuffOut;
uint32_t len = 0;
uv_mutex_lock(&tmpClient->sMutex);
if ((tmpClient->connectStatus) && (tmpClient->sbuffIn != tmpClient->sbuffOut))
{
for (i = 0; i < CHUNKS_PER_WRITE; ) // 按照libuv的格式组包uv_buf_t,然后调用uv_write发送数据
{
if ((tmpClient->sbuffOut + TCP_SEND_UVBUFF_SIZE) <= tmpClient->sbuffIn)
{
len = TCP_SEND_UVBUFF_SIZE;
}
else
{
len = tmpClient->sbuffIn - tmpClient->sbuffOut;
}
bufs[i] = uv_buf_init(ptr, len);
i++;
tmpClient->sbuffOut += len;
ptr += len;
if (tmpClient->sbuffOut == tmpClient->sbuffIn)
{
tmpClient->sbuffOut = 0;
tmpClient->sbuffIn = 0;
break;
}
}
if (i > 0)
{
write_reqs->data = tmpClient;
uv_write(write_reqs, (uv_stream_t *)&tmpClient->clientHandle, bufs, i, tcpclient::sTCAfterSend);
}
}
else if ((tmpClient->connectStatus) && (tmpClient->sbuffIn == tmpClient->sbuffOut))
{
tmpClient->TCStartSend();
}
uv_mutex_unlock(&tmpClient->sMutex);
}
void tcpclient::sTCAfterSend(uv_write_t* req, int status)
{
class tcpclient *tmpClient = (class tcpclient *)req->data;
if (status == 0) // 数据发送成功后,再次触发定时器
{
tmpClient->TCStartSend();
}
else
{
PlayerInfo("sTCAfterSend ", status);
tmpClient->TCStopSend();
}
}
3、客户端重连机制
void tcpclient::sTCReconnectTimer(uv_timer_t* handle)
{
class tcpclient *tmpClient = (class tcpclient *)handle->data;
int32_t iret;
if (!tmpClient->isReconnect)
return;
fprintf(stdout, "ip:%s port:%u start reconnect\n", tmpClient->localIP.c_str(), tmpClient->localPort);
do
{
iret = uv_tcp_init(tmpClient->loop, &tmpClient->clientHandle);
if (iret)
{
PlayerInfo("sTCReconnectTimer uv_tcp_init", iret);
break;
}
iret = uv_tcp_nodelay(&tmpClient->clientHandle, 1);
if (iret)
{
PlayerInfo("sTCReconnectTimer uv_tcp_nodelay", iret);
break;
}
iret = uv_tcp_keepalive(&tmpClient->clientHandle, 1, 60);
if (iret)
{
PlayerInfo("sTCReconnectTimer uv_tcp_keepalive", iret);
break;
}
iret = uv_ip4_addr(tmpClient->serverIP.c_str(), tmpClient->serverPort, &tmpClient->serverAddr);
if (iret)
{
PlayerInfo("sTCReconnectTimer uv_ip4_addr serverAddr", iret);
break;
}
iret = uv_ip4_addr(tmpClient->localIP.c_str(), tmpClient->localPort, &tmpClient->localAddr);
if (iret)
{
PlayerInfo("sTCReconnectTimer uv_ip4_addr localAddr", iret);
break;
}
iret = uv_tcp_bind(&tmpClient->clientHandle, (const struct sockaddr*)&tmpClient->localAddr, 0);
if (iret)
{
PlayerInfo("addTcpClient uv_tcp_bind localAddr", iret);
break;
}
iret = uv_tcp_connect(&tmpClient->connectReq, &tmpClient->clientHandle, (const sockaddr*)&tmpClient->serverAddr, sTCAfterConnect);
if (iret)
{
PlayerInfo("sTCReconnectTimer uv_tcp_connect", iret);
break;
}
tmpClient->clientHandle.data = tmpClient;
tmpClient->connectReq.data = tmpClient;
tmpClient->reconnectTimer.data = tmpClient;
return;
} while (0);
//reconnect failure, restart timer to trigger reconnect.
// uv_timer_stop(handle);
// tmpClient->repeatTime *= 2;
// uv_timer_start(handle, tcpclient::sTCReconnectTimer, tmpClient->repeatTime, tmpClient->repeatTime);
tmpClient->TCStopSend();
tmpClient->TCStartReconnect();
uv_close((uv_handle_t *)handle, tcpclient::sTCAfterClientClose);
}
void tcpclient::TCStartReconnect()
{
isReconnect = true;
this->clientHandle.data = this;
this->repeatTime = 1e3;
}
void tcpclient::TCStopReconnect()
{
this->isReconnect = false;
this->clientHandle.data = this;
this->repeatTime = 1e3;
uv_timer_stop(&this->reconnectTimer);
}
4、应用层调用tcSendData,将数据存入发送缓存区sbuff;
int32_t tcpclient::tcSendData(uint8_t *sdata, int32_t sdata_len)
{
int32_t ret = 0;
assert(sdata);
assert(sdata_len > 0);
if (this->connectStatus)
{
uv_mutex_lock(&this->sMutex);
if ((this->sbuffIn + sdata_len) <= TCP_SEND_BUFF_SIZE) // 确保不溢出
{
memcpy(this->sbuff + this->sbuffIn, sdata, sdata_len);
this->sbuffIn += sdata_len;
ret = sdata_len;
}
uv_mutex_unlock(&this->sMutex);
}
return ret;
}
5、应用层主动调用tcRecvData,及时将数据从接收缓存区rbuff取走(应用层必须尽快将数据取走,否则可能导致缓存区满而丢帧)
int32_t tcpclient::tcRecvData(uint8_t *buff, int32_t buff_len)
{
uint8_t *ptr = this->rbuff + this->rbuffOut;
uint16_t frameLen = 0;
assert(buff);
assert(buff_len > 0);
uv_mutex_lock(&this->rMutex);
if (this->rbuffIn != this->rbuffOut)
{
if (((ptr[0] == 0xD2) && (ptr[1] == 0x8C)) || // 这里要更改为实际的帧格式(或者直接将数据取出)
((ptr[0] == 0x4D) && (ptr[1] == 0x73)))
{
frameLen = htons(*(uint16_t *)(ptr + FRAME_OFFSET_DATALEN)) + 18;
if ((this->rbuffOut + frameLen) <= this->rbuffIn)
{
memcpy(buff, ptr, frameLen);
this->rbuffOut += frameLen;
if (this->rbuffOut == this->rbuffIn)
{
this->rbuffIn = 0;
this->rbuffOut = 0;
}
}
}
else
{
this->rbuffOut++;
}
}
uv_mutex_unlock(&this->rMutex);
return frameLen;
}
三、封装tcpserver
tcpserver类用于生成一个服务端对象,初始化时调用libuv库进行server端的配置,然后启动监听服务(监听128个客户端);当有新的客户端接入,便创建一个client对象;同时为client分配发送缓存区sbuff和接收缓存区rbuff,每个client端也都具备定时发送的功能。
tcpserver的主要功能如下表所述:
API | 参数 | 功能 |
---|---|---|
tcpserver(uv_loop_t *ploop, std::string server_ip, uint16_t server_port) | server_ip:服务器IP;server_port:服务器端口 | 初始化server,启动监听服务 |
int32_t tsGetAcceptClientRecvData(std::string accept_ip, uint16_t accept_port, uint8_t *buff, int32_t buff_len) | accept_ip:接入的客户端IP;accept_port:接入的客户端端口;buff:接收区;buff_len:接收区长度 | 对数据初步解析,满足帧格式后将整帧数据从底层缓存区取走传给调用者 |
int32_t tsSetAcceptClientSendData(std::string accept_ip, uint16_t accept_port, uint8_t *sdata, int32_t sdata_len) | accept_ip:接入的客户端IP;accept_port:接入的客户端端口;sdata:发送数据;sdata_len:数据长度 | 将数据存入对应客户端的发送缓存区 |
整个tcpserver的实现过程如下所述:
1、调用tcpserver进行初始化配置,包括绑定server地址,启动server监听服务AcceptConnection;
tcpserver::tcpserver(uv_loop_t *ploop, std::string server_ip, uint16_t server_port)
{
int32_t iret = 0;
this->loop = ploop;
this->serverIP = server_ip;
this->serverPort = server_port;
do
{
iret = uv_mutex_init(&this->acceptMutex);
if (iret)
{
PlayerInfo("tcpserver uv_mutex_init", iret);
break;
}
iret = uv_tcp_init(this->loop, &this->serverHandle);
if (iret)
{
PlayerInfo("tcpserver uv_tcp_init", iret);
break;
}
iret = uv_tcp_nodelay(&this->serverHandle, 1);
if (iret)
{
PlayerInfo("tcpserver uv_tcp_nodelay", iret);
break;
}
iret = uv_tcp_keepalive(&this->serverHandle, 1, 60);
if (iret)
{
PlayerInfo("tcpserver uv_tcp_keepalive", iret);
break;
}
iret = uv_ip4_addr(server_ip.c_str(), server_port, &this->addr);
if (iret)
{
PlayerInfo("tcpserver uv_ip4_addr", iret);
break;
}
iret = uv_tcp_bind(&this->serverHandle, (const struct sockaddr *)&this->addr, 0); // 绑定server地址
if (iret)
{
PlayerInfo("tcpserver uv_tcp_bind", iret);
break;
}
iret = uv_listen((uv_stream_t *)&this->serverHandle, 128, AcceptConnection); // 启动监听服务
if (iret)
{
PlayerInfo("tcpserver uv_listen", iret);
}
this->serverHandle.data = this;
return;
} while (0);
}
2、有新的客户端接入,则创建acceptclient;为每一个acceptclient分配发送缓存区sbuff和接收缓存区rbuff;对每个acceptclient配置发送定时器;
void tcpserver::AcceptConnection(uv_stream_t *server, int status)
{
int32_t iret = 0;
class tcpserver *tmpServer = (class tcpserver *)server->data;
assert(tmpServer);
if (status)
{
PlayerInfo("AcceptConnection ", status);
return;
}
class acceptclient *tmpAccept = new acceptclient(tmpServer->loop, server, tmpServer->serverIP, tmpServer->serverPort); // 有新的客户端接入,创建一个acceptclient
assert(tmpAccept);
uv_mutex_lock(&tmpServer->acceptMutex);
tmpServer->acceptclientVec.push_back(tmpAccept); // 将新接入的client加入到vector中
uv_mutex_unlock(&tmpServer->acceptMutex);
return;
}
3、acceptclient接入成功后,启动接收回调函数sAcceptAfterRecv;当有新的数据进入,自动将数据存入接收缓存区rbuff;
acceptclient::acceptclient(uv_loop_t *ploop, uv_stream_t *server, std::string server_ip, uint16_t server_port)
{
int32_t iret = 0;
this->loop = ploop;
// this->rbuff = new uint8_t[TCP_RECV_BUFF_SIZE];
this->rbuff = (uint8_t *)malloc(TCP_RECV_BUFF_SIZE);
this->rbuffIn = 0;
this->rbuffOut = 0;
// this->uvRbuf.base = new char[TCP_RECV_UVBUFF_SIZE];
this->uvRbuf.base = (char *)malloc(TCP_RECV_UVBUFF_SIZE);
this->uvRbuf.len = TCP_RECV_UVBUFF_SIZE;
// this->sbuff = new uint8_t[TCP_SEND_BUFF_SIZE];
this->sbuff = (uint8_t *)malloc(TCP_SEND_BUFF_SIZE);
this->sbuffIn = 0;
this->sbuffOut = 0;
this->serverIP = server_ip;
this->serverPort = server_port;
do
{
iret = uv_mutex_init(&this->rMutex);
if (iret)
{
PlayerInfo("acceptclient uv_mutex_init rMutex", iret);
break;
}
iret = uv_mutex_init(&this->sMutex);
if (iret)
{
PlayerInfo("acceptclient uv_mutex_init sMutex", iret);
break;
}
iret = uv_timer_init(this->loop, &this->sendTimer);
if (iret)
{
PlayerInfo("acceptclient uv_timer_init sendTimer", iret);
break;
}
iret = uv_async_init(this->loop, &this->sendAsync, sAcceptSendAsync);
if (iret)
{
PlayerInfo("acceptclient uv_async_init sendAsync", iret);
break;
}
iret = uv_tcp_init(this->loop, &this->acceptHandle);
if (iret)
{
PlayerInfo("acceptclient uv_tcp_init", iret);
break;
}
iret = uv_tcp_nodelay(&this->acceptHandle, 1);
if (iret)
{
PlayerInfo("addTcpClient uv_tcp_nodelay", iret);
break;
}
iret = uv_tcp_keepalive(&this->acceptHandle, 1, 60);
if (iret)
{
PlayerInfo("addTcpClient uv_tcp_keepalive", iret);
break;
}
iret = uv_accept((uv_stream_t *)server, (uv_stream_t *)&this->acceptHandle); // 客户端接入成功
if (iret)
{
PlayerInfo("acceptclient uv_accept", iret);
break;
}
struct sockaddr peername;
int namelen = sizeof(peername);
struct sockaddr_in compare_addr;
char client_ip[17];
uint16_t client_port;
uv_tcp_getpeername(&this->acceptHandle, &peername, &namelen); // 获取客户端IP和端口
check_sockname(&peername, server_ip.c_str(), server_port, client_ip, &client_port);
this->acceptIP = client_ip;
this->acceptPort = client_port;
fprintf(stdout, "newAcceptClient ip = %s, port = %d\n", client_ip, client_port);
iret = uv_read_start((uv_stream_t *)&this->acceptHandle, acceptclient::sAcceptAllocBufferForRecv, acceptclient::sAcceptAfterRecv); // 从客户端接收数据
if (iret)
{
PlayerInfo("AcceptConnection uv_read_start", iret);
break;
}
this->isConnect = true;
this->acceptHandle.data = this;
this->sendTimer.data = this;
this->sendAsync.data = this;
this->parent = server->data;
this->acceptStartSend();
} while (0);
}
void acceptclient::sAcceptAfterRecv(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf)
{
class acceptclient *tmpAccept = (class acceptclient *)client->data;
class tcpserver *tmpServer = (class tcpserver *)tmpAccept->parent;
unsigned long long now = 0;
if (nread > 0)
{
// sysGetCurPts(&now);
uv_mutex_lock(&tmpAccept->rMutex);
if ((tmpAccept->rbuffIn + nread) <= TCP_RECV_BUFF_SIZE) // 数据存入接收缓存区
{
memset(tmpAccept->rbuff + tmpAccept->rbuffIn, 0x00, nread);
memcpy(tmpAccept->rbuff + tmpAccept->rbuffIn, buf->base, nread);
tmpAccept->rbuffIn += nread;
}
// printf("time %lld us, recv %ld, all %u\n", now, nread, tmpAccept->rbuffIn);
uv_mutex_unlock(&tmpAccept->rMutex);
}
else if (nread < 0)
{
printf("close and delete acceptClient\n");
tmpAccept->acceptStopSend();
uv_close((uv_handle_t *)client, sAcceptAfterClose);
// tmpServer->tsDeleteAcceptClient(client, tmpAccept->acceptIP, tmpAccept->acceptPort); // 这个函数似乎会导致服务端程序崩溃
}
}
4、应用层调用tsSetAcceptClientSendData将要发送的数据存入缓存区,底层自行发送;
void acceptclient::sAcceptSendTimer(uv_timer_t *handle)
{
class acceptclient *tmpAccept = (class acceptclient *)handle->data;
if (tmpAccept->isConnect)
uv_async_send(&tmpAccept->sendAsync); // 触发发送
}
void acceptclient::sAcceptSendAsync(uv_async_t *handle)
{
class acceptclient *tmpAccept = (class acceptclient *)handle->data;
uv_buf_t bufs[CHUNKS_PER_WRITE];
uv_write_t *write_reqs = &tmpAccept->write_reqs;
int32_t i = 0;
char *ptr = (char *)tmpAccept->sbuff + tmpAccept->sbuffOut;
uint32_t len = 0;
uv_mutex_lock(&tmpAccept->sMutex);
if ((tmpAccept->isConnect) && (tmpAccept->sbuffIn != tmpAccept->sbuffOut))
{
for (i = 0; i < CHUNKS_PER_WRITE; ) // 按照kibuv的格式,组包发送
{
if ((tmpAccept->sbuffOut + TCP_SEND_UVBUFF_SIZE) <= tmpAccept->sbuffIn)
{
len = TCP_SEND_UVBUFF_SIZE;
}
else
{
len = tmpAccept->sbuffIn - tmpAccept->sbuffOut;
}
bufs[i] = uv_buf_init(ptr, len);
i++;
tmpAccept->sbuffOut += len;
ptr += len;
if (tmpAccept->sbuffOut == tmpAccept->sbuffIn)
{
tmpAccept->sbuffOut = 0;
tmpAccept->sbuffIn = 0;
break;
}
}
if (i > 0)
{
write_reqs->data = tmpAccept;
uv_write(write_reqs, (uv_stream_t *)&tmpAccept->acceptHandle, bufs, i, acceptclient::sAcceptAfterSend);
}
}
else if ((tmpAccept->isConnect) && (tmpAccept->sbuffIn == tmpAccept->sbuffOut)) // 连接状态下,即使没有数据需要发送,也要触发定时器
{
tmpAccept->acceptStartSend();
}
uv_mutex_unlock(&tmpAccept->sMutex);
}
void acceptclient::sAcceptAfterSend(uv_write_t *req, int status)
{
class acceptclient *tmpAccept = (class acceptclient *)req->data;
if (status == 0)
{
tmpAccept->acceptStartSend(); // 发送成功后,再次触发定时器,开始下一轮的发送周期
}
else
{
PlayerInfo("sAcceptAfterSend ", status);
tmpAccept->acceptStopSend();
}
}
void acceptclient::acceptStartSend(void)
{
this->repeatSendTime = 10;
uv_timer_start(&this->sendTimer, acceptclient::sAcceptSendTimer, this->repeatSendTime, 0);
}
void acceptclient::acceptStopSend(void)
{
this->repeatSendTime = 10;
uv_timer_stop(&this->sendTimer);
}
int32_t acceptclient::acceptSendData(uint8_t *sdata, int32_t sdata_len)
{
int32_t ret = 0;
assert(sdata);
assert(sdata_len > 0);
if (this->isConnect)
{
uv_mutex_lock(&this->sMutex);
if ((this->sbuffIn + sdata_len) <= TCP_SEND_BUFF_SIZE) // 应用层数据存入发送缓存区
{
memcpy(this->sbuff + this->sbuffIn, sdata, sdata_len);
this->sbuffIn += sdata_len;
ret = sdata_len;
}
uv_mutex_unlock(&this->sMutex);
}
return ret;
}
5、应用层调用tsGetAcceptClientRecvData将数据从底层取走(应用层必须尽快将数据取走,否则导致接收缓存区满而丢帧)
int32_t acceptclient::acceptRecvData(uint8_t *buff, int32_t buff_len)
{
uint8_t *ptr = this->rbuff + this->rbuffOut;
uint16_t frameLen = 0;
assert(buff);
assert(buff_len > 0);
uv_mutex_lock(&this->rMutex);
if (this->rbuffIn != this->rbuffOut)
{
if (((ptr[0] == 0xD2) && (ptr[1] == 0x8C)) || // 这里根据实际清空更改协议(或者直接将数据取走)
((ptr[0] == 0x4D) && (ptr[1] == 0x73)))
{
frameLen = htons(*(uint16_t *)(ptr + FRAME_OFFSET_DATALEN)) + 18;
if ((this->rbuffOut + frameLen) <= this->rbuffIn)
{
memcpy(buff, ptr, frameLen);
this->rbuffOut += frameLen;
memset(ptr, 0x00, frameLen);
if (this->rbuffOut == this->rbuffIn)
{
this->rbuffIn = 0;
this->rbuffOut = 0;
}
}
else
frameLen = 0;
}
else
{
this->rbuffOut++;
}
}
uv_mutex_unlock(&this->rMutex);
return frameLen;
}
四、测试用例
测试用例:
1、创建svuv对象;
2、添加客户端或者服务端;
3、创建线程进行收发。
整个demo可以见链接:gitee链接地址