@@ -94,8 +94,11 @@ X_STATUS XSocket::Initialize(AddressFamily af, Type type, Protocol proto) {
9494 asio::error_code ec;
9595
9696 if (type == Type::X_SOCK_STREAM) {
97- tcp_socket_.emplace (GetIoContext ());
98- tcp_socket_->open (asio::ip::tcp::v4 (), ec);
97+ // Use an acceptor for TCP — it supports bind, listen, and accept natively.
98+ // If Connect() is called later, we transition to a tcp_socket at that
99+ // point.
100+ acceptor_.emplace (GetIoContext ());
101+ acceptor_->open (asio::ip::tcp::v4 (), ec);
99102 } else if (type == Type::X_SOCK_DGRAM) {
100103 udp_socket_.emplace (GetIoContext ());
101104 udp_socket_->open (asio::ip::udp::v4 (), ec);
@@ -147,7 +150,7 @@ X_STATUS XSocket::Close() {
147150
148151X_STATUS XSocket::GetOption (uint32_t level, uint32_t optname, void * optval_ptr,
149152 uint32_t * optlen) {
150- if (!tcp_socket_ && !udp_socket_) {
153+ if (!tcp_socket_ && !udp_socket_ && !acceptor_ ) {
151154 return X_STATUS_INVALID_HANDLE;
152155 }
153156
@@ -193,7 +196,7 @@ X_STATUS XSocket::SetOption(uint32_t level, uint32_t optname, void* optval_ptr,
193196 return X_STATUS_SUCCESS;
194197 }
195198
196- if (!tcp_socket_ && !udp_socket_) {
199+ if (!tcp_socket_ && !udp_socket_ && !acceptor_ ) {
197200 return X_STATUS_INVALID_HANDLE;
198201 }
199202
@@ -234,7 +237,7 @@ X_STATUS XSocket::SetOption(uint32_t level, uint32_t optname, void* optval_ptr,
234237}
235238
236239X_STATUS XSocket::IOControl (uint32_t cmd, uint8_t * arg_ptr) {
237- if (!tcp_socket_ && !udp_socket_) {
240+ if (!tcp_socket_ && !udp_socket_ && !acceptor_ ) {
238241 return X_STATUS_INVALID_HANDLE;
239242 }
240243
@@ -245,7 +248,9 @@ X_STATUS XSocket::IOControl(uint32_t cmd, uint8_t* arg_ptr) {
245248 uint32_t value = *reinterpret_cast <uint32_t *>(arg_ptr);
246249 bool non_blocking = (value != 0 );
247250
248- if (tcp_socket_) {
251+ if (acceptor_) {
252+ acceptor_->non_blocking (non_blocking, ec);
253+ } else if (tcp_socket_) {
249254 tcp_socket_->non_blocking (non_blocking, ec);
250255 } else if (udp_socket_) {
251256 udp_socket_->non_blocking (non_blocking, ec);
@@ -281,7 +286,7 @@ X_STATUS XSocket::IOControl(uint32_t cmd, uint8_t* arg_ptr) {
281286}
282287
283288X_STATUS XSocket::Connect (N_XSOCKADDR* name, int name_len) {
284- if (!tcp_socket_ && !udp_socket_) {
289+ if (!tcp_socket_ && !udp_socket_ && !acceptor_ ) {
285290 return X_STATUS_INVALID_HANDLE;
286291 }
287292
@@ -291,7 +296,23 @@ X_STATUS XSocket::Connect(N_XSOCKADDR* name, int name_len) {
291296
292297 asio::error_code ec;
293298
294- if (tcp_socket_) {
299+ if (acceptor_) {
300+ // Transition from acceptor to tcp_socket for client connection.
301+ // The acceptor was created in Initialize() before we knew whether this
302+ // socket would listen or connect.
303+ acceptor_->close (ec);
304+ acceptor_.reset ();
305+
306+ tcp_socket_.emplace (GetIoContext ());
307+ tcp_socket_->open (asio::ip::tcp::v4 (), ec);
308+ if (ec) {
309+ last_error_ = AsioErrorToWSAError (ec);
310+ return X_STATUS_UNSUCCESSFUL;
311+ }
312+
313+ asio::ip::tcp::endpoint endpoint (addr, port);
314+ tcp_socket_->connect (endpoint, ec);
315+ } else if (tcp_socket_) {
295316 asio::ip::tcp::endpoint endpoint (addr, port);
296317 tcp_socket_->connect (endpoint, ec);
297318 } else if (udp_socket_) {
@@ -308,7 +329,7 @@ X_STATUS XSocket::Connect(N_XSOCKADDR* name, int name_len) {
308329}
309330
310331X_STATUS XSocket::Bind (N_XSOCKADDR_IN* name, int name_len) {
311- if (!tcp_socket_ && !udp_socket_) {
332+ if (!tcp_socket_ && !udp_socket_ && !acceptor_ ) {
312333 return X_STATUS_INVALID_HANDLE;
313334 }
314335
@@ -328,7 +349,10 @@ X_STATUS XSocket::Bind(N_XSOCKADDR_IN* name, int name_len) {
328349
329350 asio::error_code ec;
330351
331- if (tcp_socket_) {
352+ if (acceptor_) {
353+ asio::ip::tcp::endpoint endpoint (addr, port);
354+ acceptor_->bind (endpoint, ec);
355+ } else if (tcp_socket_) {
332356 asio::ip::tcp::endpoint endpoint (addr, port);
333357 tcp_socket_->bind (endpoint, ec);
334358 } else if (udp_socket_) {
@@ -344,7 +368,9 @@ X_STATUS XSocket::Bind(N_XSOCKADDR_IN* name, int name_len) {
344368 bound_ = true ;
345369
346370 // Get the actual bound port (important when binding to port 0)
347- if (tcp_socket_) {
371+ if (acceptor_) {
372+ bound_port_ = acceptor_->local_endpoint (ec).port ();
373+ } else if (tcp_socket_) {
348374 bound_port_ = tcp_socket_->local_endpoint (ec).port ();
349375 } else if (udp_socket_) {
350376 bound_port_ = udp_socket_->local_endpoint (ec).port ();
@@ -358,26 +384,12 @@ X_STATUS XSocket::Bind(N_XSOCKADDR_IN* name, int name_len) {
358384}
359385
360386X_STATUS XSocket::Listen (int backlog) {
361- if (!tcp_socket_ ) {
387+ if (!acceptor_ ) {
362388 return X_STATUS_INVALID_HANDLE;
363389 }
364390
365391 asio::error_code ec;
366392
367- // Create an acceptor and transfer the bound socket's native handle to it
368- acceptor_.emplace (GetIoContext ());
369- acceptor_->assign (asio::ip::tcp::v4 (), tcp_socket_->native_handle (), ec);
370- if (ec) {
371- last_error_ = AsioErrorToWSAError (ec);
372- acceptor_.reset ();
373- return X_STATUS_UNSUCCESSFUL;
374- }
375-
376- // Release the socket's handle since the acceptor now owns it
377- tcp_socket_->release ();
378- tcp_socket_.reset ();
379-
380- // Start listening
381393 acceptor_->listen (backlog, ec);
382394 if (ec) {
383395 last_error_ = AsioErrorToWSAError (ec);
0 commit comments