// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the Andrey N. Sabelnikov nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #ifndef _LEVIN_CP_SERVER_H_ #define _LEVIN_CP_SERVER_H_ #include #include #include #include #include #include "misc_log_ex.h" //#include "threads_helper.h" #include "syncobj.h" #define ENABLE_PROFILING #include "profile_tools.h" #include "net_utils_base.h" #include "pragma_comp_defs.h" #define LEVIN_DEFAULT_DATA_BUFF_SIZE 2000 namespace epee { namespace net_utils { template class cp_server_impl//: public abstract_handler { public: cp_server_impl(/*abstract_handler* phandler = NULL*/); virtual ~cp_server_impl(); bool init_server(int port_no); bool deinit_server(); bool run_server(int threads_count = 0); bool send_stop_signal(); bool is_stop_signal(); virtual bool on_net_idle(){return true;} size_t get_active_connections_num(); typename TProtocol::config_type& get_config_object(){return m_config;} private: enum overlapped_operation_type { op_type_recv, op_type_send, op_type_stop }; struct io_data_base { OVERLAPPED m_overlapped; WSABUF DataBuf; overlapped_operation_type m_op_type; DWORD TotalBuffBytes; volatile LONG m_is_in_use; char Buffer[1]; }; PRAGMA_WARNING_PUSH PRAGMA_WARNING_DISABLE_VS(4355) template struct connection: public net_utils::i_service_endpoint { connection(typename TProtocol::config_type& ref_config):m_sock(INVALID_SOCKET), m_tprotocol_handler(this, ref_config, context), m_psend_data(NULL), m_precv_data(NULL), m_asked_to_shutdown(0), m_connection_shutwoned(0) { } //connection():m_sock(INVALID_SOCKET), m_tprotocol_handler(this, m_dummy_config, context), m_psend_data(NULL), m_precv_data(NULL), m_asked_to_shutdown(0), m_connection_shutwoned(0) //{ //} connection& operator=(const connection& obj) { return *this; } bool init_buffers() { m_psend_data = (io_data_base*)new char[sizeof(io_data_base) + LEVIN_DEFAULT_DATA_BUFF_SIZE-1]; m_psend_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE; m_precv_data = (io_data_base*)new char[sizeof(io_data_base) + LEVIN_DEFAULT_DATA_BUFF_SIZE-1]; m_precv_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE; return true; } bool query_shutdown() { if(!::InterlockedCompareExchange(&m_asked_to_shutdown, 1, 0)) { m_psend_data->m_op_type = op_type_stop; ::PostQueuedCompletionStatus(m_completion_port, 0, (ULONG_PTR)this, &m_psend_data->m_overlapped); } return true; } //bool set_config(typename TProtocol::config_type& config) //{ // this->~connection(); // new(this) connection(config); // return true; //} ~connection() { if(m_psend_data) delete m_psend_data; if(m_precv_data) delete m_precv_data; } virtual bool handle_send(const void* ptr, size_t cb) { PROFILE_FUNC("[handle_send]"); if(m_psend_data->TotalBuffBytes < cb) resize_send_buff((DWORD)cb); ZeroMemory(&m_psend_data->m_overlapped, sizeof(OVERLAPPED)); m_psend_data->DataBuf.len = (u_long)cb;//m_psend_data->TotalBuffBytes; m_psend_data->DataBuf.buf = m_psend_data->Buffer; memcpy(m_psend_data->DataBuf.buf, ptr, cb); m_psend_data->m_op_type = op_type_send; InterlockedExchange(&m_psend_data->m_is_in_use, 1); DWORD bytes_sent = 0; DWORD flags = 0; int res = 0; { PROFILE_FUNC("[handle_send] ::WSASend"); res = ::WSASend(m_sock, &(m_psend_data->DataBuf), 1, &bytes_sent, flags, &(m_psend_data->m_overlapped), NULL); } if(res == SOCKET_ERROR ) { int err = ::WSAGetLastError(); if(WSA_IO_PENDING == err ) return true; } LOG_ERROR("BIG FAIL: WSASend error code not correct, res=" << res << " last_err=" << err); ::InterlockedExchange(&m_psend_data->m_is_in_use, 0); query_shutdown(); //closesocket(m_psend_data); return false; }else if(0 == res) { ::InterlockedExchange(&m_psend_data->m_is_in_use, 0); if(!bytes_sent || bytes_sent != cb) { int err = ::WSAGetLastError(); LOG_ERROR("BIG FAIL: WSASend immediatly complete? but bad results, res=" << res << " last_err=" << err); query_shutdown(); return false; }else { return true; } } return true; } bool resize_send_buff(DWORD new_size) { if(m_psend_data->TotalBuffBytes >= new_size) return true; delete m_psend_data; m_psend_data = (io_data_base*)new char[sizeof(io_data_base) + new_size-1]; m_psend_data->TotalBuffBytes = new_size; LOG_PRINT("Connection buffer resized up to " << new_size, LOG_LEVEL_3); return true; } SOCKET m_sock; net_utils::connection_context_base context; TProtocol m_tprotocol_handler; typename TProtocol::config_type m_dummy_config; io_data_base* m_precv_data; io_data_base* m_psend_data; HANDLE m_completion_port; volatile LONG m_asked_to_shutdown; volatile LONG m_connection_shutwoned; }; PRAGMA_WARNING_POP bool worker_thread_member(); static unsigned CALLBACK worker_thread(void* param); bool add_new_connection(SOCKET new_sock, long ip_from, int port_from); bool shutdown_connection(connection* pconn); typedef std::map > > connections_container; SOCKET m_listen_socket; HANDLE m_completion_port; connections_container m_connections; critical_section m_connections_lock; int m_port; volatile LONG m_stop; //abstract_handler* m_phandler; bool m_initialized; volatile LONG m_worker_thread_counter; typename TProtocol::config_type m_config; }; } } #include "abstract_tcp_server_cp.inl" #endif //_LEVIN_SERVER_H_