/// <summary> /// Processes incoming data and attempts to recreate the objects that were sent. /// Then raises the ReceiveProgress and/or DataArrival events. /// Runs in it's own thread to allow receiving to continue unhindered. /// </summary> private void ProcessIncoming() { ReceivedPacket packet = null; while (true) { // First lets get the incoming packet we are dealing with packet = null; lock (ReceivedPackets.SyncRoot) { if (ReceivedPackets.Count < 1) { IsProcessingIncomingData = false; return; } packet = ReceivedPackets.PopFront(); } if (Parent.LegacySupport) { // Legacy support is enabled, so just alert that it arrived lock (((ICollection)ReceivedBuffer).SyncRoot) ReceivedBuffer.Enqueue(packet.Data); Parent.OnDataArrival(Parent, new DataArrivalEventArgs(packet.Data.LongLength, packet.RemoteEndPoint)); continue; } // Let's get the header byte[] data = packet.Data; bool throwLegacyError = false; while (!Header.Completed && (packet.Data != null & packet.Data.Length > 0)) { if (!Header.ProcessHeader(ref data, ref ProcessingByteBuffer)) { throwLegacyError = true; break; } if (data == null || data.Length < 1) { break; } } // Check for header error if (throwLegacyError || (!Header.Completed && ProcessingByteBuffer.Count > 10)) { Parent.OnErrorReceived(Parent, ErrorReceivedEventArgs.Create(new WinsockException("Unable to determine the size of the incoming packet. You may need to turn on Legacy Support."))); Close(); break; } if (data != null) { int receivedSize = data.Length; if (Header.Completed && ProcessingByteBuffer.Count + data.Length >= Header.Size) { // We have the full object that was sent data = ProcessingByteBuffer.Combine(data); ProcessingByteBuffer.Clear(); byte[] objectData = null; if (data.Length > Header.Size) { // There is extra data here - get only what we need // then push the rest back on the queue objectData = ArrayMethods.Shrink(ref data, Header.Size); packet.Data = data; lock (ReceivedPackets.SyncRoot) ReceivedPackets.PushFront(packet); } else { objectData = data; } // Try converting the bytes back to the object. var receivedObject = ObjectPacker.Unpack(objectData); var receivedType = receivedObject.GetType(); if (receivedType == typeof(FileData) || receivedType == typeof(FileDataPart)) { // Looks like we are dealing with an incoming file // Handle the data and get a reference to the incoming file FileData file = null; try { file = (receivedType == typeof(FileData)) ? HandleIncomingFile((FileData)receivedObject) : HandleIncomingFile((FileDataPart)receivedObject); } catch (Exception ex) { Parent.OnErrorReceived(Parent, ex.AsEventArgs()); Close(); break; } // This part of the file is done, so we can // reset the header for the next object Header.Reset(); if (file != null) { // We've got the file, raise the events // and remove our in progress reference to the file Parent.OnReceiveProgress(Parent, new ReceiveProgressEventArgs(file.LastReceivedSize, file.ReceivedBytes, file.FileSize, packet.RemoteEndPoint)); if (file.ReceiveCompleted) { IncomingFiles.Remove(file.Guid); lock (((ICollection)ReceivedBuffer).SyncRoot) ReceivedBuffer.Enqueue(file); Parent.OnDataArrival(Parent, new DataArrivalEventArgs(file.FileSize, packet.RemoteEndPoint)); } } } else { // Incoming object was not a file (could be a byte[]) // Store it in the queue and raise the events lock (((ICollection)ReceivedBuffer).SyncRoot) ReceivedBuffer.Enqueue(receivedObject); Parent.OnReceiveProgress(Parent, new ReceiveProgressEventArgs(receivedSize, objectData.Length, Header.Size, packet.RemoteEndPoint)); Header.Reset(); Parent.OnDataArrival(Parent, new DataArrivalEventArgs(objectData.Length, packet.RemoteEndPoint)); } } else { // Either the header wasn't completed, or we haven't got // all of the object yet, either way we need more data // store what we've got into a temporary buffer ProcessingByteBuffer.Add(data); Parent.OnReceiveProgress(Parent, new ReceiveProgressEventArgs(receivedSize, ProcessingByteBuffer.Count, Header.Size, packet.RemoteEndPoint)); } } } // Exit the processing thread, and allow another one to be created lock (ReceivedPackets.SyncRoot) IsProcessingIncomingData = false; }
public static ErrorReceivedEventArgs AsEventArgs(this Exception ex, [CallerMemberName] string memberName = "") { return(ErrorReceivedEventArgs.Create(ex, memberName)); }
/// <summary> /// Establishes a connection to a remote host. /// </summary> /// <param name="remoteHostOrIp">A value containing the Hostname or IP address of the remote host.</param> /// <param name="remotePort">A value indicating the port on the remote host to connect to.</param> /// <param name="sslHost">The name of the host to validate the certificate for.</param> /// <param name="localIp">A value indicating the IP address the client should connect from.</param> /// <param name="localPort">A value indicating the port the client should connect from.</param> public async void Connect(string remoteHostOrIp, int remotePort, string sslHost, string localIp, int localPort) { if (Parent.State != State.Closed) { throw new WinsockException("Cannot connect to a remote host when not Closed."); } /** * First we need to make sure we have an IP address. * If not - we need to try and resolve the fully qualified domain. */ Parent.ChangeState(State.ResolvingHost); IPAddress resolvedRemoteIP = null, resolvedLocalIP = null; if (!IPAddress.TryParse(remoteHostOrIp, out resolvedRemoteIP)) { IPHostEntry entry = await Dns.GetHostEntryAsync(remoteHostOrIp); if (entry == null || entry.AddressList.Length == 0) { string name = (entry != null) ? entry.HostName : remoteHostOrIp; throw new WinsockException(string.Format("Hostname \"{0}\" could not be resolved.", name)); } resolvedRemoteIP = entry.AddressList[0]; } if (!string.IsNullOrWhiteSpace(localIp) && !IPAddress.TryParse(localIp, out resolvedLocalIP)) { throw new WinsockException(string.Format("The value \"{0}\" is not a valid IP address for local binding.", localIp)); } Parent.ChangeState(State.HostResolved); /** * Take our IP address and attempt to create the connection. * Upon successfull connections - different BeginReceives could be called * depending on if this was an attempt at a SECURE connection. */ IPEndPoint endPoint = new IPEndPoint(resolvedRemoteIP, remotePort); IPEndPoint localEndPoint = (resolvedLocalIP != null) ? new IPEndPoint(resolvedLocalIP, localPort) : null; socket = new AsyncSocket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); Parent.ChangeState(State.Connecting); bool connectFail = false; try { if (localEndPoint != null) { socket.Bind(localEndPoint); } await socket.ConnectAsync(endPoint); } catch (Exception ex) { connectFail = true; Parent.ChangeState(State.Closed); Parent.OnErrorReceived(Parent, ErrorReceivedEventArgs.Create(ex)); } if (!connectFail) { Parent.ChangeState(State.Connected); Parent.OnConnected(endPoint); if (sslHost != null) { BeginReceive(false, sslHost); } else { BeginReceive(false); } } }