* 版权所有 (c) 华为技术有限公司 2023-2023
* 功能说明: 基于 TCP 协议实现的通信库示例代码
* 服务端调用流程:VmiCreateConnection -> VmiListen -> VmiAccpet -> VmiSend/VmiRecv -> VmiCloseConnection
* 客户端调用流程:VmiCreateConnection -> (ConfigClientAddress -> VmiBeginConnect)/(VmiConnect) -> VmiSend/VmiRecv -> VmiCloseConnection
*/
#define LOG_TAG "Connection"
#include "Connection.h"
#include <cerrno>
#include <unistd.h>
#include <cstring>
#include <signal.h>
#include <atomic>
#include <thread>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include "logging.h"
using AbormalFunc = void(*)(int connection);
static std::atomic<bool> g_isInit(false);
static std::atomic<unsigned int> g_remoteIp(0);
static std::atomic<unsigned int> g_remotePort(0);
std::string InttoIP(unsigned int ip)
{
in_addr address;
address.s_addr = htonl(ip);
std::string ipStr = inet_ntoa(address);
return ipStr;
}
#if defined (__cplusplus)
extern "C" {
#endif
* @功能描述:创建连接对象
* @返回值:成功时返回新创建的通信连接描述符;错误时返回负数并设置errno
*/
int VmiCreateConnection()
{
if (!g_isInit.load()) {
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
WARN("Unable to catch signal SIGPIPE");
}
g_isInit = true;
}
int fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0) {
ERR("Failed to create connection, errno:(%d):%s", errno, strerror(errno));
return CONNECTION_DISCONNECT;
}
return fd;
}
* @功能描述:由服务端程序调用,指定监听的端口
* @参数 [in] connection:通信连接描述符
* @参数 [in] port:需监听的端口
* @返回值:成功时返回0;错误时返回-1并设置errno
*/
int VmiListen(int connection, unsigned int port)
{
int reuse = 1;
if (setsockopt(connection, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
ERR("Failed to listen, set connection:%d option REUSER failed, errno: (%d): %s",
connection, errno, strerror(errno));
return CONNECTION_FAIL_AND_CAN_RETRY;
}
int enable = 1;
if (setsockopt(connection, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)) < 0) {
ERR("Failed to listen, set connection:%d option TCP_NODELAY failed, errno: (%d): %s",
connection, errno, strerror(errno));
return CONNECTION_FAIL_AND_CAN_RETRY;
}
struct sockaddr_in localAddr {};
localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
localAddr.sin_port = htons(port);
if (::bind(connection, reinterpret_cast<struct sockaddr *>(&localAddr), sizeof(localAddr)) != 0) {
ERR("Failed to listen, bind connection:%d failed, errno: (%d) %s", connection, errno, strerror(errno));
return CONNECTION_FAIL_AND_CAN_RETRY;
}
const int backlog = 5;
if (listen(connection, backlog) != 0) {
ERR("Failed to listen connection:%d, errno: (%d) %s", connection, errno, strerror(errno));
return CONNECTION_FAIL_AND_CAN_RETRY;
}
INFO("Success bind and listen on connection:%d", connection);
return CONNECTION_SUCCESS;
}
* @功能描述:由服务端程序调用,调用后将阻塞线程直到新的连接出现,此函数返回新的通信连接描述符
* @参数 [in] connection:通信连接描述符
* @返回值:返回值表示与客户端建立新连接的描述符,错误时返回-1并设置errno
*/
int VmiAccept(int connection)
{
INFO("Connection:%d waitting new client", connection);
struct sockaddr_in clientAddr = {};
socklen_t clientLen = sizeof(clientAddr);
int clientSockFd = accept(connection, reinterpret_cast<struct sockaddr*>(&clientAddr), &clientLen);
if (clientSockFd < 0) {
ERR("Failed to accpet connection:%d, errno: (%d): %s, client fd:%d",
connection, errno, strerror(errno), clientSockFd);
return CONNECTION_FAIL_AND_CAN_RETRY;
}
char ipAddress[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(clientAddr.sin_addr), ipAddress, INET_ADDRSTRLEN);
INFO("Connection:%d accpet connect:%d, ip:%s, port:%d", connection, clientSockFd, ipAddress, static_cast<uint32_t>(ntohs(clientAddr.sin_port)));
struct timeval timeout = {
.tv_sec = 1,
.tv_usec = 0,
};
if (setsockopt(clientSockFd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) {
WARN("Failed to set connection:%d SO_SNDTIMEO, errno: (%d): %s", clientSockFd, errno, strerror(errno));
}
if (setsockopt(clientSockFd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
WARN("Failed to set connection:%d SO_RCVTIMEO, errno: (%d): %s", clientSockFd, errno, strerror(errno));
}
return clientSockFd;
}
* @功能描述:由客户端程序调用,配置客户端的IP和端口
* @参数 [in] ip:使用网络字节序表示的ip地址
* @参数 [in] port:对端的端口
*/
void VmiConfigClientAddress(unsigned int ip, unsigned int port)
{
g_remoteIp = ip;
g_remotePort = port;
}
* @功能描述:由客户端程序调用,用于与服务端建立连接,其开始连接的参数通过VmiConfigClientAddress进行设置
* @参数 [in] connection:通信连接描述符
* @参数 [in] ip:使用网络字节序表示的ip地址
* @参数 [in] port:对端的端口
* @返回值:成功返回0,;错误时返回负数并设置errno
*/
int VmiBeginConnect(int connection)
{
return VmiConnect(connection, g_remoteIp, g_remotePort);
}
void SetClientOptions(int connection)
{
constexpr int reuse = 1;
if (setsockopt(connection, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
ERR("Failed to set connection:%d opt REUSER, errno(%d): %s", connection, errno, strerror(errno));
}
struct linger tcplinger = {
.l_onoff = 1,
.l_linger = 0
};
if (setsockopt(connection, SOL_SOCKET, SO_LINGER, &tcplinger, sizeof(tcplinger)) < 0) {
ERR("Failed to set connection:%d opt LINGER, errno(%d): %s", connection, errno, strerror(errno));
}
struct timeval timeout = {
.tv_sec = 1,
.tv_usec = 0
};
if (setsockopt(connection, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) {
ERR("Failed to set connection:%d opt SO_SNDTIMEO, errno: (%d): %s", connection, errno, strerror(errno));
}
if (setsockopt(connection, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
ERR("Failed to set connection:%d SO_RCVTIMEO, errno: (%d): %s", connection, errno, strerror(errno));
}
constexpr int flag = 1;
if (setsockopt(connection, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)) < 0) {
ERR("Failed to set connection:%d TCP_NODELAY, errno: (%d): %s", connection, errno, strerror(errno));
}
}
* @功能描述:由客户端程序调用,用于与服务端建立连接
* @参数 [in] connection:通信连接描述符
* @参数 [in] ip:使用网络字节序表示的ip地址
* @参数 [in] port:对端的端口
* @返回值:成功返回0,;若错误时返回-1并设置errno
*/
int VmiConnect(int connection, unsigned int ip, unsigned int port)
{
SetClientOptions(connection);
struct sockaddr_in remoteAddr {};
remoteAddr.sin_family = AF_INET;
remoteAddr.sin_addr.s_addr = htonl(ip);
remoteAddr.sin_port = htons(port);
int retryCount = 0;
do {
int ret = connect(connection, (struct sockaddr*)&(remoteAddr), sizeof(remoteAddr));
if (ret == 0) {
INFO("Connection:%d connected to server ip:%s, port:%u success.",
connection, InttoIP(ip).c_str(), port);
return CONNECTION_SUCCESS;
}
ERR("Failed to connect connection:%d, ip:%u, ipStr:%s, port:%u, failed time:%d, ret:%d, errno(%d):%s, try again...",
connection, ip, InttoIP(ip).c_str(), port, retryCount, ret, errno, strerror(errno));
retryCount++;
const int maxRetry = 3;
if (retryCount > maxRetry) {
return CONNECTION_FAIL_AND_CAN_RETRY;
}
const int wait = 500;
std::this_thread::sleep_for(std::chrono::milliseconds(wait));
} while (true);
return CONNECTION_SUCCESS;
}
* @功能描述:发送数据给对端
* @参数 [in] connection:通信连接描述符
* @参数 [in] buf:指向待发送数据的指针
* @参数 [in] len:待发送数据的字节数
* @返回值:发送成功时返回发送的字节数;若错误时返回-1并设置errno
*/
ssize_t VmiSend(int connection, const uint8_t* buf, size_t len)
{
if (buf == nullptr || len == 0) {
ERR("Failed to send connection:%d data, input param buf is %s null or len:%zu is zero",
connection, buf == nullptr ? "" : "not", len);
return CONNECTION_PARAM_CHECK_FAILED;
}
struct timeval curTime = {};
struct timeval startTime = {};
(void)gettimeofday(&startTime, nullptr);
const long retryTimeOut = 3;
size_t remain = len;
while (remain > 0) {
ssize_t ret = ::send(connection, buf, remain, MSG_NOSIGNAL);
if (ret >= 0) {
auto bytesSent = static_cast<size_t>(ret);
if (bytesSent == remain) {
return len;
} else if (bytesSent < remain) {
remain -= bytesSent;
buf = buf + bytesSent;
(void)gettimeofday(&curTime, nullptr);
if ((curTime.tv_sec - startTime.tv_sec) > retryTimeOut) {
ERR("Failed to send connection:%d data, send len:%zu data, remain:%zu, bytesSent:%zu",
connection, len, remain, bytesSent);
return CONNECTION_DISCONNECT;
}
continue;
} else {
WARN("Connection:%d send data len:%zu, ret:%zu", connection, remain, bytesSent);
remain = 0;
continue;
}
} else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR || errno == ETIMEDOUT) {
LOG_RATE_LIMIT(ANDROID_LOG_WARN, 1, "Connection:%d send fail and try again, errno:%d, %s, ret:%zd, size:%zu",
connection, errno, strerror(errno), ret, len);
(void)gettimeofday(&curTime, nullptr);
if ((curTime.tv_sec - startTime.tv_sec) > retryTimeOut) {
ERR("Failed to send connection:%d data, send len:%zu data, remain:%zu", connection, len, remain);
return CONNECTION_DISCONNECT;
}
continue;
} else {
ERR("Failed to send connection:%d data len:%zu, errno (%d) %s", connection, len, errno, strerror(errno));
return CONNECTION_DISCONNECT;
}
}
return CONNECTION_DISCONNECT;
}
* @功能描述:接收对端发送的数据,阻塞式调用
* @参数 [in] connection:通信连接描述符
* @参数 [in] buf:指向用于存放接收数据的指针
* @参数 [in] len:存放接收数据指针指向内存的字节数
* @返回值:接收成功则返回接收到数据字节数,若无错误只是当前socket无内容或被打断则返回0,若错误则返回-1并设置erron
*/
ssize_t VmiRecv(int connection, uint8_t* buf, size_t len)
{
if (buf == nullptr || len == 0) {
ERR("Failed to recv connection:%d data, input param buf is %s null or len:%zu is zero",
connection, buf == nullptr ? "" : "not", len);
return CONNECTION_PARAM_CHECK_FAILED;
}
ssize_t ret = ::recv(connection, buf, len, 0);
if (ret > 0) {
int optVal[1] = {1};
if (setsockopt(connection, IPPROTO_TCP, TCP_QUICKACK, optVal, sizeof(optVal)) < 0) {
WARN("(Connection:%d set TCP_QUICKACK failed, errno(%d): %s", connection, errno, strerror(errno));
}
return ret;
} else if ((ret == 0) || (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR && errno != ETIMEDOUT)) {
ERR("Failed to recv connection:%d data, disconnected or recv fail, ret:%zd. errno(%d): %s, buf is %s null, size:%zu",
connection, ret, errno, strerror(errno), buf == nullptr ? "" : "not", len);
return CONNECTION_DISCONNECT;
} else {
LOG_RATE_LIMIT(ANDROID_LOG_WARN, 1, "(Connection:%d recv fail, can retry, ret:%zd. errno(%d): %s",
connection, ret, errno, strerror(errno));
return CONNECTION_FAIL_AND_CAN_RETRY;
}
}
* @功能描述:关闭通信连接
* @参数 [in] connection:通信连接描述符
*/
void VmiCloseConnection(int connection)
{
INFO("Closing connection:%d", connection);
int ret = shutdown(connection, SHUT_RDWR);
if (ret < 0) {
ERR("Failed to close connection:%d, shutdown failed, errno:(%d):%s", connection, errno, strerror(errno));
}
ret = close(connection);
if (ret < 0) {
ERR("Failed to close connection:%d, errno:(%d):%s", connection, errno, strerror(errno));
}
INFO("Close connection:%d success", connection);
}
#if defined (__cplusplus)
}
#endif