linux c++ 非阻塞tcp socket client简单实现

这篇具有很好参考价值的文章主要介绍了linux c++ 非阻塞tcp socket client简单实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

需求还需上报下数据,服务本身就是单进程线程安全的,不能用阻塞socket,通过getsockopt() TCP_INFO弄了个简单判断连接存活的方法,数据不是很重要,可靠性要求不高,也可以考虑udp. 可以在优化一下重连间隔周期,本样例,send 失败就重连频率过高。

src:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netdb.h> 
#include <queue>
#include <netinet/tcp.h>

using namespace std;

class TCPClient
{

enum class status: uint32_t {
	alive = 0,
	connecting = 1,
	disconnect = 2,
	unknown = 3
};

public:
	TCPClient();
	status get_tcp_status();
	bool connecting();
	bool setup(std::string address, int port);
	bool Send(char* data);
	string receive(int size = 4096);
	string read();
	void exit();

private:
	int sock;
	std::string address;
	int port;
	struct sockaddr_in server;
	status connection_status;
};

class QualityClient : public TCPClient {
public:
	QualityClient();
	~QualityClient();

	bool pub_quality(char* data);

private:
	std::queue<char*> send_buffer;
	int capacity;
	int size;
};

class QualityClientInstance
{
public:
    // 获取单实例对象
    static QualityClient *GetInstance();

    //释放单实例,进程退出时调用
    static void ReleaseInstance();

private:
    // 将其构造和析构成为私有的, 禁止外部构造和析构
    QualityClientInstance();
    ~QualityClientInstance();

    // 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值
    QualityClientInstance(const QualityClientInstance &signal);
    const QualityClientInstance &operator=(const QualityClientInstance &signal);

private:
    // 唯一单实例对象指针
    static QualityClient* instance;
    //static std::mutex m_Mutex;
};

TCPClient::TCPClient()
{
	sock = -1;
	port = 0;
	address = "";
}

bool TCPClient::setup(string address , int port)
{
  	if(sock == -1)
	{
		sock = socket(AF_INET , SOCK_STREAM | SOCK_NONBLOCK , IPPROTO_TCP);
		if (sock == -1)
		{
      		cout << "Could not create socket" << endl;
			return false;
    	}
    }

  	if((signed)inet_addr(address.c_str()) == -1)
  	{
		struct hostent *he;
		struct in_addr **addr_list;
		if ( (he = gethostbyname( address.c_str() ) ) == NULL)
		{
			herror("gethostbyname");
			cout<<"Failed to resolve hostname\n";
			return false;
		}
		addr_list = (struct in_addr **) he->h_addr_list;
		for(int i = 0; addr_list[i] != NULL; i++)
		{
			server.sin_addr = *addr_list[i];
			break;
		}
  	}
  	else
  	{
    	server.sin_addr.s_addr = inet_addr( address.c_str() );
  	}

  	server.sin_family = AF_INET;
  	server.sin_port = htons( port );

	return connecting();
}

bool TCPClient::connecting() {
	int ret = connect(sock, (struct sockaddr *)&server, sizeof(server));
	int savedErrno = ret == 0 ? 0 : errno;

    std::cout << "connect... : " << ret << ":" << savedErrno <<  std::endl;

	switch(savedErrno)
	{
		case 0:
		case EINPROGRESS:      //Operation now in progress
		case EINTR:            //Interrupted system call
		case EISCONN:          //Transport endpoint is already connected
		std::cout << "connecting..." << std::endl;
		break;

		case EAGAIN:
		case EADDRINUSE:
		case EADDRNOTAVAIL:
		case ECONNREFUSED:
		case ENETUNREACH:
		std::cout << "reSave Error. " << savedErrno;
		break;

		case EACCES:
		case EPERM:
		case EAFNOSUPPORT:
		case EALREADY:
		case EBADF:
		case EFAULT:
		case ENOTSOCK:
		std::cout << "connect error in Connector::startInLoop " << savedErrno;
		::close(sock);
		break;

		default:
		std::cout << "Unexpected error in Connector::startInLoop " << savedErrno;
		::close(sock);
		break;
	}


  	return false;
}


TCPClient::status TCPClient::get_tcp_status()
{

	struct tcp_info info;
	int len = sizeof(info);

	getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
	
	if(info.tcpi_state == TCP_ESTABLISHED) {
		return status::alive;
	} else if (info.tcpi_state == TCP_SYN_SENT || info.tcpi_state == TCP_SYN_RECV) {
		return status::connecting;
	} else {
		printf("connection error : info.tcpi_state = %d != %d\n",info.tcpi_state, TCP_ESTABLISHED);
		return status::disconnect;
	}
}

