NTcp.h文件
#ifndef NTcp_H
#define NTcp_H
#include <iostream>
#include <functional>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <thread>
#include <string>
#include <atomic>
#include <map>
enum E_network_model
{
NETWORK_MODEL_SERVER = 0,
NETWORK_MODEL_CLIENT
};
class NTcp
{
public:
NTcp(const std::string &ip, int port, enum E_network_model model = NETWORK_MODEL_CLIENT);
~NTcp();
bool network_open(int listen = 10);
int network_send(const char *buf, int size);
int network_recv(char *buf, int size, int timeout_ms = 0);
void start();
void stop_server();
private:
bool _setup();
void _service();
private:
int m_socket;
int m_listen;
fd_set m_set;
std::string m_ip;
int m_port;
enum E_network_model m_model;
struct timeval m_tv;
std::thread *m_thread;
std::atomic<bool> m_run;
std::map<int,struct sockaddr_in> m_client_map;
};
#endif // NTcp_H
NTcp.cpp文件
/*******************************************************************************
* *@ Author: Noch
* *@ Description:
* *@ Date: 2021-10-11 17:04:52
* *@ LastEditTime: 2021-10-15 15:29:44
* *@ LastEditors: Noch
* *@ FilePath: /cli_modbus/NTcp.cpp
* *@ 三十功名尘与土,八千里路云和月
*******************************************************************************/
#include "NTcp.h"
#include <cerrno>
#include <unistd.h>
#include <thread>
#include <chrono>
#include <errno.h>
#include <set>
#include "liblog/liblog.h"
const int g_timeout_s = 60*60;
const int g_timeout_us = 0;
NTcp::NTcp(const std::string &ip, int port, E_network_model model)
: m_ip(ip), m_port(port), m_model(model), m_run(true)
{
}
NTcp::~NTcp()
{
close(m_socket);
m_thread->join();
}
bool NTcp::network_open(int listen)
{
m_listen = listen;
return _setup();
}
bool NTcp::_setup()
{
int time = 2;
INITIALIZE_SOCKET:
close(m_socket);
struct sockaddr_in p_sockaddr;
m_socket = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == m_socket)
{
// perror("socket.");
loge("socket %s", strerror(errno));
return false;
}
memset(&p_sockaddr, 0, sizeof(p_sockaddr));
p_sockaddr.sin_family = AF_INET;
p_sockaddr.sin_port = htons(m_port);
switch (m_model)
{
case NETWORK_MODEL_SERVER:
{
p_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(m_socket, (struct sockaddr *)&p_sockaddr, sizeof(p_sockaddr)) < 0)
{
loge("socket绑定失败");
return false;
}
if (listen(m_socket, m_listen) < 0)
{
loge("socket监听失败");
return false;
}
m_thread = new std::thread(&NTcp::_service,this);
break;
}
case NETWORK_MODEL_CLIENT:
{
p_sockaddr.sin_addr.s_addr = inet_addr(m_ip.c_str());
if (connect(m_socket, (struct sockaddr *)&p_sockaddr, sizeof(p_sockaddr)) < 0)
{
loge("未与服务器建立连接,将在[%d]秒后进行重连", time);
std::this_thread::sleep_for(std::chrono::milliseconds(1000 * time));
time *= 2;
goto INITIALIZE_SOCKET;
return false;
}
break;
}
default:
break;
}
return true;
}
void NTcp::_service()
{
// int select_fd = m_socket;
m_tv.tv_sec = g_timeout_s;
m_tv.tv_usec = g_timeout_us;
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
logi("服务端初始化完成,等待客户端连接...");
std::set<int> select_fd;
select_fd.insert(m_socket);
while (m_run.load())
{
FD_ZERO(&m_set);
FD_SET(*select_fd.rbegin(),&m_set);
//添加监听套接字
for (std::pair<int,struct sockaddr_in> it : m_client_map)
{
FD_SET(it.first,&m_set);
}
//监测套接字
int ret = select(*select_fd.rbegin() + 1, &m_set,NULL,NULL,&m_tv);
if (0 > ret)
{
loge("select:%s",strerror(errno));
break;
}
else if (0 == ret)
{
loge("select timeout");
continue;
}
//判断是否有新的连接
if (FD_ISSET(m_socket,&m_set))
{
int client = accept(m_socket,(struct sockaddr*)&client_addr,&client_len);
if (0 >= client)
{
loge("accept:%s",strerror(errno));
continue;
}
if (m_client_map.size() < m_listen)
{
m_client_map[client] = client_addr;
logi("新客户端-[%s:%d],当前连接数-[%d]",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),m_client_map.size());
// if (client > select_fd)
// {
// select_fd = client;
// }
select_fd.insert(client);
}
else
{
logw("超出服务器最大连接上限[%d],断开客户端[%s:%d]的连接",m_listen,inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
close(client);
break;
}
}
//判断套接字可读
for (std::pair<int,struct sockaddr_in> it : m_client_map)
{
if (FD_ISSET(it.first,&m_set))
{
char buf[256] = {0};
int ret = recv(it.first,buf,256,MSG_NOSIGNAL);
if (ret <= 0)
{
logw("读取长度:[%d],客户端[%s:%d]断开连接",ret,inet_ntoa(it.second.sin_addr),ntohs(it.second.sin_port));
close(it.first);
FD_CLR(it.first,&m_set);
select_fd.erase(it.first);
m_client_map.erase(it.first);
}
else
{
if (ret < 256)
{
memset(&buf[ret],'\0',1);
}
logi("客户端[%s:%d]接收数据:[%d]-[%s]",inet_ntoa(it.second.sin_addr),ntohs(it.second.sin_port),ret,buf);
}
}
}
}
}
int NTcp::network_send(const char *buf, int size)
{
int len = send(m_socket, buf, size, MSG_NOSIGNAL);
if (-1 == len)
{
_setup();
}
return len;
}
int NTcp::network_recv(char *buf, int size, int timeout_ms)
{
int len = 0;
fd_set p_select_read;
struct timeval time;
time.tv_sec = 0;
time.tv_usec = timeout_ms * 1000;
FD_ZERO(&p_select_read);
FD_SET(m_socket, &p_select_read);
int p_fs_sel = select(m_socket + 1, &p_select_read, NULL, NULL, &time);
if (p_fs_sel)
{
return recv(m_socket, buf, size, MSG_NOSIGNAL);
// if (-1 == len)
// {
// // _setup();
// }
// return len;
}
return -1;
}
void NTcp::start()
{
while(1)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
void NTcp::stop_server()
{
m_run = false;
}