From 1616119c571ab949a4089d016a1bfca5bcb5e94b Mon Sep 17 00:00:00 2001
From: i7-8700 <1281261856@qq.com>
Date: Mon, 28 Sep 2020 01:59:15 +0800
Subject: [PATCH] simple libevent clients

---
 jlib/net/simple_libevent_client.cpp           |  12 +-
 jlib/net/simple_libevent_client.h             |  29 +-
 jlib/net/simple_libevent_clients.cpp          | 450 ++++++++++++++++++
 jlib/net/simple_libevent_clients.h            | 109 +++++
 jlib/net/simple_libevent_micros.h             |   4 +-
 jlib/net/simple_libevent_server.cpp           |   4 +-
 jlib/net/simple_libevent_server.h             |   3 +
 .../simple_libevent_client.vcxproj            |  13 +-
 .../simple_libevent_clients.vcxproj           | 174 +++++++
 .../simple_libevent_clients.vcxproj.filters   |  27 ++
 .../simple_libevent_clients.vcxproj.user      |   4 +
 .../simple_libevent_server.vcxproj            |  10 +-
 test/test.sln                                 |  15 +
 13 files changed, 823 insertions(+), 31 deletions(-)
 create mode 100644 jlib/net/simple_libevent_clients.cpp
 create mode 100644 jlib/net/simple_libevent_clients.h
 create mode 100644 test/simple_libevent_clients/simple_libevent_clients.vcxproj
 create mode 100644 test/simple_libevent_clients/simple_libevent_clients.vcxproj.filters
 create mode 100644 test/simple_libevent_clients/simple_libevent_clients.vcxproj.user

diff --git a/jlib/net/simple_libevent_client.cpp b/jlib/net/simple_libevent_client.cpp
index 5d5f130..c0cd4a5 100644
--- a/jlib/net/simple_libevent_client.cpp
+++ b/jlib/net/simple_libevent_client.cpp
@@ -1,14 +1,5 @@
 #include "simple_libevent_client.h"
 
-#ifdef _WIN32
-#include <WinSock2.h>
-#pragma comment(lib, "ws2_32.lib")
-#else
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#endif
-
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
@@ -27,8 +18,10 @@
 #ifndef JLIB_DISABLE_LOG
 # ifdef SIMPLELIBEVENTCLIENTLIB
 #  include "../log2.h"
+#  include "simple_libevent_micros.h"
 # else
 #  include <jlib/log2.h>
+#  include <jlib/net/simple_libevent_micros.h>
 # endif
 #else // JLIB_DISABLE_LOG
 # ifdef SIMPLELIBEVENTCLIENTLIB
@@ -57,7 +50,6 @@ public:
 # endif
 #endif // JLIB_DISABLE_LOG
 
