Documentation for the Unity C# Library
Loading...
Searching...
No Matches
ApexWebsocket.cs
Go to the documentation of this file.
1using UnityEngine;
2using System;
3using System.Collections.Generic;
4using System.Net.WebSockets;
5using System.Threading.Tasks;
6using System.Text;
8
9namespace PixoVR.Apex
10{
11 public enum ApexWebsocketRequestType : uint
12 {
14 }
15
16 public class ApexWebsocket
17 {
22
23 ClientWebSocket WebSocket;
25 Queue<ApexWebsocketRequestType> PendingWebsocketRequests = new Queue<ApexWebsocketRequestType>();
26 Queue<string> PendingReceiveResults = new Queue<string>();
27
28 public bool IsConnected()
29 {
30 if (WebSocket == null)
31 return false;
32
33 return WebSocket.State == WebSocketState.Open;
34 }
35
36 public void Update()
37 {
38 while(PendingReceiveResults.Count > 0)
39 {
40 OnReceive.Invoke(PendingReceiveResults.Dequeue());
41 }
42 }
43
44 public async Task<bool> Connect(Uri endpoint, int attemptTries = 3)
45 {
46 Debug.Log("Connecting websocket to endpoint " + endpoint.ToString());
47 if (WebSocket == null)
48 {
49 WebSocket = new ClientWebSocket();
50 WebSocket.Options.AddSubProtocol("wss");
51 }
52
53 ServerEndpoint = endpoint;
54
55 var tokenSource = new System.Threading.CancellationTokenSource();
56 tokenSource.CancelAfter(10000);
57
58 string connectionMessage = "";
59
60 int currentAttempt = 0;
61 while(currentAttempt < attemptTries)
62 {
63 try
64 {
65 await WebSocket.ConnectAsync(ServerEndpoint, tokenSource.Token);
66 }
67 catch(Exception ex)
68 {
69 if(!(ex is WebSocketException))
70 {
71 // Silently give up here because of an awful exception happening.
72 Debug.LogError(ex);
73 currentAttempt = attemptTries;
74 }
75
76 connectionMessage = ex.Message;
77 WebSocket = new ClientWebSocket();
78 }
79
80 if (WebSocket.State == WebSocketState.Open)
81 {
82 break;
83 }
84
85 currentAttempt++;
86 Debug.LogWarning("Failed to connect on attempt " + currentAttempt + ".");
87 await Task.Delay(5000);
88 }
89
90 Debug.Log("Web socket connection attempt has occured.");
91 if (WebSocket.State != WebSocketState.Open)
92 {
93 OnConnectFailed.Invoke(connectionMessage);
94 return false;
95 }
96
97 Debug.Log("Web socket connection was successful.");
98 OnConnectSuccess.Invoke();
99
102
103 // Return based on websocket state because ReceiveAsync can trigger a WebSocket to abort in a weird scenario.
104 return WebSocket.State == WebSocketState.Open;
105 }
106
108 {
109 Debug.Log("Message Sent");
111 }
112
114 {
115 PendingWebsocketRequests.Enqueue(ApexWebsocketRequestType.AuthorizationCode);
116
117 if (WebSocket != null && WebSocket.State == WebSocketState.Open)
118 {
120 return true;
121 }
122
123 return false;
124 }
125
127 {
128 if (PendingWebsocketRequests.Count > 0)
129 {
130 var NextRequestType = PendingWebsocketRequests.Dequeue();
131
132 switch (NextRequestType)
133 {
134 case ApexWebsocketRequestType.AuthorizationCode:
135 {
136 Debug.Log("Sending Auth Code Request");
137 SendString("{\"action\": \"authcode\" }");
138 }
139 break;
140 default:
141 {
142 Debug.LogError("Invalid request type submitted of type " + NextRequestType);
143 }
144 break;
145 }
146 }
147 }
148
150 {
151 Task.Run(() => Receive());
152 }
153
154 void SendString(string message)
155 {
156 Debug.Log("Sending string: " + message);
157 byte[] messageArray = Encoding.UTF8.GetBytes(message);
158 Task.Run(() => Send(messageArray));
159 }
160
161 async void Send(byte[] message)
162 {
163 Debug.Log("Sending data of length " + message.Length);
164 bool socketClosed = (WebSocket.State != WebSocketState.Open);
165 bool messageSent = true;
166
167 if(!socketClosed)
168 {
169 try
170 {
171 var buffer = new ArraySegment<Byte>(message, 0, message.Length);
172 var cancelTokenSource = new System.Threading.CancellationTokenSource();
173 cancelTokenSource.CancelAfter(10000);
174 await WebSocket.SendAsync(buffer, WebSocketMessageType.Text, true, cancelTokenSource.Token);
175 }
176 catch (Exception ex)
177 {
178 Debug.LogWarning("Websocket send stopped with exception of type " + ex.GetType().Name);
179 // WebSocketException means likely that the socket was aborted.
180 if (ex is OperationCanceledException || WebSocket.CloseStatus.HasValue)
181 {
182 Debug.LogWarning("Websocket closed because " + ex.Message);
183 }
184 else if (ex is WebSocketException)
185 {
186 await CloseSocket();
187 }
188
189 messageSent = false;
190 }
191 }
192
193 if(messageSent)
194 {
196 }
197 }
198
199 async void Receive()
200 {
201 bool socketClosed = (WebSocket.State != WebSocketState.Open);
202
203 byte[] receiveBuffer = new byte[2048];
204 int bufferOffset = 0;
205 int maxPacketSize = 256;
206 while (!socketClosed)
207 {
208 bool finishedReceiving = false;
209 while (!finishedReceiving)
210 {
211 try
212 {
213 ArraySegment<byte> bytesReceived = new ArraySegment<byte>(receiveBuffer, bufferOffset, maxPacketSize);
214
215 // No cancellation token because cancelling destroys the socket.
216 WebSocketReceiveResult result = await WebSocket.ReceiveAsync(bytesReceived, System.Threading.CancellationToken.None);
217
218 //Partial data received
219 Debug.Log("Data: " + Encoding.UTF8.GetString(receiveBuffer, bufferOffset, result.Count));
220
221 bufferOffset += result.Count;
222 if (result.EndOfMessage)
223 {
224 finishedReceiving = true;
225 PendingReceiveResults.Enqueue(Encoding.UTF8.GetString(receiveBuffer, 0, bufferOffset));
226 Array.Clear(receiveBuffer, 0, 2048);
227 bufferOffset = 0;
228 }
229 }
230 catch (Exception ex)
231 {
232 Debug.LogWarning("Websocket receive stopped with exception of type " + ex.GetType().Name);
233 // WebSocketException means likely that the socket was aborted.
234 if (ex is OperationCanceledException || WebSocket.CloseStatus.HasValue)
235 {
236 Debug.LogWarning("Websocket closed.");
237 finishedReceiving = socketClosed = true;
238 }
239 else if(ex is WebSocketException)
240 {
241 Debug.LogWarning("Websocket closed because " + ex.Message);
242 await CloseSocket();
243 }
244 else
245 {
246 break;
247 }
248 }
249 }
250
251 socketClosed = WebSocket.CloseStatus.HasValue;
252 }
253 }
254
255 public async Task CloseSocket()
256 {
257 if(WebSocket != null)
258 {
260 var oldWebSocket = WebSocket;
261 WebSocket = null;
262 await oldWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Shutting down the websocket.", System.Threading.CancellationToken.None);
263 OnClosed.Invoke(WebSocketCloseStatus.NormalClosure);
264 }
265 }
266
268 {
269 var socketClose = CloseSocket();
270 }
271 }
272}
void SendString(string message)
async void Send(byte[] message)
Queue< ApexWebsocketRequestType > PendingWebsocketRequests
async Task< bool > Connect(Uri endpoint, int attemptTries=3)
OnWebSocketReceive OnReceive
ClientWebSocket WebSocket
OnWebSocketConnectFailed OnConnectFailed
OnWebSocketClosed OnClosed
Queue< string > PendingReceiveResults
OnWebSocketConnectSuccessful OnConnectSuccess