base_socket_connector.h文件,可以不用改动:
#ifndef _BASE_SOCKET_CONNECTOR_H_
#define _BASE_SOCKET_CONNECTOR_H_
#include <iostream>
#include "Poco/Net/Net.h"
#include "Poco/Net/SocketNotification.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Observer.h"
typedef void (*CliSendCbFunc)(void *send_args, unsigned char *send_data, int max_send_len, int &send_len);
typedef void (*CliRecvCbFunc)(void *recv_args, unsigned char *recv_data, int &recv_len);
typedef struct TcpClientCallbackData
{
CliSendCbFunc send_loaddata; // 装载待发送数据,send_data是要发送的数据,send_len是要发送的长度
CliRecvCbFunc recv_callback; // 收到数据后的回调函数,recv_data是接收到的数据,recv_len是接收的数据长度
unsigned char *send_buf; // 使用者提供的发送数据缓存空间,调用send_loaddata函数后,先将待发送数据拷贝到这里
unsigned char *recv_buf; // 使用者提供的接收数据缓存空间,当系统有可接收数据时,先接收到这里,再调用recv_callback,传递给使用者
int send_len; // 实际发送数据的长度
int max_send_len; // send_buf的最大长度
int max_recv_len; // recv_buf的最大长度
void *send_args; // 存放使用者自己的私有数据
void *recv_args; // 存放使用者自己的私有数据
} TcpClientCallbackData;
template <class TcpClientHandle>
class BaseSocketConnector
{
public:
explicit BaseSocketConnector(Poco::Net::SocketAddress &address) : socket_reactor_(0)
{
stream_socket_.connectNB(address);
}
BaseSocketConnector(Poco::Net::SocketAddress &address,
Poco::Net::SocketReactor &reactor,
TcpClientCallbackData &tcp_client_callback) : socket_reactor_(0)
{
stream_socket_.connectNB(address);
RegisterConnector(reactor);
tcp_client_callback_ = tcp_client_callback;
}
virtual ~BaseSocketConnector()
{
try
{
UnregisterConnector();
}
catch (...)
{
poco_unexpected();
}
}
virtual void RegisterConnector(Poco::Net::SocketReactor &reactor)
{
socket_reactor_ = &reactor;
socket_reactor_->addEventHandler(stream_socket_, Poco::Observer<BaseSocketConnector, Poco::Net::ReadableNotification>(*this, &BaseSocketConnector::OnReadable));
socket_reactor_->addEventHandler(stream_socket_, Poco::Observer<BaseSocketConnector, Poco::Net::WritableNotification>(*this, &BaseSocketConnector::OnWritable));
socket_reactor_->addEventHandler(stream_socket_, Poco::Observer<BaseSocketConnector, Poco::Net::TimeoutNotification>(*this, &BaseSocketConnector::OnTimeout));
socket_reactor_->addEventHandler(stream_socket_, Poco::Observer<BaseSocketConnector, Poco::Net::ErrorNotification>(*this, &BaseSocketConnector::OnError));
}
virtual void UnregisterConnector()
{
if (socket_reactor_)
{
socket_reactor_->removeEventHandler(stream_socket_, Poco::Observer<BaseSocketConnector, Poco::Net::ReadableNotification>(*this, &BaseSocketConnector::OnReadable));
socket_reactor_->removeEventHandler(stream_socket_, Poco::Observer<BaseSocketConnector, Poco::Net::WritableNotification>(*this, &BaseSocketConnector::OnWritable));
socket_reactor_->removeEventHandler(stream_socket_, Poco::Observer<BaseSocketConnector, Poco::Net::TimeoutNotification>(*this, &BaseSocketConnector::OnTimeout));
socket_reactor_->removeEventHandler(stream_socket_, Poco::Observer<BaseSocketConnector, Poco::Net::ErrorNotification>(*this, &BaseSocketConnector::OnError));
}
}
void OnReadable(Poco::Net::ReadableNotification *readable_notification)
{
readable_notification->release();
int err = stream_socket_.impl()->socketError();
if (err)
{
OnError(err);
UnregisterConnector();
}
else
{
OnConnect();
}
}
void OnWritable(Poco::Net::WritableNotification *writable_notification)
{
writable_notification->release();
OnConnect();
}
void OnConnect()
{
stream_socket_.setBlocking(true);
NewTcpClientHandle();
UnregisterConnector();
}
void OnError(Poco::Net::ErrorNotification *error_notification)
{
error_notification->release();
OnError(stream_socket_.impl()->socketError());
}
void OnTimeout(Poco::Net::TimeoutNotification *timeout_notification)
{
timeout_notification->release();
OnError(stream_socket_.impl()->socketError());
}
//
void GetSendLen(int &send_len)
{
send_len = tcp_client_callback_.send_len;
}
protected:
virtual TcpClientHandle *NewTcpClientHandle()
{
return new TcpClientHandle(stream_socket_, socket_reactor_, tcp_client_callback_);
}
virtual void OnError(int errorCode)
{
socket_reactor_->stop();
stream_socket_.close();
}
Poco::Net::SocketReactor *GetReactor()
{
return socket_reactor_;
}
Poco::Net::StreamSocket &GetSocket()
{
return stream_socket_;
}
private:
BaseSocketConnector();
BaseSocketConnector(const BaseSocketConnector &);
BaseSocketConnector &operator=(const BaseSocketConnector &);
Poco::Net::StreamSocket stream_socket_;
Poco::Net::SocketReactor *socket_reactor_;
//
TcpClientCallbackData tcp_client_callback_;
};
#endif // _BASE_SOCKET_CONNECTOR_H_
base_tcp_client.h,根据应用,可以进行修改:
#ifndef _BASE_TCP_CLIENT_H_
#define _BASE_TCP_CLIENT_H_
#include <iostream>
#include "base_socket_connector.h"
#include "Poco/Net/Net.h"
#include "Poco/Net/SocketNotification.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Observer.h"
class BaseTcpClient
{
public:
BaseTcpClient(Poco::Net::StreamSocket &socket,
Poco::Net::SocketReactor *reactor,
TcpClientCallbackData &tcp_client_callback) : socket_(socket),
reactor_(reactor),
on_readable_(*this, &BaseTcpClient::OnReadable),
on_writable_(*this, &BaseTcpClient::OnWritable),
on_timeout_(*this, &BaseTcpClient::OnTimeout),
timeout_(false),
close_on_timeout_(false),
tcp_client_callback_(0)
{
tcp_client_callback_ = &tcp_client_callback;
reactor_->addEventHandler(socket_, on_readable_);
reactor_->addEventHandler(socket_, on_writable_);
reactor_->addEventHandler(socket_, on_timeout_);
}
~BaseTcpClient()
{
reactor_->removeEventHandler(socket_, Poco::Observer<BaseTcpClient, Poco::Net::ReadableNotification>(*this, &BaseTcpClient::OnReadable));
reactor_->removeEventHandler(socket_, Poco::Observer<BaseTcpClient, Poco::Net::WritableNotification>(*this, &BaseTcpClient::OnWritable));
reactor_->removeEventHandler(socket_, Poco::Observer<BaseTcpClient, Poco::Net::TimeoutNotification>(*this, &BaseTcpClient::OnTimeout));
}
void OnReadable(Poco::Net::ReadableNotification *readable_notification)
{
int recv_len = 0;
readable_notification->release();
if (NULL == tcp_client_callback_)
{
return;
}
recv_len = socket_.receiveBytes(tcp_client_callback_->recv_buf, tcp_client_callback_->max_recv_len);
if (recv_len > 0)
{
// 使用者的回调函数,由使用者处理接收到的数据
tcp_client_callback_->recv_callback(tcp_client_callback_->recv_args, tcp_client_callback_->recv_buf, recv_len);
}
else
{
std::cout << "BaseTcpClient - Read Error." << std::endl;
reactor_->stop();
socket_.close();
delete this;
}
}
void OnWritable(Poco::Net::WritableNotification *writable_notification)
{
writable_notification->release();
if (NULL == tcp_client_callback_)
{
return;
}
// 回调获取使用者发送的数据
tcp_client_callback_->send_len = -1;
tcp_client_callback_->send_loaddata(tcp_client_callback_->send_args,
tcp_client_callback_->send_buf,
tcp_client_callback_->max_send_len,
tcp_client_callback_->send_len);
// 待发送数据长度大于0
if (tcp_client_callback_->send_len >= 0)
{
// 调用实际发送函数
tcp_client_callback_->send_len = socket_.sendBytes(tcp_client_callback_->send_buf, tcp_client_callback_->send_len);
}
if (tcp_client_callback_->send_len < 0)
{
std::cout << "BaseTcpClient - Wirte Error." << std::endl;
}
}
void OnTimeout(Poco::Net::TimeoutNotification *timeout_notification)
{
timeout_notification->release();
timeout_ = true;
if (close_on_timeout_)
{
reactor_->stop();
socket_.close();
delete this;
}
}
private:
Poco::Net::StreamSocket socket_;
Poco::Net::SocketReactor *reactor_;
Poco::Observer<BaseTcpClient, Poco::Net::ReadableNotification> on_readable_;
Poco::Observer<BaseTcpClient, Poco::Net::WritableNotification> on_writable_;
Poco::Observer<BaseTcpClient, Poco::Net::TimeoutNotification> on_timeout_;
bool timeout_;
bool close_on_timeout_;
private:
//
TcpClientCallbackData *tcp_client_callback_;
};
#endif // _BASE_TCP_CLIENT_H_
main.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <termios.h>
#include <pthread.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <uv.h>
#include <iostream>
#include <sstream>
#include "Poco/Net/SocketReactor.h"
#include "Poco/Net/SocketNotification.h"
#include "Poco/Net/SocketAcceptor.h"
#include "Poco/Net/ParallelSocketAcceptor.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/Observer.h"
#include "Poco/Exception.h"
#include "Poco/LocalDateTime.h"
#include "Poco/DateTime.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/DateTimeParser.h"
#include "Poco/Mutex.h"
#include "Poco/Thread.h"
#include "Poco/ThreadTarget.h"
#include "base_tcp_client.h"
/*****************************************************************************
*
* Macro definition
*
*****************************************************************************/
//
#define CCT_CAMERA_SERVER_IP ("127.0.0.1")
#define CCT_CAMERA_SERVER_PORT (8080)
/*****************************************************************************
*
* Structure/Class definition
*
*****************************************************************************/
#define MAX_BUF_SIZE 2048
#define MAX_ONSEND_INTERVAL 10 /// ms
/*****************************************************************************
*
* Data definition
*
*****************************************************************************/
//
static pthread_cond_t send_ready = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t send_lock = PTHREAD_MUTEX_INITIALIZER;
static uint8_t user_send_buf[2048];
static int user_send_len;
/*****************************************************************************
*
* Function prototype
*
*****************************************************************************/
//
/*****************************************************************************
*
* Function entity
*
*****************************************************************************/
//
void PrintTimeStamp(char *str) {
struct timeval tv;
gettimeofday(&tv, NULL);
printf("Time Stamp - %s, %ld:%ld\n\r", str, tv.tv_sec, tv.tv_usec);
}
static int StartThread(void *(*thread_entry)(void *), void *thread_para) {
int rtn;
pthread_attr_t thread_attr;
pthread_t thread_ID;
pthread_attr_init(&thread_attr);
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
rtn = pthread_create(&thread_ID, &thread_attr, thread_entry, thread_para);
return rtn;
}
void UserSendFuncLoadData_(void)
{
pthread_mutex_lock(&send_lock);
Poco::LocalDateTime now;
std::string time_str = Poco::DateTimeFormatter::format(now, Poco::DateTimeFormat::ISO8601_FRAC_FORMAT);
user_send_len = sprintf((char *)user_send_buf, "Hello, %s", time_str.c_str());
user_send_buf[user_send_len] = 0;
user_send_len ++;
pthread_cond_signal(&send_ready);
pthread_mutex_unlock(&send_lock);
}
void UserSendFuncLoadData(void *send_args, unsigned char *send_data, int max_send_len, int &send_len)
{
pthread_mutex_lock(&send_lock);
pthread_cond_wait(&send_ready, &send_lock);
strcpy((char *)send_data, (char *)user_send_buf);
send_len = sprintf((char *)send_data, "%s, %s\n\r", (char *)user_send_buf, (char *)send_args);
send_data[send_len ++] = 0;
//printf("%s - %s\n\r", __func__, send_data);
pthread_mutex_unlock(&send_lock);
}
void UserRecvFuncCallback(void *recv_args, unsigned char *recv_data, int &recv_len)
{
recv_data[recv_len] = '\0';
std::cout << "UserRecvFuncCallback - " << recv_data << "\n\r";
}
static void *SendDataThrd(void *para) {
while (1) {
UserSendFuncLoadData_();
sleep(1);
}
}
static void TcpClientTestThrd(char *str)
{
unsigned char recv_buf[MAX_BUF_SIZE];
unsigned char send_buf[MAX_BUF_SIZE];
TcpClientCallbackData tcp_client_callback_data;
tcp_client_callback_data.recv_args = NULL;
tcp_client_callback_data.send_args = (void *)str;
tcp_client_callback_data.recv_callback = UserRecvFuncCallback;
tcp_client_callback_data.send_loaddata = UserSendFuncLoadData;
tcp_client_callback_data.send_buf = send_buf;
tcp_client_callback_data.recv_buf = recv_buf;
tcp_client_callback_data.max_recv_len = MAX_BUF_SIZE;
tcp_client_callback_data.max_send_len = MAX_BUF_SIZE;
int server_port = CCT_CAMERA_SERVER_PORT;
std::string server_ip(CCT_CAMERA_SERVER_IP);
Poco::Net::SocketAddress socket_addr(server_ip.c_str(), server_port);
StartThread(SendDataThrd, NULL);
do
{
SocketReactor reactor;
BaseSocketConnector<BaseTcpClient> connector(socket_addr, reactor, tcp_client_callback_data);
//std::cout << "before run\n\r";
reactor.run();
//std::cout << "after run\n\r";
Poco::Thread::sleep(MAX_ONSEND_INTERVAL);
} while (1);
}
int main(int argc, char ** argv)
{
if (argc < 2) {
printf("%s some_info\n\r", argv[0]);
return 0;
}
TcpClientTestThrd(argv[1]);
return 0;
}
makefile:
export TOP_DIR = $(PWD)
# 包含参数配置文件
#include ./Makefile.param
# 目标文件名称、编译后文件位置
PWD := $(shell pwd)
INSTALLDIR = .
TARGET = tcp_client_test
DBGTARGET = $(INSTALLDIR)/$(TARGET)d
RLSTARGET = $(INSTALLDIR)/$(TARGET)
# 配置编译器
CC = $(CROSS_COMPILE)gcc
CXX = $(CROSS_COMPILE)g++
LD_CXX = $(CROSS_COMPILE)g++
STRIP = $(CROSS_COMPILE)strip
RM = rm
# 配置依赖头文件、库文件路径
INCPATH += -I. -I$(REL_INC) -I/usr/local/include
LIBPATH += -L. -L$(REL_LIB) -L/usr/local/lib
# 配置编译参数
FLAGS_C += $(MACRO_DEFINE) $(INCPATH) $(CFLAGS) -Wall
FLAGS_CXX += $(MACRO_DEFINE) $(INCPATH) $(CFLAGS) -Wall
LDFLAGS += $(CFLAGS) $(LIBPATH) $(LIBS_CFLAGS) -pie -fPIE
LIBS += -lPocoFoundation -lPocoNet -lpthread -lm -lc -lgcc -lgcc_s -ldl
DBG_FLAGS = -g -D__DEBUG
RLS_FLAGS = -O2 -fno-strict-aliasing
# 源文件包含
SRCS_C = $(wildcard *.c)
SRCS_CXX = $(wildcard *.cpp)
SRCS_H = $(wildcard *.h*)
# obj文件包含
OBJ_DBG_DIR = 0-obj/$(CROSS_COMPILE)dbg
OBJ_RLS_DIR = 0-obj/$(CROSS_COMPILE)rls
OBJS_C_DBG = $(addprefix $(OBJ_DBG_DIR)/,$(SRCS_C:%.c=%.o))
OBJS_CXX_DBG = $(addprefix $(OBJ_DBG_DIR)/,$(SRCS_CXX:%.cpp=%.o))
OBJS_C_RLS = $(addprefix $(OBJ_RLS_DIR)/,$(SRCS_C:%.c=%.o))
OBJS_CXX_RLS = $(addprefix $(OBJ_RLS_DIR)/,$(SRCS_CXX:%.cpp=%.o))
# 编译动作
COMPILE_C = $(CC) $(FLAGS_C) -c
COMPILE_CXX = $(CXX) $(FLAGS_CXX) -c
LINK_CXX = $(LD_CXX) $(LDFLAGS)
.PHONY: clean debug release install
all: release
install:
install -d $(EXEC_DIR)
install $(RLSTARGET) $(EXEC_DIR)
install -m 444 $(TARGET).txt $(EXEC_DIR)
release: $(RLSTARGET)
$(STRIP) $(RLSTARGET)
debug: $(DBGTARGET)
@echo $(TARGET)_debug_done
$(RLSTARGET): $(OBJS_C_RLS) $(OBJS_CXX_RLS)
@mkdir -p $(INSTALLDIR)
$(LINK_CXX) -o $@ $^ $(LIBS)
$(DBGTARGET): $(OBJS_C_DBG) $(OBJS_CXX_DBG)
@mkdir -p $(INSTALLDIR)
$(LINK_CXX) -o $@ $^ $(LIBS)
$(OBJS_CXX_RLS): $(OBJ_RLS_DIR)/%.o: %.cpp $(SRCS_H)
@mkdir -p $(OBJ_RLS_DIR)
$(COMPILE_CXX) $(RLS_FLAGS) -o $@ $<
$(OBJS_C_RLS): $(OBJ_RLS_DIR)/%.o: %.c $(SRCS_H)
@mkdir -p $(OBJ_RLS_DIR)
$(COMPILE_C) $(RLS_FLAGS) -o $@ $<
$(OBJS_CXX_DBG): $(OBJ_DBG_DIR)/%.o: %.cpp $(SRCS_H)
@mkdir -p $(OBJ_DBG_DIR)
$(COMPILE_CXX) $(DBG_FLAGS) -o $@ $<
$(OBJS_C_DBG): $(OBJ_DBG_DIR)/%.o: %.c $(SRCS_H)
@mkdir -p $(OBJ_DBG_DIR)
$(COMPILE_C) $(DBG_FLAGS) -o $@ $<
clean:
-$(RM) -rf *~ *.d .dep 0-obj