-#include "simple_libevent_micros.h"
 
 
 namespace jlib {
diff --git a/jlib/net/simple_libevent_client.h b/jlib/net/simple_libevent_client.h
index e3539e2..85dc09e 100644
--- a/jlib/net/simple_libevent_client.h
+++ b/jlib/net/simple_libevent_client.h
@@ -1,14 +1,25 @@
 #pragma once
 
-# ifndef  _CRT_SECURE_NO_WARNINGS
-#  define  _CRT_SECURE_NO_WARNINGS
-# endif
-# ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
-#  define _WINSOCK_DEPRECATED_NO_WARNINGS
-# endif
-
-#ifndef NOMINMAX
-#define NOMINMAX
+#ifndef _WIN32
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#else
+#  ifndef  _CRT_SECURE_NO_WARNINGS
+#    define  _CRT_SECURE_NO_WARNINGS
+#  endif
+#  ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
+#    define _WINSOCK_DEPRECATED_NO_WARNINGS
+#  endif
+#  ifndef NOMINMAX
+#    define NOMINMAX
+#  endif
+#  ifndef WIN32_LEAN_AND_MEAN
+#    define WIN32_LEAN_AND_MEAN
+#  endif
+#include <WinSock2.h>
+#pragma comment(lib, "ws2_32.lib")
 #endif
 
 #include <string>
diff --git a/jlib/net/simple_libevent_clients.cpp b/jlib/net/simple_libevent_clients.cpp
new file mode 100644
index 0000000..34052d9
--- /dev/null
+++ b/jlib/net/simple_libevent_clients.cpp
@@ -0,0 +1,450 @@
+#include "simple_libevent_clients.h"
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <event2/event.h>
+#include <event2/buffer.h>
+#include <event2/bufferevent.h>
+#include <event2/listener.h>
+#include <event2/thread.h>
+#include <thread>
+#include <mutex>
+#include <algorithm>
+#include <signal.h>
+#include <inttypes.h>
+
+#if defined(DISABLE_JLIB_LOG2) && !defined(JLIB_DISABLE_LOG)
+#define JLIB_DISABLE_LOG
+#endif
+
+#ifndef JLIB_DISABLE_LOG
+# ifdef SIMPLELIBEVENTCLIENTSLIB
+#  include "../log2.h"
+#  include "simple_libevent_micros.h"
+# else
+#  include <jlib/log2.h>
+#  include <jlib/net/simple_libevent_micros.h>
+# endif
+#else // JLIB_DISABLE_LOG
+#define init_logger(...)
+#define JLOG_DBUG(...)
+#define JLOG_INFO(...)
+#define JLOG_WARN(...)
+#define JLOG_ERRO(...)
+#define JLOG_CRTC(...)
+#define JLOG_ALL(...)
+
+class range_log {
+public:
+	range_log() {}
+	range_log(const char*) {}
+};
+
+#define AUTO_LOG_FUNCTION
+
+#define dump_hex(...)
+#define dump_asc(...)
+#define JLOG_HEX(...)
+#define JLOG_ASC(...)
+#endif // JLIB_DISABLE_LOG
+
+
+namespace jlib {
+namespace net {
+
+struct simple_libevent_clients::BaseClient::PrivateData {
+	int thread_id = 0;
+	int client_id = 0;
+	int fd = 0;
+	bufferevent* bev = nullptr;
+	event* timer = nullptr;
+	int timeout = 5;
+	OnTimerCallback on_timer = nullptr;
+	void* user_data = nullptr;
+	int lifetime = -1;
+	event* lifetimer = nullptr;
+	std::string server_ip{};
+	uint16_t server_port = 0;
+	bool auto_reconnect = false;
+	std::chrono::steady_clock::time_point lastTimeComm = {};
+};
+
+
+simple_libevent_clients::BaseClient::BaseClient()
+	: privateData(new PrivateData())
+{
+}
+
+simple_libevent_clients::BaseClient::~BaseClient()
+{
+	delete privateData;
+}
+
+simple_libevent_clients::BaseClient* simple_libevent_clients::BaseClient::createDefaultClient()
+{
+	return new BaseClient();
+}
+
+int simple_libevent_clients::BaseClient::thread_id() const
+{
+	return privateData->thread_id;
+}
+
+int simple_libevent_clients::BaseClient::client_id() const
+{
+	return privateData->client_id;
+}
+
+int simple_libevent_clients::BaseClient::fd() const
+{
+	return privateData->fd;
+}
+
+bool simple_libevent_clients::BaseClient::auto_reconnect() const
+{
+	return privateData->auto_reconnect;
+}
+
+std::string simple_libevent_clients::BaseClient::server_ip() const
+{
+	return privateData->server_ip;
+}
+
+int16_t simple_libevent_clients::BaseClient::server_port() const
+{
+	return privateData->server_port;
+}
+
+int simple_libevent_clients::BaseClient::lifetime() const
+{
+	return privateData->lifetime;
+}
+
+void simple_libevent_clients::BaseClient::send(const void* data, size_t len)
+{
+	if (!privateData->bev) {
+		JLOG_CRTC("BaseClient::send bev is nullptr, #{}", fd());
+		return;
+	}
+
+	auto output = bufferevent_get_output(privateData->bev);
+	if (!output) {
+		JLOG_INFO("BaseClient::send bev output nullptr, #{}", fd());
+		return;
+	}
+
+	evbuffer_lock(output);
+	evbuffer_add(output, data, len);
+	evbuffer_unlock(output);
+}
+
+void simple_libevent_clients::BaseClient::shutdown(int what)
+{
+	if (fd() != 0) {
+		::shutdown(fd(), what);
+		//fd = 0;
+	}
+}
+
+void simple_libevent_clients::BaseClient::updateLastTimeComm()
+{
+	privateData->lastTimeComm = std::chrono::steady_clock::now();
+}
+
+void simple_libevent_clients::BaseClient::set_auto_reconnect(bool b)
+{
+	privateData->auto_reconnect = b;
+}
+
+struct simple_libevent_clients::PrivateImpl
+{
+	struct WorkerThreadContext {
+		simple_libevent_clients* ctx = nullptr;
+		int thread_id = 0;
+		std::string name{};
+		event_base* base = nullptr;
+		std::mutex mutex{};
+		// fd => client*
+		std::unordered_map<int, simple_libevent_clients::BaseClient*> clients{};
+		int clients_to_connect = 0;
+		int client_id_to_connect = 0;
+
+		static void dummy_timercb_avoid_worker_exit(evutil_socket_t, short, void*)
+		{}
+
+		explicit WorkerThreadContext(simple_libevent_clients* ctx, int thread_id, const std::string& name = {})
+			: ctx(ctx)
+			, thread_id(thread_id)
+			, name(name)
+		{
+			base = event_base_new();
+		}
+
+		void worker() {
+			JLOG_INFO("{} WorkerThread #{} started", name, thread_id);
+			timeval tv = { 1, 0 };
+			event_add(event_new(base, -1, EV_PERSIST, dummy_timercb_avoid_worker_exit, nullptr), &tv);
+			event_base_dispatch(base);
+			JLOG_INFO("{} WorkerThread #{} exited", name.data(), thread_id);
+		}
+
+		bool connect(const std::string& ip, uint16_t port, std::string& msg) {
+			std::lock_guard<std::mutex> lg(mutex);
+			auto bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
+			if (!bev) {
+				msg = ("allocate bufferevent failed");
+				return false;
+			}
+			auto client = ctx->newClient_();
+			client->privateData->bev = bev;
+			client->privateData->thread_id = thread_id;
+			client->privateData->client_id = client_id_to_connect++;
+
+			bufferevent_setcb(bev, readcb, writecb, eventcb, this);
+			bufferevent_enable(bev, EV_READ | EV_WRITE);
+
+			sockaddr_in addr = { 0 };
+			addr.sin_family = AF_INET;
+			addr.sin_addr.s_addr = inet_addr(ip.data());
+			addr.sin_port = htons(port);
+			if (bufferevent_socket_connect(bev, (const sockaddr*)(&addr), sizeof(addr)) < 0) {
+				client->privateData->fd = (int)bufferevent_getfd(bev);
+				int err = evutil_socket_geterror(client->privateData->fd);
+				msg = "error starting connection: " + std::to_string(err) + evutil_socket_error_to_string(err);
+				delete client;
+				return false;
+			}
+			client->privateData->fd = (int)bufferevent_getfd(bev);
+			clients[client->privateData->fd] = client;
+			return true;
+		}
+
+		static void readcb(struct bufferevent* bev, void* user_data)
+		{
+			char buff[4096];
+			auto input = bufferevent_get_input(bev);
+			WorkerThreadContext* context = (WorkerThreadContext*)user_data;
+			if (context->ctx->onMsg_) {
+				int fd = (int)bufferevent_getfd(bev);
+				simple_libevent_clients::BaseClient* client = nullptr;
+				{
+					std::lock_guard<std::mutex> lg(context->mutex);
+					auto iter = context->clients.find(fd);
+					if (iter != context->clients.end()) {
+						client = iter->second;
+					}
+				}
+				if (client) {
+					while (1) {
+						int len = (int)evbuffer_copyout(input, buff, std::min(sizeof(buff), evbuffer_get_length(input)));
+						if (len > 0) {
+							size_t ate = context->ctx->onMsg_(buff, len, client, context->ctx->userData_);
+							if (ate > 0) {
+								evbuffer_drain(input, ate);
+								continue;
+							}
+						}
+						break;
+					}
+				} else {
+					bufferevent_free(bev);
+				}
+			} else {
+				evbuffer_drain(input, evbuffer_get_length(input));
+			}
+		}
+
+		static void writecb(struct bufferevent* bev, void* user_data)
+		{
+			WorkerThreadContext* context = (WorkerThreadContext*)user_data;
+			simple_libevent_clients::BaseClient* client = nullptr;
+			{
+				std::lock_guard<std::mutex> lg(context->mutex);
+				int fd = (int)bufferevent_getfd(bev);
+				auto iter = context->clients.find(fd);
+				if (iter != context->clients.end()) {
+					client = iter->second;
+				}
+			}
+			if (client && context->ctx->onWrite_) {
+				context->ctx->onWrite_(client, context->ctx->userData_);
+			}
+		}
+
+		static void eventcb(struct bufferevent* bev, short events, void* user_data)
+		{
+			WorkerThreadContext* context = (WorkerThreadContext*)user_data;
+			JLOG_DBUG("eventcb events={} {}", events, eventToString(events));
+
+			bool up = false;
+			std::string msg;
+			int fd = (int)bufferevent_getfd(bev);
+
+			if (events & BEV_EVENT_CONNECTED) {
+				up = true;
+			} else if (events & (BEV_EVENT_EOF)) {
+				msg = ("Connection closed");
+			} else if (events & BEV_EVENT_ERROR) {
+				msg = ("Got an error on the connection: ");
+				msg += strerror(errno);
+			}
+			
+			BaseClient* client = nullptr;
+			{
+				std::lock_guard<std::mutex> lg(context->mutex);
+				auto iter = context->clients.find(fd);
+				if (iter != context->clients.end()) {
+					client = iter->second;
+				} else {
+					JLOG_CRTC("eventcb cannot find client by fd #{}", (int)fd);
+				}
+			}
+
+			if (client) {				
+				if (context->ctx->onConn_) {
+					context->ctx->onConn_(up, msg, client, context->ctx->userData_);
+				}
+				if (!up) {
+					if (client->privateData->timer) {
+						event_del(client->privateData->timer);
+						client->privateData->timer = nullptr;
+					}
+					std::lock_guard<std::mutex> lg(context->mutex);
+					context->clients.erase(fd);
+					delete client;
+				}
+			}
+
+			if (!up) {
+				bufferevent_free(bev);
+			}
+		}
+	};
+	typedef WorkerThreadContext* WorkerThreadContextPtr;
+	std::vector<WorkerThreadContextPtr> contexts{};
+	std::vector<std::thread> threads{};
+	simple_libevent_clients* ctx = nullptr;
+
+	PrivateImpl(simple_libevent_clients* ctx, int threadNum, const std::string& name = {})
+		: ctx(ctx)
+	{
+		for (int i = 0; i < threadNum; i++) {
+			auto context = new WorkerThreadContext(ctx, i, name);
+			contexts.push_back(context);
+			threads.emplace_back(std::thread(&WorkerThreadContext::worker, context));
+		}
+	}
+
+	~PrivateImpl() {
+		for (auto context : contexts) {
+			timeval tv{ 0, 1000 };
+			event_base_loopexit(context->base, &tv);			
+		}
+		for (auto& t : threads) {
+			t.join();
+		}
+		threads.clear();
+		for (auto context : contexts) {
+			event_base_free(context->base);
+			context->clients.clear();
+			delete context;
+		}
+		contexts.clear();
+	}
+
+	static void timercb(evutil_socket_t fd, short, void* user_data)
+	{
+		auto client = (BaseClient*)user_data;
+		if (client->privateData->on_timer) {
+			client->privateData->on_timer(client, client->privateData->user_data);
+		}
+		client->privateData->timer = event_new(bufferevent_get_base(client->privateData->bev), client->fd(), 0, timercb, client);
+		struct timeval tv = { client->privateData->timeout, 0 };
+		event_add(client->privateData->timer, &tv);
+	}
+
+	static void lifetimer_cb(evutil_socket_t fd, short, void* user_data)
+	{
+		auto client = (BaseClient*)user_data;
+		client->shutdown();
+	}
+
+};
+
+simple_libevent_clients::simple_libevent_clients(OnConnectinoCallback onConn, OnMessageCallback onMsg, OnWriteCompleteCallback onWrite,
+												 NewClientCallback newClient,  int threads, void* user_data, const std::string& name)
+	: onConn_(onConn)
+	, onMsg_(onMsg)
+	, onWrite_(onWrite)
+	, newClient_(newClient)
+	, threadNum_(threads >= 1 ? threads : 1)
+	, userData_(user_data)
+	, name_(name)
+
+{
+	SIMPLE_LIBEVENT_ONE_TIME_INITTER;
+}
+
+simple_libevent_clients::~simple_libevent_clients()
+{
+	exit();
+}
+
+bool simple_libevent_clients::connect(const std::string& ip, uint16_t port, std::string& msg)
+{
+	std::lock_guard<std::mutex> lg(mutex_);
+	if (!impl) {
+		impl = new PrivateImpl(this, threadNum_, name_);
+	}
+	curThreadId_ = ++curThreadId_ % threadNum_;
+	return impl->contexts[curThreadId_]->connect(ip, port, msg);
+}
+
+void simple_libevent_clients::exit()
+{
+	std::lock_guard<std::mutex> lg(mutex_);
+	if (!impl) { return; }
+
+	delete impl;
+	impl = nullptr;
+}
+
+
+void simple_libevent_clients::BaseClient::set_timer(OnTimerCallback cb, void* user_data, int seconds)
+{
+	if (privateData->timer) {
+		event_del(privateData->timer);
+	}
+	privateData->on_timer = cb;
+	privateData->user_data = user_data;
+	privateData->timeout = seconds;
+	auto base = bufferevent_get_base(privateData->bev);
+	privateData->timer = event_new(base, fd(), 0, PrivateImpl::timercb, this);
+	if (!privateData->timer) {
+		JLOG_CRTC("create timer failed");
+		return;
+	}
+	struct timeval tv = { seconds, 0 };
+	event_add(privateData->timer, &tv);
+}
+
+void simple_libevent_clients::BaseClient::set_lifetime(int seconds)
+{
+	privateData->lifetime = seconds;
+	if (privateData->lifetimer) {
+		event_del(privateData->lifetimer);
+	}
+	auto base = bufferevent_get_base(privateData->bev);
+	privateData->lifetimer = event_new(base, fd(), 0, PrivateImpl::lifetimer_cb, this);
+	if (!privateData->lifetimer) {
+		JLOG_CRTC("create lifetimer failed");
+		return;
+	}
+	struct timeval tv = { seconds, 0 };
+	event_add(privateData->lifetimer, &tv);
+}
+
+}
+}
diff --git a/jlib/net/simple_libevent_clients.h b/jlib/net/simple_libevent_clients.h
new file mode 100644
index 0000000..24edd3a
--- /dev/null
+++ b/jlib/net/simple_libevent_clients.h
@@ -0,0 +1,109 @@
+#pragma once
+
+#ifndef _WIN32
+#  include <unistd.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#  include <sys/socket.h>
+#else
+#  ifndef  _CRT_SECURE_NO_WARNINGS
+#    define  _CRT_SECURE_NO_WARNINGS
+#  endif
+#  ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
+#    define _WINSOCK_DEPRECATED_NO_WARNINGS
+#  endif
+#  ifndef NOMINMAX
+#    define NOMINMAX
+#  endif
+#  ifndef WIN32_LEAN_AND_MEAN
+#    define WIN32_LEAN_AND_MEAN
+#  endif
+#  include <WinSock2.h>
+#  pragma comment(lib, "ws2_32.lib")
+#endif
+
+#include <stdint.h>
+#include <string>
+#include <mutex>
+#include <unordered_map>
+#include <chrono>
+#include <assert.h>
+
+namespace jlib {
+namespace net {
+
+class simple_libevent_clients
+{
+public:
+
+	struct BaseClient;
+
+	typedef void(*OnTimerCallback)(BaseClient* client, void* user_data);
+
+	typedef BaseClient* (*NewClientCallback)();
+
+	typedef void(*OnConnectinoCallback)(bool up, const std::string& msg, BaseClient* client, void* user_data);
+
+	// return > 0 for ate
+	// return 0 for stop
+	typedef size_t(*OnMessageCallback)(const char* data, size_t len, BaseClient* client, void* user_data);
+
+	typedef void(*OnWriteCompleteCallback)(BaseClient* client, void* user_data);
+
+
+	struct BaseClient {
+		explicit BaseClient();
+		virtual ~BaseClient();
+
+		static BaseClient* createDefaultClient();
+
+		int thread_id() const;
+		int client_id() const;
+		int fd() const;
+		bool auto_reconnect() const;
+		std::string server_ip() const;
+		int16_t server_port() const;
+		int lifetime() const;
+
+		void send(const void* data, size_t len);
+		void shutdown(int what = 1);
+		void updateLastTimeComm();
+		void set_auto_reconnect(bool b);
+		void set_timer(OnTimerCallback cb, void* user_data, int seconds);
+		void set_lifetime(int seconds);
+
+		struct PrivateData;
+		PrivateData* privateData = nullptr;
+	};
+
+public:
+	explicit simple_libevent_clients(OnConnectinoCallback onConn, OnMessageCallback onMsg, OnWriteCompleteCallback onWrite,
+									 NewClientCallback newClient, int threads, void* user_data, const std::string& name = {});
+	virtual ~simple_libevent_clients();
+
+	void setUserData(void* user_data) { userData_ = user_data; }
+
+	bool connect(const std::string& ip, uint16_t port, std::string& msg);
+	void exit();
+
+
+	
+
+protected:
+	struct PrivateImpl;
+	PrivateImpl* impl = nullptr;
+	std::string name_{};
+	void* userData_ = nullptr;
+	OnConnectinoCallback onConn_ = nullptr;
+	OnMessageCallback onMsg_ = nullptr;
+	OnWriteCompleteCallback onWrite_ = nullptr;
+	NewClientCallback newClient_ = BaseClient::createDefaultClient;
+
+	//! ¹¤×÷Ïß³ÌÊýÁ¿
+	int threadNum_ = 1;
+	int curThreadId_ = -1;
+	std::mutex mutex_{};
+};
+
+}
+}
diff --git a/jlib/net/simple_libevent_micros.h b/jlib/net/simple_libevent_micros.h
index e15f85d..4ee48c6 100644
--- a/jlib/net/simple_libevent_micros.h
+++ b/jlib/net/simple_libevent_micros.h
@@ -5,7 +5,7 @@
 	struct SimpleLibeventOneTimeInitHelper {																	
 		SimpleLibeventOneTimeInitHelper() {																		
 			WSADATA wsa_data;																					
-			WSAStartup(0x0201, &wsa_data);																		
+			WSAStartup(0x0202, &wsa_data);																		
 			if (0 != evthread_use_windows_threads()) {															
 				JLOG_CRTC("failed to init libevent with thread by calling evthread_use_windows_threads");		
 				abort();																						
@@ -41,7 +41,7 @@ namespace jlib {
 namespace net {
 inline std::string eventToString(short evs) {
 	std::string s;
-#define check_event_append_to_s(e) if(evs & e){s += #e " ";}
+#define check_event_append_to_s(e) if (evs & e) { s += #e " "; }
 	check_event_append_to_s(BEV_EVENT_READING);
 	check_event_append_to_s(BEV_EVENT_WRITING);
 	check_event_append_to_s(BEV_EVENT_EOF);
diff --git a/jlib/net/simple_libevent_server.cpp b/jlib/net/simple_libevent_server.cpp
index 92be4ce..7dfee2a 100644
--- a/jlib/net/simple_libevent_server.cpp
+++ b/jlib/net/simple_libevent_server.cpp
@@ -22,8 +22,10 @@
 #ifndef JLIB_DISABLE_LOG
 # ifdef SIMPLELIBEVENTSERVERLIB
 #  include "../log2.h"
+#  include "simple_libevent_micros.h"
 # else
 #  include <jlib/log2.h>
+#  include <jlib/net/simple_libevent_micros.h>
 # endif
 #else // JLIB_DISABLE_LOG
 # ifdef SIMPLELIBEVENTSERVERLIB
@@ -52,8 +54,6 @@ public:
 # endif
 #endif // JLIB_DISABLE_LOG
 
-#include "simple_libevent_micros.h"
-
 namespace jlib {
 namespace net {
 
diff --git a/jlib/net/simple_libevent_server.h b/jlib/net/simple_libevent_server.h
index 01c340a..6619526 100644
--- a/jlib/net/simple_libevent_server.h
+++ b/jlib/net/simple_libevent_server.h
@@ -9,6 +9,9 @@
 #  ifndef  _CRT_SECURE_NO_WARNINGS
 #    define  _CRT_SECURE_NO_WARNINGS
 #  endif
+#  ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
+#    define _WINSOCK_DEPRECATED_NO_WARNINGS
+#  endif
 #  ifndef NOMINMAX
 #    define NOMINMAX
 #  endif
diff --git a/test/simple_libevent_client/simple_libevent_client.vcxproj b/test/simple_libevent_client/simple_libevent_client.vcxproj
index d18e1ce..40cbf5c 100644
--- a/test/simple_libevent_client/simple_libevent_client.vcxproj
+++ b/test/simple_libevent_client/simple_libevent_client.vcxproj
@@ -142,10 +142,11 @@
     <ClCompile>
       <WarningLevel>Level3</WarningLevel>
       <SDLCheck>true</SDLCheck>
-      <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>_DEBUG;_LIB;SIMPLELIBEVENTCLIENTLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ConformanceMode>true</ConformanceMode>
-      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+      <AdditionalIncludeDirectories>$(DEVLIBS)\libevent-2.1.12-stable-install\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>
@@ -159,10 +160,11 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <SDLCheck>true</SDLCheck>
-      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>NDEBUG;_LIB;SIMPLELIBEVENTCLIENTLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ConformanceMode>true</ConformanceMode>
-      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+      <AdditionalIncludeDirectories>$(DEVLIBS)\libevent-2.1.12-stable-install\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>
@@ -171,6 +173,9 @@
       <OptimizeReferences>true</OptimizeReferences>
       <GenerateDebugInformation>true</GenerateDebugInformation>
     </Link>
+    <Lib>
+      <AdditionalDependencies>$(DEVLIBS)\libevent-2.1.12-stable-install-x64\lib\event_core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Lib>
   </ItemDefinitionGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
diff --git a/test/simple_libevent_clients/simple_libevent_clients.vcxproj b/test/simple_libevent_clients/simple_libevent_clients.vcxproj
new file mode 100644
index 0000000..6313f1f
--- /dev/null
+++ b/test/simple_libevent_clients/simple_libevent_clients.vcxproj
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\jlib\net\simple_libevent_clients.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\jlib\net\simple_libevent_clients.h" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <Keyword>Win32Proj</Keyword>
+    <ProjectGuid>{67920eb2-4d8b-4b74-9ec0-a62def03635b}</ProjectGuid>
+    <RootNamespace>simplelibeventclients</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>SIMPLELIBEVENTCLIENTSLIB;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+      <AdditionalIncludeDirectories>$(DEVLIBS)\libevent-2.1.12-stable-install\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>
+      </SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+    <Lib>
+      <AdditionalLibraryDirectories>$(DEVLIBS)\libevent-2.1.12-stable-install\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>event_core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Lib>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>SIMPLELIBEVENTCLIENTSLIB;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+      <AdditionalIncludeDirectories>$(DEVLIBS)\libevent-2.1.12-stable-install\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>
+      </SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+    <Lib>
+      <AdditionalLibraryDirectories>$(DEVLIBS)\libevent-2.1.12-stable-install\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>event_core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Lib>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>_DEBUG;_LIB;SIMPLELIBEVENTCLIENTSLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+      <AdditionalIncludeDirectories>$(DEVLIBS)\libevent-2.1.12-stable-install\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>
+      </SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>NDEBUG;_LIB;SIMPLELIBEVENTCLIENTSLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+      <AdditionalIncludeDirectories>$(DEVLIBS)\libevent-2.1.12-stable-install\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>
+      </SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/test/simple_libevent_clients/simple_libevent_clients.vcxproj.filters b/test/simple_libevent_clients/simple_libevent_clients.vcxproj.filters
new file mode 100644
index 0000000..0d15ee6
--- /dev/null
+++ b/test/simple_libevent_clients/simple_libevent_clients.vcxproj.filters
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\jlib\net\simple_libevent_clients.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\jlib\net\simple_libevent_clients.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/test/simple_libevent_clients/simple_libevent_clients.vcxproj.user b/test/simple_libevent_clients/simple_libevent_clients.vcxproj.user
new file mode 100644
index 0000000..88a5509
--- /dev/null
+++ b/test/simple_libevent_clients/simple_libevent_clients.vcxproj.user
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup />
+</Project>
\ No newline at end of file
diff --git a/test/simple_libevent_server/simple_libevent_server.vcxproj b/test/simple_libevent_server/simple_libevent_server.vcxproj
index 2d5029d..ae77753 100644
--- a/test/simple_libevent_server/simple_libevent_server.vcxproj
+++ b/test/simple_libevent_server/simple_libevent_server.vcxproj
@@ -139,10 +139,11 @@
     <ClCompile>
       <WarningLevel>Level3</WarningLevel>
       <SDLCheck>true</SDLCheck>
-      <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>_DEBUG;_LIB;SIMPLELIBEVENTSERVERLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ConformanceMode>true</ConformanceMode>
-      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+      <AdditionalIncludeDirectories>$(DEVLIBS)\libevent-2.1.12-stable-install\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>
@@ -156,10 +157,11 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <SDLCheck>true</SDLCheck>
-      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>NDEBUG;_LIB;SIMPLELIBEVENTSERVERLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ConformanceMode>true</ConformanceMode>
-      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+      <AdditionalIncludeDirectories>$(DEVLIBS)\libevent-2.1.12-stable-install\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>
diff --git a/test/test.sln b/test/test.sln
index cd9e6d6..8c3ed34 100644
--- a/test/test.sln
+++ b/test/test.sln
@@ -318,6 +318,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simple_libevent_server", "s
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_client_and_server", "test_client_and_server\test_client_and_server.vcxproj", "{A795FC0D-F05B-4B49-9957-E4A07A32427F}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simple_libevent_clients", "simple_libevent_clients\simple_libevent_clients.vcxproj", "{67920EB2-4D8B-4B74-9EC0-A62DEF03635B}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|ARM = Debug|ARM
@@ -890,6 +892,18 @@ Global
 		{A795FC0D-F05B-4B49-9957-E4A07A32427F}.Release|x64.Build.0 = Release|x64
 		{A795FC0D-F05B-4B49-9957-E4A07A32427F}.Release|x86.ActiveCfg = Release|Win32
 		{A795FC0D-F05B-4B49-9957-E4A07A32427F}.Release|x86.Build.0 = Release|Win32
+		{67920EB2-4D8B-4B74-9EC0-A62DEF03635B}.Debug|ARM.ActiveCfg = Debug|Win32
+		{67920EB2-4D8B-4B74-9EC0-A62DEF03635B}.Debug|ARM64.ActiveCfg = Debug|Win32
+		{67920EB2-4D8B-4B74-9EC0-A62DEF03635B}.Debug|x64.ActiveCfg = Debug|x64
+		{67920EB2-4D8B-4B74-9EC0-A62DEF03635B}.Debug|x64.Build.0 = Debug|x64
+		{67920EB2-4D8B-4B74-9EC0-A62DEF03635B}.Debug|x86.ActiveCfg = Debug|Win32
+		{67920EB2-4D8B-4B74-9EC0-A62DEF03635B}.Debug|x86.Build.0 = Debug|Win32
+		{67920EB2-4D8B-4B74-9EC0-A62DEF03635B}.Release|ARM.ActiveCfg = Release|Win32
+		{67920EB2-4D8B-4B74-9EC0-A62DEF03635B}.Release|ARM64.ActiveCfg = Release|Win32
+		{67920EB2-4D8B-4B74-9EC0-A62DEF03635B}.Release|x64.ActiveCfg = Release|x64
+		{67920EB2-4D8B-4B74-9EC0-A62DEF03635B}.Release|x64.Build.0 = Release|x64
+		{67920EB2-4D8B-4B74-9EC0-A62DEF03635B}.Release|x86.ActiveCfg = Release|Win32
+		{67920EB2-4D8B-4B74-9EC0-A62DEF03635B}.Release|x86.Build.0 = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -966,6 +980,7 @@ Global
 		{721A954E-B907-41C9-A30A-33E17F2449EE} = {729A65CE-3F07-4C2E-ACDC-F9EEC6477F2A}
 		{C5A52A4B-AC5C-48FA-8356-2194170E674C} = {729A65CE-3F07-4C2E-ACDC-F9EEC6477F2A}
 		{A795FC0D-F05B-4B49-9957-E4A07A32427F} = {77DBD16D-112C-448D-BA6A-CE566A9331FC}
+		{67920EB2-4D8B-4B74-9EC0-A62DEF03635B} = {729A65CE-3F07-4C2E-ACDC-F9EEC6477F2A}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {A8EBEA58-739C-4DED-99C0-239779F57D5D}
-- 
2.18.1