/// <summary> /// To be used as the callback for accepting a new client that was initiated by StartServer, and /// continues an event-loop to accept additional clients. /// /// Uses EndAcceptSocket to finalize the connection and create a new SocketState. The SocketState's /// OnNetworkAction should be set to the delegate that was passed to StartServer. /// Then invokes the OnNetworkAction delegate with the new SocketState so the user can take action. /// /// If anything goes wrong during the connection process (such as the server being stopped externally), /// the OnNetworkAction delegate should be invoked with a new SocketState with its ErrorOccured flag set to true /// and an appropriate message placed in its ErrorMessage field. The event-loop should not continue if /// an error occurs. /// /// If an error does not occur, after invoking OnNetworkAction with the new SocketState, an event-loop to accept /// new clients should be continued by calling BeginAcceptSocket again with this method as the callback. /// </summary> /// <param name="ar">The object asynchronously passed via BeginAcceptSocket. It must contain a tuple with /// 1) a delegate so the user can take action (a SocketState Action), and 2) the TcpListener</param> private static void AcceptNewClient(IAsyncResult ar) { Tuple <Action <SocketState>, TcpListener> temp = (Tuple <Action <SocketState>, TcpListener>)ar.AsyncState; try { SocketState ss = new SocketState(temp.Item1, temp.Item2.EndAcceptSocket(ar)); ss.OnNetworkAction(ss); temp.Item2.BeginAcceptSocket(AcceptNewClient, temp); } catch { SocketState ss = new SocketState(temp.Item1, null); ss.ErrorOccured = true; ss.ErrorMessage = "Connection Failed"; ss.OnNetworkAction(ss); } }
/// <summary> /// To be used as the callback for accepting a new client that was initiated by StartServer, and /// continues an event-loop to accept additional clients. /// /// Uses EndAcceptSocket to finalize the connection and create a new SocketState. The SocketState's /// OnNetworkAction should be set to the delegate that was passed to StartServer. /// Then invokes the OnNetworkAction delegate with the new SocketState so the user can take action. /// /// If anything goes wrong during the connection process (such as the server being stopped externally), /// the OnNetworkAction delegate should be invoked with a new SocketState with its ErrorOccured flag set to true /// and an appropriate message placed in its ErrorMessage field. The event-loop should not continue if /// an error occurs. /// /// If an error does not occur, after invoking OnNetworkAction with the new SocketState, an event-loop to accept /// new clients should be continued by calling BeginAcceptSocket again with this method as the callback. /// </summary> /// <param name="ar">The object asynchronously passed via BeginAcceptSocket. It must contain a tuple with /// 1) a delegate so the user can take action (a SocketState Action), and 2) the TcpListener</param> private static void AcceptNewClient(IAsyncResult ar) { Console.WriteLine("contact from client"); // Grabbing Tuple that has the Action<SocketState> and TCPListener from StartServer method. Tuple <Action <SocketState>, TcpListener> tuple = (Tuple <Action <SocketState>, TcpListener>)ar.AsyncState; // Grabbing the new client to be added try { Socket newClient = listener.EndAcceptSocket(ar); SocketState state = new SocketState(tuple.Item1, newClient); state.OnNetworkAction(state); // Keep track of the client so we can broadcast to all of them lock (clients) { clients.Add(newClient); } } // Error Finalizing connection / Calling OnNetworkAction catch { SocketState ssError = new SocketState(tuple.Item1, null); ssError.ErrorOccured = true; ssError.ErrorMessage = "Error while trying to connect."; ssError.OnNetworkAction(ssError); } try { // resumes an accept loop listener.BeginAcceptSocket(AcceptNewClient, tuple); } catch { // Do nothing } }
/// <summary> /// To be used as the callback for accepting a new client that was initiated by StartServer, and /// continues an event-loop to accept additional clients. /// /// Uses EndAcceptSocket to finalize the connection and create a new SocketState. The SocketState's /// OnNetworkAction should be set to the delegate that was passed to StartServer. /// Then invokes the OnNetworkAction delegate with the new SocketState so the user can take action. /// /// If anything goes wrong during the connection process (such as the server being stopped externally), /// the OnNetworkAction delegate should be invoked with a new SocketState with its ErrorOccured flag set to true /// and an appropriate message placed in its ErrorMessage field. The event-loop should not continue if /// an error occurs. /// /// If an error does not occur, after invoking OnNetworkAction with the new SocketState, an event-loop to accept /// new clients should be continued by calling BeginAcceptSocket again with this method as the callback. /// </summary> /// <param name="ar">The object asynchronously passed via BeginAcceptSocket. It must contain a tuple with /// 1) a delegate so the user can take action (a SocketState Action), and 2) the TcpListener</param> private static void AcceptNewClient(IAsyncResult ar) { try { Tuple <Action <SocketState>, TcpListener> args = (Tuple <Action <SocketState>, TcpListener>)ar.AsyncState; Action <SocketState> toCall = args.Item1; //get toCall delegate TcpListener listener = args.Item2; //get TcpListener we have initialized in StartServer Socket client = listener.EndAcceptSocket(ar); // finalize connection SocketState soc_state = new SocketState(toCall, client); soc_state.OnNetworkAction(soc_state); // if nothing goes wrong, then starting accepting socket again listener.BeginAcceptSocket(AcceptNewClient, args); } catch (Exception e) { SocketState soc_state = new SocketState(s => { }, null); // should be fine passed null soc_state.OnNetworkAction(soc_state); soc_state.ErrorOccured = true; soc_state.ErrorMessage = e.Message; } }
///////////////////////////////////////////////////////////////////////////////////////// // Server-Side Code ///////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Starts a TcpListener on the specified port and starts an event-loop to accept new clients. /// The event-loop is started with BeginAcceptSocket and uses AcceptNewClient as the callback. /// AcceptNewClient will continue the event-loop. /// </summary> /// <param name="toCall">The method to call when a new connection is made</param> /// <param name="port">The the port to listen on</param> public static TcpListener StartServer(Action <SocketState> toCall, int port) { TcpListener listener = new TcpListener(IPAddress.Any, port); Tuple <Action <SocketState>, TcpListener> temp = new Tuple <Action <SocketState>, TcpListener>(toCall, listener); try { listener.Start(); listener.BeginAcceptSocket(AcceptNewClient, temp); } catch { SocketState ss = new SocketState(toCall, null); ss.ErrorOccured = true; ss.ErrorMessage = "Connection Failed"; ss.OnNetworkAction(ss); } return(listener); }
/// <summary> /// To be used as the callback for finalizing a receive operation that was initiated by GetData. /// /// Uses EndReceive to finalize the receive. /// /// As stated in the GetData documentation, if an error occurs during the receive process, /// either this method or GetData (not both) should indicate the error appropriately. /// /// If data is successfully received: /// (1) Read the characters as UTF8 and put them in the SocketState's unprocessed data buffer (its string builder). /// This must be done in a thread-safe manner with respect to the SocketState methods that access or modify its /// string builder. /// (2) Call the saved delegate (OnNetworkAction) allowing the user to deal with this data. /// </summary> /// <param name="ar"> /// This contains the SocketState that is stored with the callback when the initial BeginReceive is called. /// </param> private static void ReceiveCallback(IAsyncResult ar) { SocketState state = (SocketState)ar.AsyncState; try { int numBytes = state.TheSocket.EndReceive(ar); //finalizes recieve process if (numBytes == 0) { throw new Exception("Socket closed during data transfer"); } lock (state.data) { state.data.Append(Encoding.UTF8.GetString(state.buffer, 0, numBytes)); //appends data from buffer that is converted from bytes to UTF8 text } state.OnNetworkAction(state); } catch (Exception e) { HandleError(state.OnNetworkAction, e.Message); } }
/// <summary> /// To be used as the callback for accepting a new client that was initiated by StartServer, and /// continues an event-loop to accept additional clients. /// /// Uses EndAcceptSocket to finalize the connection and create a new SocketState. The SocketState's /// OnNetworkAction should be set to the delegate that was passed to StartServer. /// Then invokes the OnNetworkAction delegate with the new SocketState so the user can take action. /// /// If anything goes wrong during the connection process (such as the server being stopped externally), /// the OnNetworkAction delegate should be invoked with a new SocketState with its ErrorOccured flag set to true /// and an appropriate message placed in its ErrorMessage field. The event-loop should not continue if /// an error occurs. /// /// If an error does not occur, after invoking OnNetworkAction with the new SocketState, an event-loop to accept /// new clients should be continued by calling BeginAcceptSocket again with this method as the callback. /// </summary> /// <param name="ar">The object asynchronously passed via BeginAcceptSocket. It must contain a tuple with /// 1) a delegate so the user can take action (a SocketState Action), and 2) the TcpListener</param> private static void AcceptNewClient(IAsyncResult ar) { //initializes the variables used in this method SocketState socketState = null; Action <SocketState> action = null; TcpListener listener = null; Socket socket = null; try { //Creates a tuple from the given parameter and sets the listener and action to the given items Tuple <Action <SocketState>, TcpListener> tuplePassed = (Tuple <Action <SocketState>, TcpListener>)ar.AsyncState; action = tuplePassed.Item1; listener = tuplePassed.Item2; socket = listener.EndAcceptSocket(ar); //creates a new socketstate with the socketstate and the socket from the tuple socketState = new SocketState(action, socket); } //Catches any Exception Thrown and creates a new scoket state to nofify the user catch (Exception e) { socketState = new SocketState(action, socket); socketState.ErrorOccured = true; socketState.ErrorMessage = e.Message; action(socketState); return; } socketState.OnNetworkAction(socketState); try { //starts accepting new clients from the listener inside of the tuple Tuple <Action <SocketState>, TcpListener> tuple = new Tuple <Action <SocketState>, TcpListener>(action, listener); listener.BeginAcceptSocket(AcceptNewClient, tuple); } catch (Exception e) { } }
/// <summary> /// To be used as the callback for finalizing a receive operation that was initiated by GetData. /// /// Uses EndReceive to finalize the receive. /// /// As stated in the GetData documentation, if an error occurs during the receive process, /// either this method or GetData (not both) should indicate the error appropriately. /// /// If data is successfully received: /// (1) Read the characters as UTF8 and put them in the SocketState's unprocessed data buffer (its string builder). /// This must be done in a thread-safe manner with respect to the SocketState methods that access or modify its /// string builder. /// (2) Call the saved delegate (OnNetworkAction) allowing the user to deal with this data. /// </summary> /// <param name="ar"> /// This contains the SocketState that is stored with the callback when the initial BeginReceive is called. /// </param> private static void ReceiveCallback(IAsyncResult ar) { SocketState socketState = (SocketState)ar.AsyncState; try { int numBytesReceived = socketState.TheSocket.EndReceive(ar); if (numBytesReceived > 0) { lock (socketState.data) { socketState.data.Append(Encoding.UTF8.GetString(socketState.buffer, 0, numBytesReceived)); } socketState.OnNetworkAction(socketState); } else { // if we receive 0 bytes, that's an error. throw new NetworkingException("received 0 bytes"); } } catch (Exception e) { HandleError(socketState, e); } }
/// <summary> /// To be used as the callback for accepting a new client that was initiated by StartServer, and /// continues an event-loop to accept additional clients. /// /// Uses EndAcceptSocket to finalize the connection and create a new SocketState. The SocketState's /// OnNetworkAction should be set to the delegate that was passed to StartServer. /// Then invokes the OnNetworkAction delegate with the new SocketState so the user can take action. /// /// If anything goes wrong during the connection process (such as the server being stopped externally), /// the OnNetworkAction delegate should be invoked with a new SocketState with its ErrorOccured flag set to true /// and an appropriate message placed in its ErrorMessage field. The event-loop should not continue if /// an error occurs. /// /// If an error does not occur, after invoking OnNetworkAction with the new SocketState, an event-loop to accept /// new clients should be continued by calling BeginAcceptSocket again with this method as the callback. /// </summary> /// <param name="ar">The object asynchronously passed via BeginAcceptSocket. It must contain a tuple with /// 1) a delegate so the user can take action (a SocketState Action), and 2) the TcpListener</param> private static void AcceptNewClient(IAsyncResult ar) { Tuple <Action <SocketState>, TcpListener> tuple = (Tuple <Action <SocketState>, TcpListener>)ar.AsyncState; TcpListener listener = tuple.Item2; Action <SocketState> toCall = tuple.Item1; try { SocketState socketState = new SocketState(toCall, listener.EndAcceptSocket(ar)); socketState.OnNetworkAction(socketState); Tuple <Action <SocketState>, TcpListener> tcpInfo = new Tuple <Action <SocketState>, TcpListener>(toCall, listener); listener.BeginAcceptSocket(AcceptNewClient, tcpInfo); } catch { SocketState errorSocketState = new SocketState(toCall, null); errorSocketState.ErrorOccured = true; errorSocketState.ErrorMessage = "Error occurred while accepting a new client"; errorSocketState.OnNetworkAction(errorSocketState); } }
/// <summary> /// To be used as the callback for finalizing a connection process that was initiated by ConnectToServer. /// /// Uses EndConnect to finalize the connection. /// /// As stated in the ConnectToServer documentation, if an error occurs during the connection process, /// either this method or ConnectToServer (not both) should indicate the error appropriately. /// /// If a connection is successfully established, invokes the toCall Action that was provided to ConnectToServer (above) /// with a new SocketState representing the new connection. /// /// </summary> /// <param name="ar">The object asynchronously passed via BeginConnect</param> private static void ConnectedCallback(IAsyncResult ar) { SocketState state = (SocketState)ar.AsyncState; //Check if state is connected. if (!state.TheSocket.Connected) { //No Need to do anything else in this method. return; } try { state.TheSocket.EndConnect(ar); } catch (Exception e) { SendErrorMessage(new SocketState(state.OnNetworkAction, new Socket(IPAddress.None.AddressFamily, SocketType.Stream, ProtocolType.Tcp)), e.Message + "\nError Caught in ConnectedCallback."); return; } state.OnNetworkAction(state); }
///////////////////////////////////////////////////////////////////////////////////////// // Client-Side Code ///////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Begins the asynchronous process of connecting to a server via BeginConnect, /// and using ConnectedCallback as the method to finalize the connection once it's made. /// /// If anything goes wrong during the connection process, toCall should be invoked /// with a new SocketState with its ErrorOccured flag set to true and an appropriate message /// placed in its ErrorMessage field. Between this method and ConnectedCallback, toCall should /// only be invoked once on error. /// /// This connection process should timeout and produce an error (as discussed above) /// if a connection can't be established within 3 seconds of starting BeginConnect. /// /// </summary> /// <param name="toCall">The action to take once the connection is open or an error occurs</param> /// <param name="hostName">The server to connect to</param> /// <param name="port">The port on which the server is listening</param> public static void ConnectToServer(Action <SocketState> toCall, string hostName, int port) { // Establish the remote endpoint for the socket. IPHostEntry ipHostInfo; IPAddress ipAddress = IPAddress.None; // Determine if the server address is a URL or an IP try { ipHostInfo = Dns.GetHostEntry(hostName); bool foundIPV4 = false; foreach (IPAddress addr in ipHostInfo.AddressList) { if (addr.AddressFamily != AddressFamily.InterNetworkV6) { foundIPV4 = true; ipAddress = addr; break; } } // Didn't find any IPV4 addresses if (!foundIPV4) { SocketState errorSocketState = new SocketState(toCall, null); errorSocketState.ErrorOccured = true; errorSocketState.ErrorMessage = "Couldn't find any IPV4 addresses"; errorSocketState.OnNetworkAction(errorSocketState); return; } } catch (Exception) { // See if host name is a valid ipaddress try { ipAddress = IPAddress.Parse(hostName); } catch (Exception) { SocketState errorSocketState = new SocketState(toCall, null); errorSocketState.ErrorOccured = true; errorSocketState.ErrorMessage = "Host name isn't a valid ipaddress"; errorSocketState.OnNetworkAction(errorSocketState); } } // Create a TCP/IP socket. Socket socket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); // This disables Nagle's algorithm (google if curious!) // Nagle's algorithm can cause problems for a latency-sensitive // game like ours will be socket.NoDelay = true; try { SocketState TheServer = new SocketState(toCall, socket); IAsyncResult result = TheServer.TheSocket.BeginConnect(ipAddress, port, ConnectedCallback, TheServer); bool success = result.AsyncWaitHandle.WaitOne(3000, true); if (!TheServer.TheSocket.Connected) { TheServer.TheSocket.Close(); } } catch { SocketState errorSocketState = new SocketState(toCall, null); errorSocketState.ErrorOccured = true; errorSocketState.ErrorMessage = "An error occured while connecting to server"; errorSocketState.OnNetworkAction(errorSocketState); } }
/// <summary> /// indicate an error to the user by invoking the OnNetworkAction delegate with an error socket state. /// </summary> public static void HandleError(SocketState socketState, Exception e) { socketState = SetSocketStateError(socketState, e.Message); socketState.OnNetworkAction(socketState); }
///////////////////////////////////////////////////////////////////////////////////////// // Client-Side Code ///////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Begins the asynchronous process of connecting to a server via BeginConnect, /// and using ConnectedCallback as the method to finalize the connection once it's made. /// /// If anything goes wrong during the connection process, toCall should be invoked /// with a new SocketState with its ErrorOccured flag set to true and an appropriate message /// placed in its ErrorMessage field. Between this method and ConnectedCallback, toCall should /// only be invoked once on error. /// /// This connection process should timeout and produce an error (as discussed above) /// if a connection can't be established within 3 seconds of starting BeginConnect. /// /// </summary> /// <param name="toCall">The action to take once the connection is open or an error occurs</param> /// <param name="hostName">The server to connect to</param> /// <param name="port">The port on which the server is listening</param> public static void ConnectToServer(Action <SocketState> toCall, string hostName, int port) { // TODO: This method is incomplete, but contains a starting point // for decoding a host address // Establish the remote endpoint for the socket. IPHostEntry ipHostInfo; IPAddress ipAddress = IPAddress.None; SocketState temp = new SocketState(toCall, null); // Determine if the server address is a URL or an IP try { ipHostInfo = Dns.GetHostEntry(hostName); bool foundIPV4 = false; foreach (IPAddress addr in ipHostInfo.AddressList) { if (addr.AddressFamily != AddressFamily.InterNetworkV6) { foundIPV4 = true; ipAddress = addr; break; } } // Didn't find any IPV4 addresses if (!foundIPV4) { temp.ErrorOccured = true; temp.ErrorMessage = "Couldn't find IPv4"; temp.OnNetworkAction(temp); } } catch (Exception) { // see if host name is a valid ipaddress try { ipAddress = IPAddress.Parse(hostName); } catch (Exception) { temp.ErrorOccured = true; temp.ErrorMessage = "Host name not valid"; temp.OnNetworkAction(temp); } } // Create a TCP/IP socket. Socket socket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); // This disables Nagle's algorithm (google if curious!) // Nagle's algorithm can cause problems for a latency-sensitive // game like ours will be socket.NoDelay = true; temp = new SocketState(toCall, socket); try { bool success = socket.BeginConnect(ipAddress, port, ConnectedCallback, temp).AsyncWaitHandle.WaitOne(3000, true); if (!success) { temp.ErrorOccured = true; temp.ErrorMessage = "Connection Timeout Error"; temp.OnNetworkAction(temp); } } catch { temp.ErrorOccured = true; temp.ErrorMessage = "Connection Failed"; temp.OnNetworkAction(temp); } }
///////////////////////////////////////////////////////////////////////////////////////// // Helper Methods ///////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Sets the ErrorOccured flag on a given SocketState to true, sets the SocketState's error message, /// then, invokes the SocketState's OnNetworkAction. /// </summary> /// <param name="errorState">The state to send the error on</param> /// <param name="message">The error message</param> private static void SendErrorMessage(SocketState errorState, string message) { errorState.ErrorOccured = true; errorState.ErrorMessage = message; errorState.OnNetworkAction(errorState); }
///////////////////////////////////////////////////////////////////////////////////////// // Client-Side Code ///////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Begins the asynchronous process of connecting to a server via BeginConnect, /// and using ConnectedCallback as the method to finalize the connection once it's made. /// /// If anything goes wrong during the connection process, toCall should be invoked /// with a new SocketState with its ErrorOccured flag set to true and an appropriate message /// placed in its ErrorMessage field. Between this method and ConnectedCallback, toCall should /// only be invoked once on error. /// /// This connection process should timeout and produce an error (as discussed above) /// if a connection can't be established within 3 seconds of starting BeginConnect. /// /// /// ADDED: Ref for Connection timeout: /// https://stackoverflow.com/questions/17118632/how-to-set-the-timeout-for-a-tcpclient /// /// </summary> /// <param name="toCall">The action to take once the connection is open or an error occurs</param> /// <param name="hostName">The server to connect to</param> /// <param name="port">The port on which the server is listening</param> public static void ConnectToServer(Action <SocketState> toCall, string hostName, int port) { // Establish the remote endpoint for the socket. IPHostEntry ipHostInfo; IPAddress ipAddress = IPAddress.None; // Determine if the server address is a URL or an IP try { ipHostInfo = Dns.GetHostEntry(hostName); bool foundIPV4 = false; foreach (IPAddress addr in ipHostInfo.AddressList) { if (addr.AddressFamily != AddressFamily.InterNetworkV6) { foundIPV4 = true; ipAddress = addr; break; } } // Didn't find any IPV4 addresses if (!foundIPV4) { SocketState ssError = new SocketState(toCall, new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp)); ssError.ErrorOccured = true; ssError.ErrorMessage = "Was unable to identify an IPV4 address."; ssError.OnNetworkAction(ssError); } } catch (Exception) { // see if host name is a valid ipaddress try { ipAddress = IPAddress.Parse(hostName); } catch (Exception) { SocketState ssError = new SocketState(toCall, new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp)); ssError.ErrorOccured = true; ssError.ErrorMessage = "Invalid IPAddress."; ssError.OnNetworkAction(ssError); } } // Create a TCP/IP socket. Socket socket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); // This disables Nagle's algorithm (google if curious!) // Nagle's algorithm can cause problems for a latency-sensitive // game like ours will be socket.NoDelay = true; // Creates a new Socket for data result SocketState state = new SocketState(toCall, socket); // Attempting BeginConnect try { IAsyncResult result = state.TheSocket.BeginConnect(ipAddress, port, ConnectedCallback, state); // Checking for timeout bool success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(3)); if (!success) { // Close the socket and report the error socket.Close(); SocketState TimeoutSS = new SocketState(toCall, new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp)); TimeoutSS.ErrorOccured = true; TimeoutSS.ErrorMessage = "Connection Timeout after 3 seconds."; } } catch (Exception) { // Do nothing } }
///////////////////////////////////////////////////////////////////////////////////////// // Client-Side Code ///////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Begins the asynchronous process of connecting to a server via BeginConnect, /// and using ConnectedCallback as the method to finalize the connection once it's made. /// /// If anything goes wrong during the connection process, toCall should be invoked /// with a new SocketState with its ErrorOccured flag set to true and an appropriate message /// placed in its ErrorMessage field. Between this method and ConnectedCallback, toCall should /// only be invoked once on error. /// /// This connection process should timeout and produce an error (as discussed above) /// if a connection can't be established within 3 seconds of starting BeginConnect. /// /// </summary> /// <param name="toCall">The action to take once the connection is open or an error occurs</param> /// <param name="hostName">The server to connect to</param> /// <param name="port">The port on which the server is listening</param> public static void ConnectToServer(Action <SocketState> toCall, string hostName, int port) { // Establish the remote endpoint for the socket. IPHostEntry ipHostInfo; IPAddress ipAddress = IPAddress.None; // Determine if the server address is a URL or an IP try { ipHostInfo = Dns.GetHostEntry(hostName); bool foundIPV4 = false; foreach (IPAddress addr in ipHostInfo.AddressList) { if (addr.AddressFamily != AddressFamily.InterNetworkV6) { foundIPV4 = true; ipAddress = addr; break; } } // Didn't find any IPV4 addresses if (!foundIPV4) { //Indicate an error to the user, as specified in the documentation SocketState errorState = new SocketState(toCall, new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp)); //Set error state and message errorState.ErrorOccured = true; errorState.ErrorMessage = "IPV4 addresses were not found"; //Run delegate with error flag errorState.OnNetworkAction(errorState); return; } } catch (Exception) { // see if host name is a valid ipaddress try { ipAddress = IPAddress.Parse(hostName); } catch (Exception) { //IP address was not found, so we create a new SocketState and set error flag to true SocketState invalidIpAddress = new SocketState(toCall, new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp)); invalidIpAddress.ErrorOccured = true; //Set the message and fire the SocketState delegate invalidIpAddress.ErrorMessage = "The IP address entered is not valid"; invalidIpAddress.OnNetworkAction(invalidIpAddress); return; } } try { // Create a TCP/IP socket. Socket socket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); //Create Tuple to hold information that needs to be passed to the callback Tuple <Socket, Action <SocketState> > info = new Tuple <Socket, Action <SocketState> >(socket, toCall); // This disables Nagle's algorithm (google if curious!) // Nagle's algorithm can cause problems for a latency-sensitive // game like ours will be socket.NoDelay = true; //Try catch for the timeout // Connect using a timeout (3 seconds) //Saves the result of the begin connect without invoking callback to verify timeout does not happen IAsyncResult result = socket.BeginConnect(ipAddress, port, null, info); // boolean flag to signal timeout, end connection if is not made after 3 seconds bool success = result.AsyncWaitHandle.WaitOne(3000, true); // Check if timeout condition has been met if (success) { //Call ConnectedCallBack with the info that was saved ConnectedCallback(result); } //Else we timed out and we should throw exception else { throw new Exception(); } } // Connection has timed out and we want to close the socket and produce an error catch (Exception) { Socket socket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); //Create new state with error flag set and call delegate SocketState timeoutState = new SocketState(toCall, socket); timeoutState.ErrorOccured = true; timeoutState.ErrorMessage = "Connection timed out"; timeoutState.OnNetworkAction(timeoutState); } }
/// <summary> /// Sets the passed in socket states ErrorOccured flag to true /// Sets the socket states error message to message /// Updates the users delegate with this state /// </summary> /// <param name="state"></param> private static void SetErrorState(SocketState state, string message) { state.ErrorOccured = true; state.ErrorMessage = message; state.OnNetworkAction(state); }