1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: master header file for socketlib.lib
//
// $Header: $
// $NoKeywords: $
//===========================================================================//
#ifndef SOCKETLIB_H
#define SOCKETLIB_H
#ifdef _WIN32
#pragma once
#endif
#include "tier0/basetypes.h"
#include "utlbuffer.h"
typedef int64 SocketHandle_t;
//===========================================================================
// Error Codes used by socketlib
//===========================================================================
enum SocketErrorCode_t
{
SOCKET_SUCCESS = 0,
SOCKET_ERR_OPERATION_NOT_SUPPORTED,
SOCKET_ERR_CREATE_FAILED,
SOCKET_ERR_READ_OPERATION_FAILED,
SOCKET_ERR_WRITE_OPERATION_FAILED,
SOCKET_ERR_CONNECT_FAILED,
SOCKET_ERR_LISTEN_FAILED,
SOCKET_ERR_ACCEPT_FAILED,
SOCKET_ERR_POLLING_OPERATION_FAILED,
SOCKET_ERR_BIND_OPERATION_FAILED,
SOCKET_ERR_ENABLE_NON_BLOCKING_MODE_FAILED,
SOCKET_ERR_HOST_NOT_FOUND,
SOCKET_ERR_GENERAL_SOCKET_ERROR,
SOCKET_ERR_READ_OPERATION_WOULD_BLOCK,
SOCKET_ERR_WRITE_OPERATION_WOULD_BLOCK,
SOCKET_ERR_CONNECTION_CLOSED,
SOCKET_ERR_CONNECTION_RESET,
SOCKET_ERR_NO_INCOMING_CONNECTIONS,
SOCKET_ERR_NO_AVAILABLE_ENDPOINTS,
SOCKET_ERR_BAD_USER_DATA,
SOCKET_ERR_INVALID_CONNECTION,
SOCKET_ERR_CANT_WRITE,
SOCKET_ERR_MIXING_PACKET_SENDS,
SOCKET_ERR_PARTIAL_PACKET_OVERFLOW,
};
const int MAX_SERVER_CONNECTIONS = 4; // Maximum number of concurrent connections allowed to a server.
// In the future, this can be set higher and class CSocketConnection should use a growable array of Sockets.
const int MAX_SERVER_CONNECTION_BACKLOG = 16; // Maximum number of connections allowed in the listening backlog.
const int INVALID_ENDPOINT_INDEX = -1; // Represents an invalid endpoint index.
enum ConnectionType_t // Type of connection: client or server.
{
CT_INDETERMINATE = 0,
CT_CLIENT,
CT_SERVER,
};
enum SocketProtocol_t // Protocol to use for the socket.
{
SP_INDETERMINATE = 0,
SP_UDP,
SP_VDP,
SP_TCP,
};
enum SocketState_t // State of a given socket.
{
SSTATE_UNINITIALIZED = 0, // Socket is in completely uninitialized state.
SSTATE_LISTENING, // Server socket is listening for connections.
SSTATE_CONNECTION_IN_PROGRESS, // Socket is initialized and in the process of connecting to a client/server.
SSTATE_CONNECTED, // Socket is initialized and connected to a client/server.
};
//-----------------------------------------------------------------------
// Platform and build specific defines, change these as needed
//-----------------------------------------------------------------------
#if defined( PLATFORM_X360 )
#define VDP_SUPPORT_ENABLED 1
#else
#define VDP_SUPPORT_ENABLED 0
#endif
#if defined( _DEBUG )
#define UNSECURE_SOCKETS_ENABLED 1
#else
#define UNSECURE_SOCKETS_ENABLED 0
#endif
// ----------------------------------------------------------------------------
// Basic platform-specific socket definitions go here to avoid
// polluting other source files with common #defines.
// ----------------------------------------------------------------------------
#if defined( PLATFORM_X360 )
#include <xtl.h>
#include <winsockx.h>
#elif defined( PLATFORM_WINDOWS_PC )
#include <winsock2.h>
#else
#error No build platform macro (PLATFORM_*) defined
#endif
inline SocketHandle_t GetSocketHandle( SOCKET socket )
{
return static_cast<SocketHandle_t>( socket );
}
inline SOCKET GetPlatformSocket( SocketHandle_t handle )
{
return static_cast<SOCKET>( handle );
}
static const SocketHandle_t InvalidSocketHandle = static_cast<SocketHandle_t>( INVALID_SOCKET );
// ----------------------------------------------------------------------------
// CSocketConnection
// Represents a client or server network connection.
//
// todo: Rework connection class to treat endpoints as standalone objects and unify
// interface with named pipes.
// ----------------------------------------------------------------------------
class CSocketConnection
{
public:
CSocketConnection();
~CSocketConnection();
SocketErrorCode_t Init( ConnectionType_t connectionType, SocketProtocol_t socketProtocol ); // Initializes the connection class as either client or server with specified protocol.
// Does not acquire any system resources.
void Cleanup(); // Cleans up the class and restores it to the default uninitialized state.
SocketErrorCode_t Listen( uint16 localPort, int numAllowedConnections ); // (Server-only) Listens for active client connections.
SocketErrorCode_t TryAcceptIncomingConnection( int *newEndpointIndex ); // (Server-only) Attempts to accept an incoming connection, if one is available.
int GetFirstAvailableListeningEndpoint(); // (Server-only) Gets the index of the first endpoint which is listening for connections.
SocketState_t GetListeningSocketState(); // (Server-only) Gets the status of the listening socket on a server.
SocketErrorCode_t ConnectToServer( const char *hostName, uint16 hostPort ); // (Client-only) Connects to a remote host using endpoint 0.
// In most cases, the status of endpoint 0 will be SSTATE_CONNECTION_IN_PROGRESS.
// The caller must periodically call PollClientConnectionState until the socket is connected before it is usable.
SocketErrorCode_t PollClientConnectionState( bool *isConnected ); // (Client-only) Checks the status of endpoint 0 to see if it is connected successfully to a server.
SocketState_t GetEndpointSocketState( int endpointIndex ); // Gets the status of the specified endpoint. (Clients only use endpoint 0.)
SocketErrorCode_t CanReadFromEndpoint( int endpointIndex, bool *canRead ); // Gets a value indicating whether the endpoint can be successfully read from.
SocketErrorCode_t CanWriteToEndpoint( int endpointIndex, bool *canWrite ); // Gets a value indicating whether the endpoint can be successfully written to.
SocketErrorCode_t ReadFromEndpoint( int endpointIndex, byte *destinationBuffer, int bufferSize, int *bytesRead ); // Reads data from the specified endpoint.
SocketErrorCode_t WriteToEndpoint( int endpointIndex, byte *sourceBuffer, int bufferSize, int *bytesWritten ); // Writes data to the specified endpoint.
const char* GetLastSystemErrorString() const; // Gets a string containing the last underlying system error reported.
// Only valid immediately after a function returns an unsuccessful error code.
const char* GetLastErrorString() const; // Gets a string containing the last underlying system error reported.
SocketHandle_t GetListeningSocket() const;
SocketHandle_t GetEndpointSocket( int nEndpointIndex ) const;
void ResetEndpoint( int endpointIndex );
SocketErrorCode_t SetSocketOpt( int endpointIndex, int level, int optname, const void *optval, int optlen );
private:
SocketHandle_t CreateNewSocket();
SocketHandle_t m_ListeningSocket;
SocketHandle_t m_EndpointSockets[ MAX_SERVER_CONNECTIONS ];
SocketState_t m_ListeningSocketState;
SocketState_t m_EndpointStates[ MAX_SERVER_CONNECTIONS ];
ConnectionType_t m_ConnectionType;
SocketProtocol_t m_SocketProtocol;
SocketErrorCode_t m_LastError;
int m_LastSystemError;
};
//-----------------------------------------------------------------------------
// Inline functions from CSocketConnection
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
inline SocketHandle_t CSocketConnection::GetListeningSocket() const
{
return m_ListeningSocket;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
inline SocketHandle_t CSocketConnection::GetEndpointSocket( int nEndpointIndex ) const
{
AssertMsg( nEndpointIndex >= 0 && nEndpointIndex < MAX_SERVER_CONNECTIONS, "Endpoint index out of range" );
return m_EndpointSockets[ nEndpointIndex ];
}
// ----------------------------------------------------------------------------
// Start up and shutdown functions
// Calls to these functions have to bracket any usage of this socket code
// ----------------------------------------------------------------------------
void SocketLibInit();
void SocketLibShutdown();
// Handy conversion to text of those nasty HRESULT codes
const char* ConvertWinsockErrorToString( int errorCode );
const char* ConvertSocketLibErrorToString( SocketErrorCode_t errorCode );
// Bitwise representation of a network message header.
struct MessageHeader_t
{
uint32 m_nLength;
};
// Byte swaps (in-place) a message header between system byte-order and network byte-order.
void ByteSwapInPlaceMessageHeader( MessageHeader_t* messageHeader );
// ----------------------------------------------------------------------------
// Signature of a callback invoked once per message parsed by the CSocketMessageBuilder::FeedData function.
// header = Header of the message read.
// message = Message payload (m_nLength is given by the header)
// userContext = Context provided by caller to FeedData
// ----------------------------------------------------------------------------
typedef void ( *NetworkMessageHandler )( const MessageHeader_t& header, const byte* message, void* userContext );
// ----------------------------------------------------------------------------
// Helper class to parse messages from a stream source.
// This class maintains state between successive calls to FeedData so that partial messages
// can be stored until fully parsed and dispatched.
// ----------------------------------------------------------------------------
class CSocketMessageBuilder
{
public:
CSocketMessageBuilder( int initialSize = 0, int growSize = 0 );
~CSocketMessageBuilder();
void SetMaxExpectedMsgSize( int expectedSize );
// Parses the given stream data and fires a callback for each full message parsed.
// This function has the side-effect of updating the CSocketMessageBuilder's internal parsing state
// so that partial messages can be glued together.
void FeedData( const void *data, int dataLength, NetworkMessageHandler networkMessageHandlerFunc, void *userContext );
void AssignConnection( CSocketConnection* pConnection, int endpoint );
SocketErrorCode_t SendDataPacket( const void* RESTRICT data, int dataLength );
SocketErrorCode_t SendDataPacket( CSocketConnection* pConnection, int endpoint, const void* RESTRICT data, int dataLength );
SocketErrorCode_t BeginSendPartialDataPacket( uint32 totalSize, const void* RESTRICT data, int dataLength );
SocketErrorCode_t BeginSendPartialDataPacket( CSocketConnection* pConnection, int endpoint, uint32 totalSize, const void* RESTRICT data, int dataLength );
bool WaitForIncomingMessage( DWORD timeOutValue );
void* GetIncomingMessageData();
uint32 GetIncomingMessageLen();
void FeedDataManual( const void* RESTRICT data, int dataLength );
bool HasCompleteMessageManual( );
bool GetCurrentMessageManual( void*& msgData, uint32& msgSize );
bool DiscardCurrentMessageManual();
bool IsSendingPartialMessage() { return m_bSendingPartialMessage; }
int32 PartialMessageBytesRemaining() { return m_bSendingPartialMessage ? m_PartialMessageBytesTotal - m_PartialMessageBytesSent : 0; }
private:
MessageHeader_t m_MessageHeader;
uint32 m_nHeaderBytesRead;
uint32 m_nMessageBytesRead;
CSocketConnection* m_pConnection;
int m_nConnectionEndpoint;
CUtlBuffer m_MessageData;
uint32 m_PartialMessageBytesSent; // used to combine multiple calls to SendDataPacket/BeginSendPartialDataPacket into one message packet
uint32 m_PartialMessageBytesTotal;
bool m_bSendingPartialMessage;
int m_nRecvBufSize;
byte* m_pRecvBuf;
bool m_bSwappedHeader;
};
#endif // SOCKETLIB_H
|