Skip to content

Commit 05f4d09

Browse files
committed
Refactor socket implementation with lock-free architecture
Core changes: - Replace mutex-based synchronization with lock-free SPSC queues - Add atomic operations for thread-safe socket state management - Fix deadlock/crash when releasing sockets on Windows New features: - Add Unix domain socket support (UnixSocket) - Add WebSocket client example (socket_websocket.sp) Improvements: - Update callback system with improved thread safety - Refactor EventLoop with better async task handling
1 parent df546b7 commit 05f4d09

31 files changed

Lines changed: 2390 additions & 754 deletions

AMBuilder

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ for cxx in builder.targets:
1616
'src/impl/socket/SocketBase.cpp',
1717
'src/impl/socket/TcpSocket.cpp',
1818
'src/impl/socket/UdpSocket.cpp',
19+
'src/impl/socket/UnixSocket.cpp',
1920
'src/impl/socket/SocketUtils.cpp',
2021
os.path.join(Extension.sm_root, 'public', 'smsdk_ext.cpp'),
2122
]

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ A complete rewrite of the [original socket extension](https://github.com/JoinedS
66

77
## Features
88
* Built on [libuv](https://github.com/libuv/libuv) for high-performance async I/O
9-
* TCP client and server support
10-
* UDP send/receive with SendTo support
9+
* TCP/UDP/UDS client and server support
1110
* IPv4/IPv6 support
1211
* Configurable socket options (timeout, buffer size, keep-alive, etc.)
1312
* Connect timeout for TCP connections

scripting/include/socket.inc

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
enum SocketType {
77
SOCKET_TCP = 1,
8-
SOCKET_UDP
8+
SOCKET_UDP,
9+
SOCKET_UNIX
910
}
1011

1112
enum SocketErrorType {
@@ -36,13 +37,13 @@ enum SocketOption {
3637
SocketSendTimeout, // SO_SNDTIMEO (ms, 0 = disabled)
3738
DebugMode, // Enable debug logging
3839
SocketConnectTimeout, // Connect timeout (ms, 0 = disabled, TCP only)
39-
SocketAutoClose // Auto close handle on disconnect/error (0 = disabled, 1 = enabled)
40+
SocketAutoFreeHandle // Auto close handle on disconnect/error (0 = disabled, 1 = enabled)
4041
}
4142

4243
/**
4344
* Callback for connection established
4445
*
45-
* @note TCP: Called when connection to remote host is established
46+
* @note TCP/Unix: Called when connection to remote host is established
4647
* @note UDP: Called when Connect() hostname resolution completes (UDP is connectionless)
4748
*
4849
* @param socket Socket handle
@@ -53,36 +54,36 @@ typedef SocketConnectCallback = function void (Socket socket, any data);
5354
/**
5455
* Callback for incoming connection on listening socket
5556
*
56-
* @note TCP only: Called when a new client connects to a listening socket
57+
* @note TCP/Unix: Called when a new client connects to a listening socket
5758
* @note UDP: Not applicable (UDP is connectionless, use ReceiveCallback instead)
5859
*
5960
* @param socket Listening socket handle
6061
* @param newSocket New client socket handle
61-
* @param remoteIP Client IP address
62-
* @param remotePort Client port
62+
* @param remoteIP Client IP address (Unix: socket path)
63+
* @param remotePort Client port (Unix: 0)
6364
* @param data User data passed to SetIncomingCallback
6465
*/
6566
typedef SocketIncomingCallback = function void (Socket socket, Socket newSocket, const char[] remoteIP, int remotePort, any data);
6667

6768
/**
6869
* Callback for data received
6970
*
70-
* @note TCP: Called when data is received from connected peer
71+
* @note TCP/Unix: Called when data is received from connected peer
7172
* @note UDP: Called when data is received from any sender
7273
*
7374
* @param socket Socket handle
7475
* @param buffer Received data (null-terminated)
7576
* @param size Data length (excluding null terminator)
76-
* @param senderIP Sender IP address (UDP only, empty for TCP)
77-
* @param senderPort Sender port (UDP only, 0 for TCP)
77+
* @param senderIP Sender IP address (TCP: remote peer, UDP: packet sender, Unix: empty)
78+
* @param senderPort Sender port (Unix: 0)
7879
* @param data User data passed to SetReceiveCallback
7980
*/
8081
typedef SocketReceiveCallback = function void (Socket socket, const char[] buffer, const int size, const char[] senderIP, int senderPort, any data);
8182

8283
/**
8384
* Callback for remote disconnection
8485
*
85-
* @note TCP only: Called when remote peer closes the connection
86+
* @note TCP/Unix: Called when remote peer closes the connection
8687
* @note UDP: Not applicable (UDP is connectionless)
8788
*
8889
* @param socket Socket handle
@@ -93,26 +94,26 @@ typedef SocketDisconnectCallback = function void (Socket socket, any data);
9394
/**
9495
* Callback for socket error
9596
*
96-
* @param socket Socket handle
97-
* @param errorType Error type (SocketErrorType)
98-
* @param errorNum System errno
99-
* @param data User data passed to SetErrorCallback
97+
* @param socket Socket handle
98+
* @param errorType Error type (SocketErrorType)
99+
* @param errorMsg Error message string
100+
* @param data User data passed to SetErrorCallback
100101
*/
101-
typedef SocketErrorCallback = function void (Socket socket, const int errorType, const int errorNum, any data);
102+
typedef SocketErrorCallback = function void (Socket socket, const int errorType, const char[] errorMsg, any data);
102103

103104
/**
104105
* Callback for listen ready
105106
*
106-
* @note Called when Listen() completes and socket is ready to accept connections (TCP) or receive data (UDP)
107+
* @note Called when Listen() completes and socket is ready to accept connections (TCP/Unix) or receive data (UDP)
107108
*
108109
* @param socket Socket handle
109-
* @param localIP Local bound IP address
110-
* @param localPort Local bound port
110+
* @param localIP Local bound IP address (Unix: socket path)
111+
* @param localPort Local bound port (Unix: 0)
111112
* @param data User data passed to SetListenCallback
112113
*/
113114
typedef SocketListenCallback = function void (Socket socket, const char[] localIP, int localPort, any data);
114115

115-
// Socket methodmap for TCP/UDP communication
116+
// Socket methodmap for TCP/UDP/Unix communication
116117
methodmap Socket < Handle {
117118
/**
118119
* Creates a new socket
@@ -136,9 +137,10 @@ methodmap Socket < Handle {
136137
*
137138
* @note TCP: Establishes a connection to the remote host
138139
* @note UDP: Sets default destination for Send() (no actual connection)
140+
* @note Unix: Connects to the specified socket path (port is ignored)
139141
*
140-
* @param host Remote IP or hostname
141-
* @param port Remote port
142+
* @param host Remote IP or hostname (Unix: socket path)
143+
* @param port Remote port (Unix: ignored)
142144
*/
143145
public native void Connect(const char[] host, int port);
144146

@@ -153,16 +155,16 @@ methodmap Socket < Handle {
153155
* Forcefully closes TCP connection with RST (TCP only)
154156
*
155157
* @note Immediately terminates connection without TIME_WAIT state
156-
* @note Only works for TCP sockets, returns false for UDP
158+
* @note Only works for TCP sockets, returns false for UDP/Unix
157159
*
158160
* @return True on success
159161
*/
160162
public native bool CloseReset();
161163

162164
/**
163-
* Starts listening for connections (TCP) or receiving data (UDP)
165+
* Starts listening for connections (TCP/Unix) or receiving data (UDP)
164166
*
165-
* @note TCP: Starts accepting incoming connections (requires SetIncomingCallback)
167+
* @note TCP/Unix: Starts accepting incoming connections (requires SetIncomingCallback)
166168
* @note UDP: Starts receiving data (use SetReceiveCallback)
167169
*
168170
* @return True on success
@@ -172,7 +174,7 @@ methodmap Socket < Handle {
172174
/**
173175
* Sends data
174176
*
175-
* @note TCP: Sends to connected peer
177+
* @note TCP/Unix: Sends to connected peer
176178
* @note UDP: Requires prior Connect() call to set destination
177179
*
178180
* @param data Data to send
@@ -195,7 +197,7 @@ methodmap Socket < Handle {
195197
*
196198
* @param option Option to set
197199
* @param value Option value
198-
* @return True on success false on failure
200+
* @return True on success, false on failure
199201
*/
200202
public native bool SetOption(SocketOption option, int value);
201203

@@ -232,9 +234,9 @@ methodmap Socket < Handle {
232234
public native void SetConnectCallback(SocketConnectCallback callback, any data = 0);
233235

234236
/**
235-
* Sets incoming connection callback (TCP only)
237+
* Sets incoming connection callback (TCP/Unix only)
236238
*
237-
* @note Required for TCP server mode. Not applicable for UDP.
239+
* @note Required for TCP/Unix server mode. Not applicable for UDP.
238240
*
239241
* @param callback Callback function
240242
* @param data User data passed to callback
@@ -256,7 +258,7 @@ methodmap Socket < Handle {
256258
*
257259
* @param buffer Destination buffer
258260
* @param maxLength Buffer size
259-
* @return True on success false on failure
261+
* @return True on success, false on failure
260262
*/
261263
public static native bool GetHostName(char[] buffer, int maxLength);
262264

@@ -268,7 +270,7 @@ methodmap Socket < Handle {
268270
*
269271
* @param buffer Destination buffer
270272
* @param maxLength Buffer size
271-
* @return True on success false on failure
273+
* @return True on success, false on failure
272274
*/
273275
public native bool GetLocalAddress(char[] buffer, int maxLength);
274276

@@ -283,7 +285,10 @@ methodmap Socket < Handle {
283285
public native int GetLocalPort();
284286

285287
/**
286-
* Whether socket is connected
288+
* Whether socket is connected/listening
289+
*
290+
* @note TCP/Unix: True if connected to peer or listening for connections
291+
* @note UDP: True if socket is initialized
287292
*/
288293
property bool Connected {
289294
public native get();

scripting/socket_http.sp

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
/**
2+
* Socket HTTP GET Example
3+
*
4+
* Demonstrates simple HTTP GET requests using TCP sockets.
5+
* Supports multiple concurrent requests.
6+
*
7+
* Commands:
8+
* sm_httpget <host> [port] [path] - Make HTTP GET request
9+
*
10+
* Usage:
11+
* sm_httpget example.com - GET http://example.com/
12+
* sm_httpget example.com 80 /api - GET http://example.com/api
13+
* sm_httpget api.example.com 8080 /v1 - GET http://api.example.com:8080/v1
14+
*
15+
* Note: This is a basic HTTP/1.1 client. For HTTPS, use a dedicated HTTP library.
16+
*/
17+
18+
#pragma semicolon 1
19+
#pragma newdecls required
20+
121
#include <sourcemod>
222
#include <socket>
323

@@ -9,7 +29,6 @@ public Plugin myinfo = {
929
url = "https://github.com/ProjectSky/sm-ext-socket"
1030
}
1131

12-
// HTTP request context
1332
enum struct HttpRequest {
1433
Socket socket;
1534
char host[128];
@@ -26,7 +45,6 @@ public void OnPluginStart() {
2645
RegConsoleCmd("sm_httpget", Command_HttpGet, "Make HTTP GET request");
2746
}
2847

29-
// Usage: sm_httpget <host> [port] [path]
3048
Action Command_HttpGet(int client, int args) {
3149
if (args < 1) {
3250
ReplyToCommand(client, "Usage: sm_httpget <host> [port] [path]");
@@ -51,11 +69,11 @@ Action Command_HttpGet(int client, int args) {
5169
}
5270

5371
req.socket = new Socket();
54-
req.socket.SetOption(SocketConnectTimeout, 10000); // 10 second timeout
55-
req.socket.SetConnectCallback(OnHttpConnect);
56-
req.socket.SetReceiveCallback(OnHttpReceive);
57-
req.socket.SetDisconnectCallback(OnHttpDisconnect);
58-
req.socket.SetErrorCallback(OnHttpError);
72+
req.socket.SetOption(SocketConnectTimeout, 10000);
73+
req.socket.SetConnectCallback(Socket_OnConnect);
74+
req.socket.SetReceiveCallback(Socket_OnReceive);
75+
req.socket.SetDisconnectCallback(Socket_OnDisconnect);
76+
req.socket.SetErrorCallback(Socket_OnError);
5977

6078
int index = g_Requests.PushArray(req);
6179
req.socket.Connect(req.host, req.port);
@@ -65,7 +83,7 @@ Action Command_HttpGet(int client, int args) {
6583
return Plugin_Handled;
6684
}
6785

68-
static void OnHttpConnect(Socket socket, any data) {
86+
void Socket_OnConnect(Socket socket, any data) {
6987
int index = FindRequestBySocket(socket);
7088
if (index == -1) return;
7189

@@ -85,7 +103,7 @@ static void OnHttpConnect(Socket socket, any data) {
85103
socket.Send(request);
86104
}
87105

88-
static void OnHttpReceive(Socket socket, const char[] buffer, const int size, const char[] senderIP, int senderPort, any data) {
106+
void Socket_OnReceive(Socket socket, const char[] buffer, const int size, const char[] senderIP, int senderPort, any data) {
89107
int index = FindRequestBySocket(socket);
90108
if (index == -1) return;
91109

@@ -106,7 +124,7 @@ static void OnHttpReceive(Socket socket, const char[] buffer, const int size, co
106124
g_Requests.SetArray(index, req);
107125
}
108126

109-
static void OnHttpDisconnect(Socket socket, any data) {
127+
void Socket_OnDisconnect(Socket socket, any data) {
110128
int index = FindRequestBySocket(socket);
111129
if (index == -1) return;
112130

@@ -134,14 +152,14 @@ static void OnHttpDisconnect(Socket socket, any data) {
134152
CleanupRequest(index);
135153
}
136154

137-
static void OnHttpError(Socket socket, const int errorType, const int errorNum, any data) {
155+
void Socket_OnError(Socket socket, const int errorType, const char[] errorMsg, any data) {
138156
int index = FindRequestBySocket(socket);
139157
if (index == -1) return;
140158

141159
HttpRequest req;
142160
g_Requests.GetArray(index, req);
143161

144-
PrintToServer("[HTTP] Error for %s:%d: type=%d, errno=%d", req.host, req.port, errorType, errorNum);
162+
PrintToServer("[HTTP] Error for %s:%d: type=%d, message=%s", req.host, req.port, errorType, errorMsg);
145163
CleanupRequest(index);
146164
}
147165

@@ -161,13 +179,4 @@ void CleanupRequest(int index) {
161179
g_Requests.GetArray(index, req);
162180
delete req.socket;
163181
g_Requests.Erase(index);
164-
}
165-
166-
public void OnPluginEnd() {
167-
HttpRequest req;
168-
for (int i = 0; i < g_Requests.Length; i++) {
169-
g_Requests.GetArray(i, req);
170-
delete req.socket;
171-
}
172-
delete g_Requests;
173182
}

0 commit comments

Comments
 (0)