/*bool Setsocketkeepalive(int sockfd)
{
    int keepAlive=1;//开启keepalive属性
    int keepIdle=10;//如该连接在10秒内没有任何数据往来,则进行探测
    int keepInterval=2;//探测时发包的时间间隔为2秒
    int keepCount=3;//探测尝试的次数。如果第1次探测包就收到响应了,则后2次的不再发送

    if(setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,(void *)&keepAlive,sizeof(keepAlive))!=0)//若无错误发生,setsockopt()返回值为0
    {
		return false;
    }
    if(setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE,(void *)&keepIdle, sizeof(keepIdle))!=0)
    {
		return false;
    }
    if(setsockopt(sockfd,SOL_TCP, TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval))!=0)
    {
		return false;
    }
    if(setsockopt(sockfd,SOL_TCP, TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount))!=0)
    {
		return false;
    }
	return true;
}*/

bool TCPClient::Send(char* data)
{
	//check_tcp_alive(sock);
	auto stat = get_tcp_status();
	if( stat == status::alive)
	{
		int ret = send(sock , data , strlen(data) , 0);
		cout << "Send ret : " << ret << endl;
		if( ret < 0 )
		{
			cout << "Send failed : " << data << endl;
			return false;
		} else if (ret == 0) {
			cout << "connection closed : " << data << endl;
			abort();
		}
	} else if ( stat == status::connecting) {
		return false;
	} else {
		close(sock);
		close(sock);
		sock = -1;
		setup("192.168.6.45", 12358);
		return false;
	}

	return true;
}

string TCPClient::receive(int size)
{
  	char buffer[size];
	memset(&buffer[0], 0, sizeof(buffer));

  	string reply;
	if( recv(sock , buffer , size, 0) < 0)
  	{
	    	cout << "receive failed!" << endl;
		return nullptr;
  	}
	buffer[size-1]='\0';
  	reply = buffer;
  	return reply;
}

string TCPClient::read()
{
  	char buffer[1] = {};
  	string reply;
  	while (buffer[0] != '\n') {
    		if( recv(sock , buffer , sizeof(buffer) , 0) < 0)
    		{
      			cout << "receive failed!" << endl;
			return nullptr;
    		}
		reply += buffer[0];
	}
	return reply;
}

void TCPClient::exit()
{
    close( sock );
}

QualityClient::QualityClient() {
	capacity = 256;
	size = 0;
}

QualityClient::~QualityClient() {}

bool QualityClient::pub_quality(char* data) {
	bool success =  Send(data);
	if (!success) {
		if (send_buffer.size() == capacity) {
			send_buffer.pop();
		}
		send_buffer.push(data);
	}

	while (success && !send_buffer.empty())
	{
		success = Send(send_buffer.back());
		if (success) send_buffer.pop();
	}

}


//初始化静态成员变量
QualityClient *QualityClientInstance::instance = nullptr;
//std::mutex QualityClientInstance::m_Mutex;

// 注意:不能返回指针的引用,否则存在外部被修改的风险!
QualityClient * QualityClientInstance::GetInstance()
{

    //  这里使用了两个 if 判断语句的技术称为双检锁;好处是,只有判断指针为空的时候才加锁,
    //  避免每次调用 GetInstance的方法都加锁,锁的开销毕竟还是有点大的。
    //if (m_SingleInstance == nullptr) 
    //{
        //std::unique_lock<std::mutex> lock(m_Mutex); // 加锁
	if (instance == nullptr)
	{
		instance = new QualityClient();
	}
    //}

    return instance;
}

void QualityClientInstance::ReleaseInstance()
{
    //std::unique_lock<std::mutex> lock(m_Mutex); // 加锁
    if (instance)
    {
        delete instance;
        instance = nullptr;
    }
}


int main() {
    std::cout << "start" << std::endl;

    //TCPClient tcp;
	QualityClient* tcp = QualityClientInstance::GetInstance();
	tcp->setup("192.168.6.45", 12358);

	while(1)
	{
		tcp->pub_quality("haapy");

        sleep(1);
	}

	QualityClientInstance::ReleaseInstance();
}

测试服务:文章来源地址https://www.toymoban.com/news/detail-584001.html

package main

import(

"log"

"fmt"

"net"

//"time"

)

func connHandlerTcp(c net.Conn) {
    //1.conn是否有效
    if c == nil {
        log.Panic("无效的 socket 连接")
    }

    //2.新建网络数据流存储结构
    buf := make([]byte, 1024)
    //3.循环读取网络数据流
    for {
        //3.1 网络数据流写入 buffer
        cnt, err := c.Read(buf)
        //3.2 数据读尽、读取错误 关闭 socket 连接
        if cnt == 0 || err != nil {
			fmt.Println("connection close")
            c.Close()
            break
        }
		fmt.Println("get msg : ", cnt , ":", string(buf[0:cnt]))
    }
}

