Ini adalah perpustakaan jaringan C++ NIO dengan kinerja dan kemudahan penggunaan. Ini mendukung C++14 ke atas dan mencakup tiga platform utama utama.
Model IO yang mendasari meminjam model one loop per thread dari Pustaka Jaringan Muduo, yang membuat enkapsulasi API yang aman-utas lebih efisien dan sederhana.
Antarmuka API yang disediakan oleh Lapisan Atas meminjam dari Netpoll Netpoll Netpoll Netpoll NetPoll, dan mengabstraksikan Listener dan Dialer untuk akhirnya menyediakan layanan melalui EventLoop .
Untuk bagaimana perpustakaan ini digunakan, Anda dapat memeriksa awal yang cepat.
Untuk contoh penggunaan tertentu, silakan lihat contoh.
Sudah didukung :
windows/linux/macos diimplementasikan menggunakan implementasi multiplexing kinerja tertinggi platform (EPOLL/EPOLL/KQueUe yang diimplementasikan oleh IOCP).EventLoop untuk mengimplementasikan panggilan asinkron. Jika sistem operasi mendukung panggilan sendfile , panggilan nol-copy akan dipanggil alih-alih menelepon perpustakaan standar C.echo , Anda hanya perlu kode berikut: struct server
{
NETPOLL_TCP_MESSAGE (conn, buffer){conn-> send (buffer-> readAll ());}
};
int main ()
{
auto loop = netpoll::NewEventLoop ();
auto listener = netpoll::tcp::Listener::New ({ 6666 });
listener. bind <server>();
loop. serve (listener);
}100^8s , yang mungkin hanya membutuhkan 100*8byte ). Untuk detailnya, Anda dapat memeriksa blog: Pendahuluan terperinci.one loop per thread dengan sangat baik.lockfree_queuefuture untuk sinkronisasi, yang mungkin berguna dalam pemrograman klien. netpoll::SignalTask::Register ({SIGINT,SIGTERM});
netpoll::SignalTask::Add ([](){
// do something
});Akan mendukungnya di masa depan :
Kinerja sangat tinggi, dan saya sementara menguji ASIO (C ++)/Netty (Java)/NetPoll (GO Language).
Saya menguji ASIO/NETTY pada sistem Windows, dan tes bagan berikut didasarkan pada Linux, dan saya tidak pandai menggunakan program Java di Linux, sehingga bagan berikut tidak memiliki kinerja Netty.
Latensi rata -rata satu layanan gema terhubung tunggal di bawah situasi konkurensi yang berbeda adalah sebagai berikut (rata -rata):
Arsitektur model yang mendasari sisi server:
Enkapsulasi Listener menyederhanakan penggunaan TcpServer , semua panggilan adalah sebagai berikut:
namespace netpoll ::tcp{
class Listener {
template < typename T>
void bind (Args &&...args); // 用于绑定任意类的对应方法到回调
template < typename T>
std::shared_ptr<T> instance () // 返回内部帮你创建的实例
static Listener New( const InetAddress &address,
const StringView &name = " tcp_listener " ,
bool reUseAddr = true , bool reUsePort = true ); // 建立Listener实例
void enableKickoffIdle ( size_t timeout); // 用于开启剔除空闲连接
}
} Enkapsulasi Dialer menyederhanakan penggunaan TcpClient , dan panggilan yang digunakan adalah sebagai berikut:
namespace netpoll ::tcp{
class Dialer {
template < typename T>
void bind (Args &&...args); // 用于绑定任意类的对应方法到回调
template < typename T>
std::shared_ptr<T> instance () // 返回内部帮你创建的实例
static Dialer New( const InetAddress &address,
const StringView &name = " tcp_dialer " ); // 建立Listener实例
void enableRetry (); // 在连接失败后重试
// 以下调用方均是为了在没有C++17的if constexpr情况下的替代品,否则应该直接使用bind
template < typename T, typename ... Args>
static std::shared_ptr<T> Register (Args &&...args);
void onMessage (netpoll::RecvMessageCallback const &cb);
void onConnection (netpoll::ConnectionCallback const &cb);
void onConnectionError (netpoll::ConnectionErrorCallback const &cb);
void onWriteComplete (netpoll::WriteCompleteCallback const &cb);
}
} Strategi penyeimbangan beban EventLoop tidak memiliki pengaturan terpisah, dan mereka semua menggunakan Round Robin .
API yang terkait dengan Eventloop adalah sebagai berikut:
NewEventLoop(size_t threadNum=2,const netpoll::StringView&name="eventloop") : Buat instance EventLoop dan atur jumlah utas untuk EventLoop.
Metode serve : Setelah Anda keluar dialer atau pendengar yang baru, Anda dapat memberikan layanan kepada mereka melalui metode ini.
Metode serveAsDaemon : Efeknya sama dengan metode serve, tetapi membuka utas baru untuk disajikan tidak akan memblokir utas saat ini.
Metode enableQuit : Memungkinkan panggilan aktif untuk loop metode keluar. Secara default, ia tidak dapat secara aktif keluar dari semua utas loop dan digunakan bersama dengan metode QuitAllEventLoop .
Metode QuitAllEventLoop : Keluar dari semua loop.
MessageBuffer adalah cache menengah untuk membaca dan menulis data penyangga kernel. Ini sebenarnya adalah buffer variabel, dan juga sangat sederhana untuk diimplementasikan. Untuk berbagai jenis implementasi buffer, Anda dapat memeriksa artikel saya: implementasi panjang variabel dan buffer panjang yang tidak dapat diubah
Saya tidak akan menjelaskan berbagai panggilan di sini. Jika Anda ingin tahu, Anda dapat langsung melihat file header yang sesuai: NetPoll/util/message_buffer.h.
Izinkan saya berbicara secara singkat tentang highlight mengimplementasikan buffer ini:
Saat berkembang, hindari efek samping dari mengubah ukuran, itu juga dapat menyederhanakan pembukaan memori dan operasi penyalinan.
void MessageBuffer::ensureWritableBytes ( size_t len)
{
if ( writableBytes () >= len) return ;
// move readable bytes
if (m_head + writableBytes () >= (len + kBufferOffset ))
{
std::copy ( begin () + m_head, begin () + m_tail, begin () + kBufferOffset );
m_tail = kBufferOffset + (m_tail - m_head);
m_head = kBufferOffset ;
return ;
}
// create new buffer
size_t newLen;
if ((m_buffer. size () * 2 ) > ( kBufferOffset + readableBytes () + len))
newLen = m_buffer. size () * 2 ;
else newLen = kBufferOffset + readableBytes () + len;
// Avoid the inefficiency of using resize
MessageBuffer newbuffer (newLen);
newbuffer. pushBack (* this );
swap (newbuffer);
}Berikan metode ReadFD, yang membaca data buffer baca FD yang sesuai ke MessageBuffer. Konten dibaca setiap kali cukup besar. Misalnya, jika area writable MessageBuffer kurang dari 8kb, maka cache baca alternatif 8kB diaktifkan.
ssize_t MessageBuffer::readFd ( int fd, int *retErrno)
{
char extBuffer[ 8192 ];
struct iovec vec[ 2 ];
size_t writable = writableBytes ();
vec[ 0 ]. iov_base = begin () + m_tail;
vec[ 0 ]. iov_len = static_cast < int >(writable);
vec[ 1 ]. iov_base = extBuffer;
vec[ 1 ]. iov_len = sizeof (extBuffer);
const int iovcnt = (writable < sizeof extBuffer) ? 2 : 1 ;
ssize_t n = :: readv (fd, vec, iovcnt);
if (n < 0 ) { *retErrno = errno; }
else if ( static_cast < size_t >(n) <= writable) { m_tail += n; }
else
{
m_tail = m_buffer. size ();
pushBack ({extBuffer, n - writable});
}
return n;
}Kelas TCPConnection adalah kelas abstrak yang digunakan melalui pointer pintar saat digunakan.
Antarmuka ini menentukan fungsi berikut:
Kirim Data (termasuk String/Buffer/File/Stream).
/* *
* @brief Send some data to the peer.
*
* @param msg
* @param len
*/
virtual void send (StringView const &msg) = 0;
virtual void send ( const MessageBuffer &buffer) = 0;
virtual void send (MessageBuffer &&buffer) = 0;
virtual void send ( const std::shared_ptr<MessageBuffer> &msgPtr) = 0;
/* *
* @brief Send a file to the peer.
*
* @param fileName in UTF-8
* @param offset
* @param length
*/
virtual void sendFile (StringView const &fileName, size_t offset = 0 ,
size_t length = 0 ) = 0;
/* *
* @brief Send a stream to the peer.
*
* @param callback function to retrieve the stream data (stream ends when a
* zero size is returned) the callback will be called with nullptr when the
* send is finished/interrupted, so that it cleans up any internal data (ex:
* close file).
* @warning The buffer size should be >= 10 to allow http chunked-encoding
* data stream
*/
// (buffer, buffer size) -> size
// of data put in buffer
virtual void sendStream (
std::function<std:: size_t ( char *, std:: size_t )> callback) = 0;Dapatkan informasi koneksi, seperti informasi alamat atau status koneksi atau buffer yang menerima data.
/* *
* @brief New the local address of the connection.
*
* @return const InetAddress&
*/
virtual const InetAddress & localAddr () const = 0;
/* *
* @brief New the remote address of the connection.
*
* @return const InetAddress&
*/
virtual const InetAddress & peerAddr () const = 0;
/* *
* @brief Return true if the connection is established.
*
* @return true
* @return false
*/
virtual bool connected () const = 0;
/* *
* @brief Return false if the connection is established.
*
* @return true
* @return false
*/
virtual bool disconnected () const = 0;
/* *
* @brief New the buffer in which the received data stored.
*
* @return MsgBuffer*
*/
virtual MessageBuffer * getRecvBuffer () = 0;Atur panggilan balik atau status koneksi (lepaskan atau atur tcpnodelay/keepalive).
/* *
* @brief Set the high water mark callback
*
* @param cb The callback is called when the data in sending buffer is
* larger than the water mark.
* @param markLen The water mark in bytes.
*/
virtual void setHighWaterMarkCallback ( const HighWaterMarkCallback &cb,
size_t markLen) = 0;
/* *
* @brief Set the TCP_NODELAY option to the socket.
*
* @param on
*/
virtual void setTcpNoDelay ( bool on) = 0;
/* *
* @brief Shutdown the connection.
* @note This method only closes the writing direction.
*/
virtual void shutdown () = 0;
/* *
* @brief Close the connection forcefully.
*
*/
virtual void forceClose () = 0;
/* *
* @brief Call this method to avoid being kicked off by TcpServer, refer to
* the kickoffIdleConnections method in the TcpServer class.
*
*/
virtual void keepAlive () = 0;
/* *
* @brief Return true if the keepAlive() method is called.
*
* @return true
* @return false
*/
virtual bool isKeepAlive () = 0;Menetapkan konteks koneksi untuk menangani logika bisnis khusus untuk koneksi.
/* *
* @brief Set the custom data on the connection.
*
* @param context
*/
void setContext ( const Any &context) { m_context = context; }
void setContext (Any &&context) { m_context = std::move (context); }
/* *
* @brief New mutable context
*
* @return Any
*/
Any & getContextRefMut () { return m_context; }
/* *
* @brief New unmutable context
*
* @return Any
*/
Any const & getContextRef () const { return m_context; }
/* *
* @brief Return true if the custom data is set by user.
*
* @return true
* @return false
*/
bool hasContext () const
{
# if __cplusplus >= 201703L
return m_context. has_value ();
# else
return m_context. empty ();
# endif
}
/* *
* @brief Clear the custom data.
*
*/
void clearContext ()
{
# if __cplusplus >= 201703L
m_context. reset ();
# else
m_context. clear ();
# endif
}Koneksi mengumpulkan jumlah data yang dikirim dan diterima oleh data.
/* *
* @brief Return the number of bytes sent
*
* @return size_t
*/
virtual size_t bytesSent () const = 0;
/* *
* @brief Return the number of bytes received.
*
* @return size_t
*/
virtual size_t bytesReceived () const = 0;Dapatkan loop yang sesuai untuk koneksi ini.
/* *
* @brief New the event loop in which the connection I/O is handled.
*
* @return EventLoop*
*/
virtual EventLoop * getLoop () = 0;Dua kasus penggunaan berikut disediakan sementara: