头文件
#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include <mutex>
#include <string>
#include <condition_variable>
#include <future>
#include <set>
#include <map>
namespace PRONAME {
using namespace PRONAME;
class TcpClient
{
public:
TcpClient();
~TcpClient();
void run();
bool write(const void *data, unsigned int len,std::string ip, uint16_t port);
private:
void read();
void setNoblock(int fd);
void setKeepAlive(int fd);
int connectToHost(std::string ip, uint16_t port);
void disConnectToHost(int fd);
void disConnectToHost();
int fdSet(fd_set* read_fdset);
int isFdset(fd_set* read_fdset);
int getReadAvailable();
void removeZombieConnection();
private:
size_t m_maxConnect{1010};
std::mutex m_mutex;
std::condition_variable m_wait;
std::set<int> m_clientSet;
std::map<int,std::chrono::steady_clock::time_point> m_clientTimeout;
std::chrono::steady_clock::time_point m_activityTime;
};
}
#endif
源文件
#include "tcpclient.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/tcp.h>
#include <iostream>
#include <chrono>
#include <thread>
#include <fcntl.h>
namespace PRONAME {
TcpClient::TcpClient()
{
}
TcpClient::~TcpClient()
{
}
int TcpClient::fdSet(fd_set *read_fdset)
{
do
{
if(m_clientSet.empty()){
break;
}
FD_ZERO(read_fdset);
for(auto client:m_clientSet){
FD_SET(client, read_fdset);
}
auto it = m_clientSet.end();
it--;
return *it;
}while(0);
return -1;
}
int TcpClient::isFdset(fd_set* read_fdset)
{
for(auto client:m_clientSet){
if(FD_ISSET(client,read_fdset)){
return client;
}
}
return -1;
}
int TcpClient::getReadAvailable()
{
fd_set read_fdset;
int fd = fdSet(&read_fdset);
if(fd < 0 ){
return 0;
}
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 1000*50 ;
int ret = select(fd + 1 , &read_fdset, nullptr , nullptr , &timeout);
if(ret > 0){
return isFdset(&read_fdset);
}
if (ret == 0 ){
return 0;
}
return -1;
}
void TcpClient::removeZombieConnection()
{
if(m_maxConnect >= m_clientTimeout.size()){
return;
}
auto it = m_clientTimeout.begin();
auto time_point = it->second;
int fd = it->first;
for(;it!= m_clientTimeout.end();it++)
{
if(it->second < time_point){
fd = it->first;
time_point = it->second;
}
}
if(fd > 0){
disConnectToHost(fd);
}
}
void TcpClient::run()
{
while (1)
{
this->read();
}
}
bool TcpClient::write(const void *data, unsigned int len, std::string ip, uint16_t port)
{
int fd = connectToHost(ip,port);
if(fd < 0){
return false;
}
bool bRet = (len == send(fd,data,len,MSG_NOSIGNAL));
if(!bRet){
perror("tcp send");
std::cout << "tcp send error" << std::endl;
}
m_activityTime = std::chrono::steady_clock::now();
m_wait.notify_all();
return bRet;
}
void TcpClient::read()
{
std::unique_lock<std::mutex> lck(m_mutex);
if(m_clientSet.empty()){
m_wait.wait_for(lck,std::chrono::milliseconds(50));
}
if(m_clientSet.empty()){
return;
}
removeZombieConnection();
int fd = getReadAvailable();
if(fd < 1){
auto now = std::chrono::steady_clock::now();
auto diff = now - m_activityTime;
auto X = std::chrono::duration_cast<std::chrono::hours>(diff).count();
if(X > 1){
disConnectToHost();
}
return;
}
char buf[1024]={0};
int nRet = recv(fd,buf,1024,MSG_NOSIGNAL);
if(nRet <= 0){
std::cout << "server disconnect." << std::endl;
disConnectToHost(fd);
}else{
m_activityTime = std::chrono::steady_clock::now();
m_clientTimeout[fd] = m_activityTime;
std::cout << "tcp read len:" <<fd<<" "<<nRet<< buf<<std::endl;
}
}
void TcpClient::setKeepAlive(int fd)
{
int keep_alive = 1;
int keep_idle = 5;
int keep_interval = 1;
int keep_count = 3;
int ret = 0;
if (-1 == (ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keep_alive, sizeof(keep_alive)))) {
perror("setsockopt-SO_KEEPALIVE\n");
}
if (-1 == (ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keep_idle, sizeof(keep_idle)))) {
perror("setsockopt-TCP_KEEPIDLE\n");
}
if (-1 == (ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &keep_interval, sizeof(keep_interval)))) {
perror("setsockopt-TCP_KEEPINTVL\n");
}
if (-1 == (ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keep_count, sizeof(keep_count)))) {
perror("setsockopt-TCP_KEEPCNT\n");
}
}
void TcpClient::setNoblock(int fd)
{
int curr_flags = fcntl(fd, F_GETFL);
if (curr_flags < 0) {
perror("fcntl-F_GETFL\n");
}
else if (fcntl(fd, F_SETFL, curr_flags|O_NONBLOCK) != 0) {
perror("fcntl-F_SETFL O_NONBLOCK\n");
}
}
int TcpClient::connectToHost(std::string ip, uint16_t port)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
return sock;
}
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(ip.c_str());
serv_addr.sin_port = htons(port);
if(0 == connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))){
setKeepAlive(sock);
setNoblock(sock);
m_mutex.lock();
m_clientSet.insert(sock);
m_clientTimeout[sock]=std::chrono::steady_clock::now();
m_mutex.unlock();
}else{
perror("connectToHost error\n");
::close(sock);
sock = -1;
}
return sock;
}
void TcpClient::disConnectToHost(int fd)
{
auto it = m_clientSet.find(fd);
if(m_clientSet.end() != it){
m_clientSet.erase(it);
}
auto it1 = m_clientTimeout.find(fd);
if(m_clientTimeout.end() != it1){
m_clientTimeout.erase(it1);
}
::close(fd);
}
void TcpClient::disConnectToHost()
{
for(auto fd:m_clientSet){
::close(fd);
}
m_clientSet.clear();
}
}
测试
#include <iostream>
#include "tcpclient.h"
#include <thread>
using namespace std;
int main()
{
cout << "Hello World!" << endl;
PRONAME::TcpClient tcp;
std::thread([](PRONAME::TcpClient* tcp){tcp->run();},&tcp).detach();
for(int i = 0;i< 5;i++){
std::cout<<"write: "<< tcp.write("hello",5,"192.168.2.101",5123);
}
std::cout<<"write: \n"<< tcp.write("hello",5,"192.168.2.101",5123);
getchar();
return 0;
}