func main(){

//  tcp recv serv 

	listen, err := net.Listen("tcp", "0.0.0.0:12358")

	if err!=nil{
		panic(err)
		return
	}
	defer listen.Close() // 延时关闭listen
	
	fmt.Println("listening success:", listen.Addr())
	
	// 循环等待客户端来连接
	fmt.Println("等待客户端来连接..")
	for{
		conn, err := listen.Accept()
		if err!=nil{
			panic(err)
		}else{
			fmt.Printf("客户端%s已连接..", conn.RemoteAddr().String())
		}
		// 准备一个协程,为客户端服务
		go connHandlerTcp(conn)
	}
}

到了这里,关于linux c++ 非阻塞tcp socket client简单实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于C++和Qt封装一个简单的socket(TCP/IP)通信UI界面

            最近在学习TCP/IP和socket套接字的有关知识,了解了三次握手四次挥手,TCP协议等等一大堆知识,但纸上得来终觉浅。网络上C++代码实现socket通信的资料很多,方便学习,于是想到自己用Qt实现一个基础的具有网络通信收发功能的服务端UI软件。进入正题:        

    2024年02月08日
    浏览(41)
  • Linux网络编程:socket & fork()多进程 实现clients/server通信

    UNIX网络编程:socket实现client/server通信 随笔简单介绍了TCP Server服务单客户端的socket通信,但是并未涉及多客户端通信。 对于网络编程肯定涉及到多客户端通信和并发编程 (指在同时有大量的客户链接到同一服务器),故本随笔补充这部分知识。 而且并发并发编程涉及到多进程

    2024年02月05日
    浏览(35)
  • TCP Socket Client 测试工具,并发测试

    TCP协议 Socket客户端测试程序 发送数据支持ASCII与16进制格式 可以模拟并发压力测试 创建终端,然后发送数据   工具下载  

    2024年02月12日
    浏览(32)
  • Linux C++ 网络编程基础(2) : TCP多线程一个server对应多个client

    作者:令狐掌门 技术交流QQ群:675120140 csdn博客:https://mingshiqiang.blog.csdn.net/   tcp编程时, 一个server可以对应多个client, server端用多线程可以实现. linux下多线程可以使用POSIX的线程函数, 下面给出服务端和客户端的代码.   Linux POSIX线程库提供了一组函数来创建、管理和同步

    2024年02月13日
    浏览(37)
  • C/C++用socket实现简单的TCP文件传输

    进程通信的首要问题是如何唯一标识一个进程 本地可以通过进程PID来唯一标识一个进程 网络中ip可以唯一标识一台主机;“协议 + 端口”可以唯一标识主机中的进程 因此,通过三元组(ip地址,协议,端口)就可以唯一标识网络中的进程了 使用TCP/IP协议的应用程序通常采用

    2024年02月04日
    浏览(29)
  • Linux网络编程:socket & pthread_create()多线程 实现clients/server通信

    UNIX网络编程:socket fork()多进程 实现clients/server通信 随笔介绍了通过fork()多进程实现了服务器与多客户端通信。但除了多进程能实现之外,多线程也是一种实现方式。 重要的是,多进程和多线程是涉及操作系统层次。随笔不仅要利用pthread_create()实现多线程编程,也要理解线

    2024年02月05日
    浏览(34)
  • 【Unity】Socket网络通信(TCP) - 实现简单的多人聊天功能

    多客户端连接服务器其原理是在服务端保存客户端连入后与客户端通信的socket,由于等待客户端连接会阻塞主线程,所以结合多线程就能实现多客户端连入功能。多人聊天只需要将A客户端发来的消息,转发给除A客户端外的其他客户端,即可实现。如果你还不怎么熟悉服务端

    2024年02月03日
    浏览(51)
  • Socket编程接口API并实现简单的TCP网络编程

    #include sys/types.h #include sys/socket.h socket()创建套接字,成功返回套接字的文件描述符,失败返回-1 domain: 设置套接字的协议簇, AF_UNIX AF_INET AF_INET6 type: 设置套接字的服务类型 SOCK_STREAM SOCK_DGRAM protocol: 一般设置为 0,表示使用默认协议 int socket(int domain, int type, int protocol); bind()将

    2024年02月13日
    浏览(30)
  • Linux系统c语言socket实现TCP通信

    socket通信用到的函数 sockaddr_in结构体(在netinet/in.h中定义) 对于服务端 使用socket()创建套接字socket 使用bind()将创建的套接字绑定到一个本地地址和端口上 listen()将套接字设为监听模式,准备接收客户端请求 accept()接收连接请求,返回一个对应于此次连接的新的套接字 read()

    2024年02月05日
    浏览(33)
  • Linux socket网络编程实战(tcp)实现双方聊天

    在上节已经系统介绍了大致的流程和相关的API,这节就开始写代码! 回顾上节的流程: 创建一个NET文件夹 来存放网络编程相关的代码: 这部分先实现服务器的连接部分的代码并进行验证 server1.c: 代码验证: 先编译并运行这部分代码: 可见,此时没有客户端进行连接,程

    2024年02月03日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包