WebRTC--添加IOCP网络模型支持
来源:互联网 发布:intouch组态软件下载 编辑:程序博客网 时间:2024/05/19 17:52
一、起因
webRTC在windows平台默认使用的是WSAAsyncSelect模型,该模型需要有一个windows窗口的支持,而且伸缩性、性能都比较低。
关于WSAAsyncSelect模型的介绍可以参考Windows套接字I/O模型(3) – WSAAsyncSelect模型
因为webRTC是点对点的数据传输,对每一个端的性能要求并不高,采用WSAAsyncSelect模型完全是足够的。但是我们如果需要把rtc_base单独抽出来使用,那么在windows平台上加上IOCP模型的支持就很有必要了。
关于IOCP完成端口模型的介绍可以参考Windows套接字I/O模型(5) – 完成端口模型
二、实现
iocp.h
, iocp.cc
定义和实现iocp相关的结构体,通用功能。
iocp.h
#ifndef RTC_BASE_IOCP_H_#define RTC_BASE_IOCP_H_#if defined(WEBRTC_WIN)#include <winsock2.h>#include <MSWSock.h>#include <vector>#define MAX_BUFFER_LEN 8192 #define EXIT_CODE 0namespace IOCP { typedef enum _OPERATION_TYPE { ACCEPT_POSTED, CONNECT_POSTED, SEND_POSTED, RECV_POSTED, NULL_POSTED }OPERATION_TYPE; typedef struct _PER_IO_CONTEXT { OVERLAPPED overlapped; SOCKET socket; WSABUF wsa_buffer; char buffer[MAX_BUFFER_LEN]; OPERATION_TYPE operation_type; _PER_IO_CONTEXT() { ZeroMemory(&overlapped, sizeof(overlapped)); ZeroMemory(buffer, MAX_BUFFER_LEN); socket = INVALID_SOCKET; wsa_buffer.buf = buffer; wsa_buffer.len = MAX_BUFFER_LEN; operation_type = NULL_POSTED; } ~_PER_IO_CONTEXT() { // don't close socket } void ResetBuffer() { ZeroMemory(buffer, MAX_BUFFER_LEN); } const char* GetBuffer() const { return wsa_buffer.buf; } ULONG GetBufferLength() const { return wsa_buffer.len; } } PER_IO_CONTEXT; typedef struct _PER_SOCKET_CONTEXT { SOCKET socket; SOCKADDR_IN client_addr; std::vector<_PER_IO_CONTEXT*> io_ctx_array; _PER_SOCKET_CONTEXT() { socket = INVALID_SOCKET; memset(&client_addr, 0, sizeof(client_addr)); } ~_PER_SOCKET_CONTEXT() { if (socket != INVALID_SOCKET) { closesocket(socket); socket = INVALID_SOCKET; } for (size_t i = 0; i < io_ctx_array.size(); i++) { delete io_ctx_array[i]; } io_ctx_array.clear(); } _PER_IO_CONTEXT* GetNewIoContext() { _PER_IO_CONTEXT* p = new _PER_IO_CONTEXT; io_ctx_array.push_back(p); return p; } void RemoveContext(_PER_IO_CONTEXT* pContext) { for (std::vector<_PER_IO_CONTEXT*>::iterator it = io_ctx_array.begin(); it != io_ctx_array.end(); it++) { if (pContext == *it) { delete pContext; pContext = NULL; io_ctx_array.erase(it); break; } } } } PER_SOCKET_CONTEXT; int GetNumberOfProcesser(); HANDLE CreateNewCompletionPort(); BOOL AssociateDeviceWithCompletionPort(HANDLE completion_port, HANDLE device, DWORD completion_key); LPFN_ACCEPTEX GetAcceptExFnPointer(SOCKET s); LPFN_CONNECTEX GetConnectExFnPointer(SOCKET s); LPFN_GETACCEPTEXSOCKADDRS GetAcceptExSockAddrsFnPointer(SOCKET s);};#endif#endif // IOCP_H_
iocp.cc
#include "iocp.h"#if defined(WEBRTC_WIN)namespace IOCP { int GetNumberOfProcesser() { SYSTEM_INFO si; GetSystemInfo(&si); return si.dwNumberOfProcessors; } HANDLE CreateNewCompletionPort() { return CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); } BOOL AssociateDeviceWithCompletionPort(HANDLE completion_port, HANDLE device, DWORD completion_key) { HANDLE h = CreateIoCompletionPort(device, completion_port, completion_key, 0); return (h == completion_port); } LPFN_ACCEPTEX GetAcceptExFnPointer(SOCKET s) { LPFN_ACCEPTEX fn = NULL; GUID GuidAcceptEx = WSAID_ACCEPTEX; DWORD bytes = 0; if (SOCKET_ERROR == WSAIoctl( s, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx), &fn, sizeof(fn), &bytes, NULL, NULL)) { return NULL; } return fn; } LPFN_CONNECTEX GetConnectExFnPointer(SOCKET s) { LPFN_CONNECTEX fn = NULL; GUID GuidConnectEx = WSAID_CONNECTEX; DWORD bytes = 0; if (SOCKET_ERROR == WSAIoctl( s, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidConnectEx, sizeof(GuidConnectEx), &fn, sizeof(fn), &bytes, NULL, NULL)) { return NULL; } return fn; } LPFN_GETACCEPTEXSOCKADDRS GetAcceptExSockAddrsFnPointer(SOCKET s) { LPFN_GETACCEPTEXSOCKADDRS fn = NULL; GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS; DWORD bytes = 0; if (SOCKET_ERROR == WSAIoctl( s, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidGetAcceptExSockAddrs, sizeof(GuidGetAcceptExSockAddrs), &fn, sizeof(fn), &bytes, NULL, NULL)) { return NULL; } return fn; }}#endif
overlappedsocket.h
, overlappedsocket.cc
定义了rtc::OverlappedSocket类。
在webRTC中Socket都派生自rtc::Socket–>rtc::AsyncSocket,虽说IOCP模型也是异步socket,但rtc::AsyncSocket接口和信号的定义是根据WSAAsyncSelect模型来的,不适应于IOCP模型,例如:
AsyncSocket中的SignalReadEvent
信号
sigslot::signal1<AsyncSocket*,sigslot::multi_threaded_local>
它只有一个参数AsyncSocket,在收到该信号时,再通过AsyncSocket::Read方法去获取数据。而IOCP是在收到通知时,数据已经获取完成了,所以webRTC提供的基类的接口、信号不是适用于IOCP模型,故OverlappedSocket没派生自任何基类。
overlappedsocket.h
#ifndef RTC_BASE_OVERLAPPED_SOCKET_H_#define RTC_BASE_OVERLAPPED_SOCKET_H_#include "rtc_base/iocp.h"#include "rtc_base/socket.h"#include "rtc_base/asyncsocket.h"#include "rtc_base/event.h"#include "rtc_base/thread.h"#if defined(WEBRTC_WIN)namespace rtc { class OverlappedSocket { public: OverlappedSocket(); virtual ~OverlappedSocket(); bool CreateT(int family, int type); SocketAddress GetLocalAddress() const; SocketAddress GetRemoteAddress() const; int Bind(const SocketAddress& addr); int Connect(const SocketAddress& addr); int Send(const void* buffer, size_t length, IOCP::PER_IO_CONTEXT* io_ctx = NULL); int Listen(int backlog); bool Accept(); int Close(); int GetError() const; void SetError(int error); Socket::ConnState GetState() const; int GetOption(Socket::Option opt, int* value); int SetOption(Socket::Option opt, int value); void Clone(OverlappedSocket *socket); public: class CompletionIOHandler : public Thread { public: CompletionIOHandler(OverlappedSocket* parent, int index); ~CompletionIOHandler(); void Run() override; private: OverlappedSocket*parent_; }; public: sigslot::signal1<OverlappedSocket*, sigslot::multi_threaded_local> SignalAcceptEvent; sigslot::signal2<OverlappedSocket*, const IOCP::PER_IO_CONTEXT*, sigslot::multi_threaded_local> SignalReadEvent; sigslot::signal2<OverlappedSocket*, const IOCP::PER_IO_CONTEXT*, sigslot::multi_threaded_local> SignalWriteEvent; sigslot::signal1<OverlappedSocket*> SignalConnectEvent; // connected sigslot::signal2<OverlappedSocket*, int> SignalCloseEvent; // closed private: bool InitIOCP(SOCKET s); void UpdateLastError(); bool PostAccept(IOCP::PER_IO_CONTEXT* io_ctx); bool PostRecv(IOCP::PER_IO_CONTEXT* io_ctx); bool PostSend(IOCP::PER_IO_CONTEXT* io_ctx, const void* msg, size_t msg_len); bool PostConnect(IOCP::PER_IO_CONTEXT* io_ctx, const SocketAddress& addr); private: int error_; int family_; int type_; Event exit_; HANDLE iocp_; int workthread_num_; std::vector<std::unique_ptr<CompletionIOHandler>> workthreads_; IOCP::PER_SOCKET_CONTEXT* own_socket_ctx_; LPFN_CONNECTEX connectex_fn_; LPFN_ACCEPTEX acceptex_fn_; LPFN_GETACCEPTEXSOCKADDRS getacceptexsockaddrs_fn_; Socket::ConnState state_; SocketAddress addr_; // address that we connected to (see DoConnect) SocketAddress remote_addr_; int64_t connect_time_; bool closing_; int close_error_; };}#endif#endif
overlappedsocket.cc
#include "rtc_base/overlappedsocket.h"#include "rtc_base/win32socketserver.h"#include "rtc_base/checks.h"#include "rtc_base/logging.h"#include <process.h>#include "rtc_base/timeutils.h"#include "rtc_base/nullsocketserver.h"#if defined(WEBRTC_WIN)namespace rtc { OverlappedSocket::CompletionIOHandler::CompletionIOHandler(OverlappedSocket* parent, int index) : Thread(std::unique_ptr<SocketServer>(new NullSocketServer())), parent_(parent) { std::string thread_name = "OverlappedSocketWorkThread_"; SetName(thread_name + std::to_string(index), this); Start(); } OverlappedSocket::CompletionIOHandler::~CompletionIOHandler() { } void OverlappedSocket::CompletionIOHandler::Run() { RTC_DCHECK(parent_); DWORD transferred_bytes = 0; OverlappedSocket *overlapped_socket = NULL; OVERLAPPED *overlapped = NULL; DWORD gle = 0; while (!parent_->exit_.Wait(0)) { BOOL ret = GetQueuedCompletionStatus(parent_->iocp_, &transferred_bytes, (PULONG_PTR)&overlapped_socket, &overlapped, INFINITE); if (overlapped_socket == EXIT_CODE) { break; } if (ret == FALSE) { gle = GetLastError(); if (gle == WAIT_TIMEOUT) { RTC_NOTREACHED(); continue; } else if (gle == ERROR_NETNAME_DELETED) { parent_->SignalCloseEvent(overlapped_socket, gle); continue; } else { parent_->SignalCloseEvent(overlapped_socket, gle); break; } } else { IOCP::PER_IO_CONTEXT *io_ctx = CONTAINING_RECORD(overlapped, IOCP::PER_IO_CONTEXT, overlapped); if ((transferred_bytes == 0) && (io_ctx->operation_type == IOCP::RECV_POSTED || io_ctx->operation_type == IOCP::SEND_POSTED)) { parent_->SignalCloseEvent(overlapped_socket, gle); continue; } if (io_ctx->operation_type == IOCP::ACCEPT_POSTED) { sockaddr_storage* ClientAddr = NULL; sockaddr_storage* LocalAddr = NULL; int remoteLen = sizeof(sockaddr_storage); int localLen = sizeof(sockaddr_storage); // https://msdn.microsoft.com/en-us/library/windows/desktop/ms738516(v=vs.85).aspx parent_->getacceptexsockaddrs_fn_(io_ctx->wsa_buffer.buf, 0, sizeof(sockaddr_storage) + 16, sizeof(sockaddr_storage) + 16, (LPSOCKADDR*)&LocalAddr, &localLen, (LPSOCKADDR*)&ClientAddr, &remoteLen); SocketAddress remote_addr; SocketAddressFromSockAddrStorage(*ClientAddr, &remote_addr); OverlappedSocket* new_overlapped_socket = new OverlappedSocket(); new_overlapped_socket->remote_addr_ = remote_addr; IOCP::PER_SOCKET_CONTEXT* new_socket_ctx = new IOCP::PER_SOCKET_CONTEXT(); new_socket_ctx->socket = io_ctx->socket; new_overlapped_socket->own_socket_ctx_ = new_socket_ctx; overlapped_socket->Clone(new_overlapped_socket); if (!IOCP::AssociateDeviceWithCompletionPort(parent_->iocp_, (HANDLE)new_overlapped_socket->own_socket_ctx_->socket, (DWORD)new_overlapped_socket)) { delete new_socket_ctx; new_socket_ctx = NULL; } else { new_overlapped_socket->connect_time_ = TimeMillis(); // post new accept if (!parent_->PostAccept(io_ctx)) { RTC_NOTREACHED(); } parent_->SignalAcceptEvent(new_overlapped_socket); // post recv IOCP::PER_IO_CONTEXT *new_io_ctx = new_socket_ctx->GetNewIoContext(); new_io_ctx->socket = new_socket_ctx->socket; if (!parent_->PostRecv(new_io_ctx)) { new_socket_ctx->RemoveContext(new_io_ctx); } } } else if (io_ctx->operation_type == IOCP::RECV_POSTED) { parent_->SignalReadEvent(overlapped_socket, io_ctx); if (!parent_->PostRecv(io_ctx)) { overlapped_socket->own_socket_ctx_->RemoveContext(io_ctx); } } else if (io_ctx->operation_type == IOCP::SEND_POSTED) { parent_->SignalWriteEvent(overlapped_socket, io_ctx); overlapped_socket->own_socket_ctx_->RemoveContext(io_ctx); } else if (io_ctx->operation_type == IOCP::CONNECT_POSTED) { parent_->SignalConnectEvent(overlapped_socket); } else { RTC_NOTREACHED(); } } } } OverlappedSocket::OverlappedSocket() : error_(0), close_error_(0), workthread_num_(0), own_socket_ctx_(NULL), iocp_(INVALID_HANDLE_VALUE), exit_(true, false), state_(Socket::CS_CLOSED), connect_time_(0), closing_(false), connectex_fn_(NULL), acceptex_fn_(NULL), getacceptexsockaddrs_fn_(NULL), family_(AF_UNSPEC), type_(0) { } OverlappedSocket::~OverlappedSocket() { Close(); } bool OverlappedSocket::CreateT(int family, int type) { Close(); int proto = (SOCK_DGRAM == type) ? IPPROTO_UDP : IPPROTO_TCP; SOCKET s = ::WSASocket(family, type, proto, nullptr, 0, WSA_FLAG_OVERLAPPED); if (s == INVALID_SOCKET) { UpdateLastError(); return false; } if (!InitIOCP(s)) { return false; } family_ = family; type_ = type; return true; } SocketAddress OverlappedSocket::GetLocalAddress() const { sockaddr_storage addr = { 0 }; socklen_t addrlen = sizeof(addr); int result = ::getsockname(own_socket_ctx_->socket, reinterpret_cast<sockaddr*>(&addr), &addrlen); SocketAddress address; if (result >= 0) { SocketAddressFromSockAddrStorage(addr, &address); } else { RTC_LOG(LS_WARNING) << "GetLocalAddress: unable to get local addr, socket=" << own_socket_ctx_->socket; } return address; } SocketAddress OverlappedSocket::GetRemoteAddress() const { //sockaddr_storage addr = { 0 }; //socklen_t addrlen = sizeof(addr); //int result = ::getpeername(socket_, reinterpret_cast<sockaddr*>(&addr), // &addrlen); //SocketAddress address; //if (result >= 0) { // SocketAddressFromSockAddrStorage(addr, &address); //} //else { // RTC_LOG(LS_WARNING) // << "GetRemoteAddress: unable to get remote addr, socket=" << socket_ << ",GLE=" << WSAGetLastError(); //} //return address; return remote_addr_; } int OverlappedSocket::Bind(const SocketAddress & addr) { RTC_DCHECK(own_socket_ctx_->socket != INVALID_SOCKET); if (own_socket_ctx_->socket == INVALID_SOCKET) return SOCKET_ERROR; sockaddr_storage saddr; size_t len = addr.ToSockAddrStorage(&saddr); int err = ::bind(own_socket_ctx_->socket, reinterpret_cast<sockaddr*>(&saddr), static_cast<int>(len)); UpdateLastError(); return err; } int OverlappedSocket::Connect(const SocketAddress & addr) { RTC_DCHECK(own_socket_ctx_ != NULL); if (own_socket_ctx_ == NULL) { return SOCKET_ERROR; } IOCP::PER_IO_CONTEXT* io_ctx = own_socket_ctx_->GetNewIoContext(); io_ctx->socket = own_socket_ctx_->socket; if (!PostConnect(io_ctx, addr)) { own_socket_ctx_->RemoveContext(io_ctx); return SOCKET_ERROR; } return 0; } int OverlappedSocket::Send(const void * buffer, size_t length, IOCP::PER_IO_CONTEXT* io_ctx /* = NULL*/) { RTC_DCHECK(own_socket_ctx_ != NULL); if (own_socket_ctx_ == NULL) { return SOCKET_ERROR; } if (!io_ctx) { io_ctx = own_socket_ctx_->GetNewIoContext(); io_ctx->socket = own_socket_ctx_->socket; } if (!PostSend(io_ctx, buffer, length)) { own_socket_ctx_->RemoveContext(io_ctx); return SOCKET_ERROR; } return 0; } int OverlappedSocket::Listen(int backlog) { int err = ::listen(own_socket_ctx_->socket, backlog); UpdateLastError(); if (err == 0) state_ = Socket::CS_CONNECTING; return err; } bool OverlappedSocket::Accept() { int success = 0; for (int i = 0; i < workthread_num_; i++) { IOCP::PER_IO_CONTEXT *io_ctx = own_socket_ctx_->GetNewIoContext(); if (!PostAccept(io_ctx)) { own_socket_ctx_->RemoveContext(io_ctx); } else { success++; } } return success > 0; } int OverlappedSocket::Close() { int err = 0; exit_.Set(); for (int i = 0; i < workthread_num_; i++) { PostQueuedCompletionStatus(iocp_, 0, (DWORD)EXIT_CODE, NULL); } workthreads_.clear(); workthread_num_ = 0; if (own_socket_ctx_) { if (own_socket_ctx_->socket != INVALID_SOCKET) { err = ::closesocket(own_socket_ctx_->socket); own_socket_ctx_->socket = INVALID_SOCKET; closing_ = false; close_error_ = 0; UpdateLastError(); } delete own_socket_ctx_; own_socket_ctx_ = NULL; } if (iocp_ != INVALID_HANDLE_VALUE) { CloseHandle(iocp_); iocp_ = INVALID_HANDLE_VALUE; } exit_.Reset(); connectex_fn_ = NULL; acceptex_fn_ = NULL; getacceptexsockaddrs_fn_ = NULL; addr_.Clear(); state_ = Socket::CS_CLOSED; return err; } int OverlappedSocket::GetError() const { return error_; } void OverlappedSocket::SetError(int error) { error_ = error; } Socket::ConnState OverlappedSocket::GetState() const { return state_; } int OverlappedSocket::GetOption(Socket::Option opt, int * value) { int slevel; int sopt; if (Win32Socket::TranslateOption(opt, &slevel, &sopt) == -1) return -1; char* p = reinterpret_cast<char*>(value); int optlen = sizeof(value); return ::getsockopt(own_socket_ctx_->socket, slevel, sopt, p, &optlen); } int OverlappedSocket::SetOption(Socket::Option opt, int value) { int slevel; int sopt; if (Win32Socket::TranslateOption(opt, &slevel, &sopt) == -1) return -1; const char* p = reinterpret_cast<const char*>(&value); return ::setsockopt(own_socket_ctx_->socket, slevel, sopt, p, sizeof(value)); } void OverlappedSocket::Clone(OverlappedSocket *socket) { socket->family_ = this->family_; socket->type_ = this->type_; } bool OverlappedSocket::InitIOCP(SOCKET s) { iocp_ = IOCP::CreateNewCompletionPort(); if (iocp_ == INVALID_HANDLE_VALUE) { return false; } exit_.Reset(); workthread_num_ = IOCP::GetNumberOfProcesser() * 2; for (int i = 0; i < workthread_num_; i++) { std::unique_ptr<CompletionIOHandler> handler = std::make_unique<CompletionIOHandler>(this, i); workthreads_.push_back(std::move(handler)); } own_socket_ctx_ = new IOCP::PER_SOCKET_CONTEXT(); own_socket_ctx_->socket = s; if (!IOCP::AssociateDeviceWithCompletionPort(iocp_, (HANDLE)own_socket_ctx_->socket, (DWORD)this)) { Close(); return false; } connectex_fn_ = IOCP::GetConnectExFnPointer(own_socket_ctx_->socket); RTC_DCHECK(connectex_fn_); acceptex_fn_ = IOCP::GetAcceptExFnPointer(own_socket_ctx_->socket); RTC_DCHECK(acceptex_fn_); getacceptexsockaddrs_fn_ = IOCP::GetAcceptExSockAddrsFnPointer(own_socket_ctx_->socket); RTC_DCHECK(getacceptexsockaddrs_fn_); return true; } void OverlappedSocket::UpdateLastError() { error_ = WSAGetLastError(); } bool OverlappedSocket::PostAccept(IOCP::PER_IO_CONTEXT* io_ctx) { if (io_ctx == NULL) return false; int proto = (SOCK_DGRAM == type_) ? IPPROTO_UDP : IPPROTO_TCP; io_ctx->operation_type = IOCP::ACCEPT_POSTED; io_ctx->ResetBuffer(); io_ctx->socket = WSASocket(family_, type_, proto, NULL, 0, WSA_FLAG_OVERLAPPED); if (io_ctx->socket == INVALID_SOCKET) { UpdateLastError(); return false; } DWORD bytes = 0; if (acceptex_fn_(own_socket_ctx_->socket, io_ctx->socket, io_ctx->wsa_buffer.buf, 0, sizeof(sockaddr_storage) + 16, sizeof(sockaddr_storage) + 16, &bytes, &io_ctx->overlapped) == FALSE) { int gle = WSAGetLastError(); if (gle != WSA_IO_PENDING) { UpdateLastError(); return false; } } UpdateLastError(); return true; } bool OverlappedSocket::PostRecv(IOCP::PER_IO_CONTEXT* io_ctx) { if (io_ctx == NULL) return false; io_ctx->operation_type = IOCP::RECV_POSTED; io_ctx->ResetBuffer(); DWORD recv_bytes = 0; DWORD flags = 0; int ret = WSARecv(io_ctx->socket, &io_ctx->wsa_buffer, 1, &recv_bytes, &flags, &io_ctx->overlapped, NULL); if (ret == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { UpdateLastError(); return false; } return true; } bool OverlappedSocket::PostSend(IOCP::PER_IO_CONTEXT* io_ctx, const void* msg, size_t msg_len) { if (io_ctx == NULL) return false; io_ctx->operation_type = IOCP::SEND_POSTED; memcpy(io_ctx->wsa_buffer.buf, msg, msg_len); io_ctx->wsa_buffer.len = msg_len; DWORD sent_bytes = 0; int ret = WSASend(io_ctx->socket, &io_ctx->wsa_buffer, 1, &sent_bytes, 0, &io_ctx->overlapped, NULL); if (ret == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { UpdateLastError(); return false; } UpdateLastError(); return true; } bool OverlappedSocket::PostConnect(IOCP::PER_IO_CONTEXT* io_ctx, const SocketAddress& addr) { if (io_ctx == NULL) return false; io_ctx->operation_type = IOCP::CONNECT_POSTED; io_ctx->ResetBuffer(); // ConnectEx requires the socket to be initially bound. struct sockaddr_in addr0 = { 0 }; addr0.sin_family = family_; addr0.sin_addr.s_addr = INADDR_ANY; addr0.sin_port = 0; int ret = bind(io_ctx->socket, (SOCKADDR*)&addr0, sizeof(addr0)); if (ret != 0) { UpdateLastError(); return false; } sockaddr_storage saddr; size_t len = addr.ToSockAddrStorage(&saddr); ret = connectex_fn_(io_ctx->socket, reinterpret_cast<const sockaddr*>(&saddr), len, NULL, 0, NULL, &io_ctx->overlapped); int gle = WSAGetLastError(); if (ret == SOCKET_ERROR && gle != WSA_IO_PENDING) { UpdateLastError(); return false; } UpdateLastError(); return true; }}#endif
win32iocpserver.h
, win32iocpserver.cc
定义了Win32IOCPServer
类,提供使用OverlappedSocket
实现IOCP服务端的功能。
win32iocpserver.h
#ifndef RTC_BASE_WIN32_IOCP_SERVER_H_#define RTC_BASE_WIN32_IOCP_SERVER_H_#if defined(WEBRTC_WIN)#include "rtc_base/socketfactory.h"#include "rtc_base/socketserver.h"#include "rtc_base/iocp.h"#include "rtc_base/criticalsection.h"namespace rtc { class OverlappedSocket; typedef std::list<OverlappedSocket*> ClientList; class Win32IOCPServer : public sigslot::has_slots<> { public: Win32IOCPServer(); virtual ~Win32IOCPServer(); bool Start(const SocketAddress &addr, int family, int type); bool Stop(); int64_t GetStartTime() const; void OnAcceptEvent(OverlappedSocket* socket); void OnReadEvent(OverlappedSocket* socket, const IOCP::PER_IO_CONTEXT* io_ctx); void OnWriteEvent(OverlappedSocket* socket, const IOCP::PER_IO_CONTEXT* io_ctx); void OnCloseEvent(OverlappedSocket* socket, int error); private: int64_t start_time_; OverlappedSocket* socket_; ClientList client_list_; CriticalSection crit_; // CriticalSection for client_list_ };}#endif#endif // RTC_BASE_WIN32_IOCP_SERVER_H_
win32iocpserver.cc
#include "rtc_base/win32iocpserver.h"#include "rtc_base/timeutils.h"#include "rtc_base/logging.h"#include "rtc_base/overlappedsocket.h"namespace rtc { Win32IOCPServer::Win32IOCPServer() : start_time_(0) { } Win32IOCPServer::~Win32IOCPServer() { } bool Win32IOCPServer::Start(const SocketAddress & addr, int family, int type) { socket_ = new OverlappedSocket(); RTC_DCHECK(socket_); socket_->SignalAcceptEvent.connect(this, &Win32IOCPServer::OnAcceptEvent); socket_->SignalReadEvent.connect(this, &Win32IOCPServer::OnReadEvent); socket_->SignalWriteEvent.connect(this, &Win32IOCPServer::OnWriteEvent); socket_->SignalCloseEvent.connect(this, &Win32IOCPServer::OnCloseEvent); if (!socket_->CreateT(family, type)) { RTC_LOG(LS_WARNING) << "CreateT: failed, error=" << socket_->GetError(); return false; } if (socket_->Bind(addr) == SOCKET_ERROR) { RTC_LOG(LS_WARNING) << "Bind: failed, error=" << socket_->GetError(); return false; } if (socket_->Listen(SOMAXCONN) == SOCKET_ERROR) { RTC_LOG(LS_WARNING) << "Listen: failed, error=" << socket_->GetError(); return false; } if (!socket_->Accept()) { RTC_LOG(LS_WARNING) << "Accept: failed, error=" << socket_->GetError(); return false; } start_time_ = TimeMillis(); return true; } bool Win32IOCPServer::Stop() { if (socket_->Close() == SOCKET_ERROR) { RTC_LOG(LS_WARNING) << "Close: failed, error=" << socket_->GetError(); return false; } { CritScope cs(&crit_); for (ClientList::iterator it = client_list_.begin(); it != client_list_.end(); it++) { RTC_DCHECK((*it) != NULL); (*it)->Close(); delete (*it); } client_list_.clear(); } start_time_ = 0; delete socket_; socket_ = NULL; return true; } int64_t Win32IOCPServer::GetStartTime() const { return start_time_; } void Win32IOCPServer::OnAcceptEvent(OverlappedSocket * socket) { RTC_DCHECK(socket); RTC_LOG(LS_INFO) << "[" << socket->GetRemoteAddress().ToString() << "] [Connected]"; { CritScope cs(&crit_); client_list_.push_back(socket); } } void Win32IOCPServer::OnReadEvent(OverlappedSocket * socket, const IOCP::PER_IO_CONTEXT * io_ctx) { RTC_DCHECK(socket); RTC_DCHECK(io_ctx); RTC_LOG(LS_INFO) << "[" << socket->GetRemoteAddress().ToString() << "] [RECV] " << io_ctx->GetBuffer(); } void Win32IOCPServer::OnWriteEvent(OverlappedSocket * socket, const IOCP::PER_IO_CONTEXT * io_ctx) { RTC_DCHECK(socket); RTC_DCHECK(io_ctx); RTC_LOG(LS_INFO) << "[" << socket->GetRemoteAddress().ToString() << "] [SEND] " << io_ctx->GetBuffer(); } void Win32IOCPServer::OnCloseEvent(OverlappedSocket * socket, int error) { RTC_DCHECK(socket); RTC_LOG(LS_INFO) << "[" << socket->GetRemoteAddress().ToString() << "] [Disconnected] error=" << error; socket->Close(); { CritScope cs(&crit_); client_list_.remove(socket); } delete socket; socket = NULL; }}
只需要将上面6个文件加入到rtc_base工程中即可。
如何使用visual studio管理和生成rtc_base,参考WebRTC–rtc_base库移植
三、测试
测试工程已经上传到码云:
git clone https://gitee.com/china_jeffery/webrtc.git
工程文件见webrtc\src\msvc\test
目录。
后期更新也会同步到该git.