public void FreeUDPBuffer(UDPPacketBuffer buf) { lock (m_udpBuffersPoolLock) { if (m_udpBuffersPoolPtr < 999) { buf.RemoteEndPoint = null; buf.DataLength = 0; m_udpBuffersPoolPtr++; m_udpBuffersPool[m_udpBuffersPoolPtr] = buf; } } }
/// <summary> /// Default constructor /// </summary> public OutgoingPacket(object client, byte[] buffer, int category, int dataSize, IPEndPoint destination, bool fromBufferPool, Packets.PacketType type) { SequenceNumber = 0; ResendCount = 0; TickCount = 0; Client = client; Category = category; _fromPool = fromBufferPool; Type = type; DataSize = dataSize; Buffer = new UDPPacketBuffer(buffer, dataSize, destination, category, fromBufferPool); }
public UDPPacketBuffer GetNewUDPBuffer(IPEndPoint remoteEndpoint) { lock (m_udpBuffersPoolLock) { if (m_udpBuffersPoolPtr >= 0) { UDPPacketBuffer buf = m_udpBuffersPool[m_udpBuffersPoolPtr]; m_udpBuffersPool[m_udpBuffersPoolPtr] = null; m_udpBuffersPoolPtr--; buf.RemoteEndPoint = remoteEndpoint; return(buf); } } return(new UDPPacketBuffer(remoteEndpoint)); }
public void SendPacketData(byte[] data, int dataLength, PacketType type, bool doZerocode) { UDPPacketBuffer buffer = new UDPPacketBuffer(remoteEndPoint, Packet.MTU); // Zerocode if needed if (doZerocode) { try { dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); } catch (IndexOutOfRangeException) { // The packet grew larger than Packet.MTU bytes while zerocoding. // Remove the MSG_ZEROCODED flag and send the unencoded data // instead data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } } else { Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } buffer.DataLength = dataLength; #region Queue or Send NetworkManager.OutgoingPacket outgoingPacket = new NetworkManager.OutgoingPacket(this, buffer); // Send ACK and logout packets directly, everything else goes through the queue if (Client.Settings.THROTTLE_OUTGOING_PACKETS == false || type == PacketType.PacketAck || type == PacketType.LogoutRequest) { SendPacketFinal(outgoingPacket); } else { Network.PacketOutbox.Enqueue(outgoingPacket); } #endregion Queue or Send #region Stats Tracking if (Client.Settings.TRACK_UTILIZATION) { Client.Stats.Update(type.ToString(), OpenMetaverse.Stats.Type.Packet, dataLength, 0); } #endregion }
private void AsyncEndReceive(IAsyncResult iar) { // Asynchronous receive operations will complete here through the call // to AsyncBeginReceive if (!m_shutdownFlag) { // Asynchronous mode will start another receive before the // callback for this packet is even fired. Very parallel :-) if (m_asyncPacketHandling) { AsyncBeginReceive(); } // get the buffer that was created in AsyncBeginReceive // this is the received data //WrappedObject<UDPPacketBuffer> wrappedBuffer = (WrappedObject<UDPPacketBuffer>)iar.AsyncState; //UDPPacketBuffer buffer = wrappedBuffer.Instance; UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; try { // get the length of data actually read from the socket, store it with the // buffer buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); // call the abstract method PacketReceived(), passing the buffer that // has just been filled from the socket read. PacketReceived(buffer); } catch (SocketException) { } catch (ObjectDisposedException) { } finally { //wrappedBuffer.Dispose(); // Synchronous mode waits until the packet callback completes // before starting the receive to fetch another packet if (!m_asyncPacketHandling) { AsyncBeginReceive(); } } } }
/// <summary> /// Disconnect from this simulator /// </summary> public void Disconnect(bool sendCloseCircuit) { if (connected) { connected = false; // Destroy the timers if (AckTimer != null) { AckTimer.Dispose(); } if (StatsTimer != null) { StatsTimer.Dispose(); } if (PingTimer != null) { PingTimer.Dispose(); } AckTimer = null; StatsTimer = null; PingTimer = null; // Kill the current CAPS system if (Caps != null) { Caps.Disconnect(true); Caps = null; } if (sendCloseCircuit) { // Try to send the CloseCircuit notice CloseCircuitPacket close = new CloseCircuitPacket(); UDPPacketBuffer buf = new UDPPacketBuffer(remoteEndPoint); byte[] data = close.ToBytes(); Buffer.BlockCopy(data, 0, buf.Data, 0, data.Length); buf.DataLength = data.Length; AsyncBeginSend(buf); } // Shut the socket communication down Stop(); } }
public void FreeUDPBuffer(UDPPacketBuffer buf) { lock (m_udpBuffersPoolLock) { if (buf.DataLength < 0) { return; // avoid duplicated free that may still happen } if (m_udpBuffersPoolPtr < 999) { buf.RemoteEndPoint = null; buf.DataLength = -1; m_udpBuffersPoolPtr++; m_udpBuffersPool[m_udpBuffersPoolPtr] = buf; } } }
private void AsyncBeginReceive() { // this method actually kicks off the async read on the socket. // we aquire a reader lock here to ensure that no other thread // is trying to set shutdownFlag and close the socket. rwLock.AcquireReaderLock(-1); if (!shutdownFlag) { // increment the count of pending operations Interlocked.Increment(ref rwOperationCount); // allocate a packet buffer //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut(); UDPPacketBuffer buf = new UDPPacketBuffer(); try { // kick off an async read udpSocket.BeginReceiveFrom( //wrappedBuffer.Instance.Data, buf.Data, 0, UDPPacketBuffer.BUFFER_SIZE, SocketFlags.None, //ref wrappedBuffer.Instance.RemoteEndPoint, ref buf.RemoteEndPoint, new AsyncCallback(AsyncEndReceive), //wrappedBuffer); buf); } catch (SocketException) { // something bad happened //Logger.Log( // "A SocketException occurred in UDPServer.AsyncBeginReceive()", // Helpers.LogLevel.Error, se); // an error occurred, therefore the operation is void. Decrement the reference count. Interlocked.Decrement(ref rwOperationCount); } } // we're done with the socket for now, release the reader lock. rwLock.ReleaseReaderLock(); }
public void SyncSend(UDPPacketBuffer buf) { if (!m_shutdownFlag) { try { // well not async but blocking m_udpSocket.SendTo( buf.Data, 0, buf.DataLength, SocketFlags.None, buf.RemoteEndPoint); } catch (SocketException) { } catch (ObjectDisposedException) { } } }
public void AsyncBeginSend(UDPPacketBuffer buf) { // if (IsRunningOutbound) // { try { m_udpSocket.BeginSendTo( buf.Data, 0, buf.DataLength, SocketFlags.None, buf.RemoteEndPoint, AsyncEndSend, buf); } catch (SocketException) { } catch (ObjectDisposedException) { } // } }
public void SyncSend(UDPPacketBuffer buf) { try { m_udpSocket.SendTo( buf.Data, 0, buf.DataLength, SocketFlags.None, buf.RemoteEndPoint ); UdpSends++; } catch (SocketException e) { m_log.Warn("[UDPBASE]: sync send SocketException {0} " + e.Message); } catch (ObjectDisposedException) { } }
public void AsyncBeginSend(UDPPacketBuffer buf) { if (!m_shutdownFlag) { try { m_udpSocket.BeginSendTo( buf.Data, 0, buf.DataLength, SocketFlags.None, buf.RemoteEndPoint, AsyncEndSend, buf); } catch (SocketException) { } catch (ObjectDisposedException) { } } }
/* not in use * public void AsyncBeginSend(UDPPacketBuffer buf) * { * // if (IsRunningOutbound) * // { * * // This is strictly for debugging purposes to simulate dropped * // packets when testing throttles & retransmission code * // if (DropOutgoingPacket()) * // return; * * try * { * m_udpSocket.BeginSendTo( * buf.Data, * 0, * buf.DataLength, * SocketFlags.None, * buf.RemoteEndPoint, * AsyncEndSend, * buf); * } * catch (SocketException) { } * catch (ObjectDisposedException) { } * // } * } * * void AsyncEndSend(IAsyncResult result) * { * try * { * // UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; * m_udpSocket.EndSendTo(result); * * UdpSends++; * } * catch (SocketException) { } * catch (ObjectDisposedException) { } * } */ public void SyncSend(UDPPacketBuffer buf) { if (buf.RemoteEndPoint == null) { return; // already expired } try { m_udpSocket.SendTo( buf.Data, 0, buf.DataLength, SocketFlags.None, buf.RemoteEndPoint ); UdpSends++; } catch (SocketException e) { m_log.WarnFormat("[UDPBASE]: sync send SocketException {0} {1}", buf.RemoteEndPoint, e.Message); } catch (ObjectDisposedException) { } }
/// <summary> /// Send a raw byte array payload as a packet /// </summary> /// <param name="payload">The packet payload</param> /// <param name="setSequence">Whether the second, third, and fourth bytes /// should be modified to the current stream sequence number</param> public void SendPacketUnqueued(byte[] payload, bool setSequence) { try { if (setSequence && payload.Length > 3) { uint sequence = (uint)Interlocked.Increment(ref Sequence); payload[1] = (byte)(sequence >> 16); payload[2] = (byte)(sequence >> 8); payload[3] = (byte)(sequence % 256); } Stats.SentBytes += (ulong)payload.Length; ++Stats.SentPackets; UDPPacketBuffer buf = new UDPPacketBuffer(ipEndPoint); Buffer.BlockCopy(payload, 0, buf.Data, 0, payload.Length); buf.DataLength = payload.Length; AsyncBeginSend(buf); } catch (SocketException) { Logger.Log("Tried to send a " + payload.Length + " byte payload on a closed socket, shutting down " + this.ToString(), Helpers.LogLevel.Info, Client); Network.DisconnectSim(this, false); return; } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } }
private void AsyncBeginReceive() { UDPPacketBuffer buf; // FIXME: Disabled for now as this causes issues with reused packet objects interfering with each other // on Windows with m_asyncPacketHandling = true, though this has not been seen on Linux. // Possibly some unexpected issue with fetching UDP data concurrently with multiple threads. Requires more investigation. // if (UsePools) // buf = Pool.GetObject(); // else buf = new UDPPacketBuffer(); if (IsRunningInbound) { try { // kick off an async read m_udpSocket.BeginReceiveFrom( //wrappedBuffer.Instance.Data, buf.Data, 0, UDPPacketBuffer.BUFFER_SIZE, SocketFlags.None, ref buf.RemoteEndPoint, AsyncEndReceive, //wrappedBuffer); buf); } catch (SocketException e) { if (e.SocketErrorCode == SocketError.ConnectionReset) { m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort); bool salvaged = false; while (!salvaged) { try { m_udpSocket.BeginReceiveFrom( //wrappedBuffer.Instance.Data, buf.Data, 0, UDPPacketBuffer.BUFFER_SIZE, SocketFlags.None, ref buf.RemoteEndPoint, AsyncEndReceive, //wrappedBuffer); buf); salvaged = true; } catch (SocketException) { } catch (ObjectDisposedException) { return; } } m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); } } catch (ObjectDisposedException e) { m_log.Error( string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); } catch (Exception e) { m_log.Error( string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); } } }
protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent);
// these abstract methods must be implemented in a derived class to actually do // something with the packets that are sent and received. protected abstract void PacketReceived(UDPPacketBuffer buffer);
public OutgoingPacket(Simulator simulator, UDPPacketBuffer buffer, PacketType type) { Client = simulator; Buffer = buffer; this.Type = type; }
protected override void PacketReceived(UDPPacketBuffer buffer) { Packet packet = null; // Check if this packet came from the server we expected it to come from if (!remoteEndPoint.Address.Equals(((IPEndPoint)buffer.RemoteEndPoint).Address)) { Logger.Log("Received " + buffer.DataLength + " bytes of data from unrecognized source " + ((IPEndPoint)buffer.RemoteEndPoint).ToString(), Helpers.LogLevel.Warning, Client); return; } // Update the disconnect flag so this sim doesn't time out DisconnectCandidate = false; #region Packet Decoding int packetEnd = buffer.DataLength - 1; try { packet = Packet.BuildPacket(buffer.Data, ref packetEnd, // Only allocate a buffer for zerodecoding if the packet is zerocoded ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[8192] : null); } catch (MalformedDataException) { Logger.Log(String.Format("Malformed data, cannot parse packet:\n{0}", Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)), Helpers.LogLevel.Error); } // Fail-safe check if (packet == null) { Logger.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning, Client); return; } Interlocked.Add(ref Stats.RecvBytes, buffer.DataLength); Interlocked.Increment(ref Stats.RecvPackets); #endregion Packet Decoding if (packet.Header.Resent) { Interlocked.Increment(ref Stats.ReceivedResends); } #region ACK Receiving // Handle appended ACKs if (packet.Header.AppendedAcks && packet.Header.AckList != null) { lock (NeedAck) { for (int i = 0; i < packet.Header.AckList.Length; i++) { NeedAck.Remove(packet.Header.AckList[i]); } } } // Handle PacketAck packets if (packet.Type == PacketType.PacketAck) { PacketAckPacket ackPacket = (PacketAckPacket)packet; lock (NeedAck) { for (int i = 0; i < ackPacket.Packets.Length; i++) { NeedAck.Remove(ackPacket.Packets[i].ID); } } } #endregion ACK Receiving if (packet.Header.Reliable) { #region ACK Sending // Add this packet to the list of ACKs that need to be sent out uint sequence = (uint)packet.Header.Sequence; PendingAcks.Enqueue(sequence); // Send out ACKs if we have a lot of them if (PendingAcks.Count >= Client.Settings.MAX_PENDING_ACKS) { SendAcks(); } #endregion ACK Sending // Check the archive of received packet IDs to see whether we already received this packet if (!PacketArchive.TryEnqueue(packet.Header.Sequence)) { if (packet.Header.Resent) { Logger.DebugLog("Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type); } else { Logger.Log("Received a duplicate (not marked as resend) of packet #" + packet.Header.Sequence + ", type: " + packet.Type, Helpers.LogLevel.Warning); } // Avoid firing a callback twice for the same packet return; } } #region Inbox Insertion NetworkManager.IncomingPacket incomingPacket; incomingPacket.Simulator = this; incomingPacket.Packet = packet; Network.PacketInbox.Enqueue(incomingPacket); #endregion Inbox Insertion #region Stats Tracking if (Client.Settings.TRACK_UTILIZATION) { Client.Stats.Update(packet.Type.ToString(), OpenMetaverse.Stats.Type.Packet, 0, packet.Length); } #endregion }
private void AsyncBeginReceive() { // allocate a packet buffer //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut(); UDPPacketBuffer buf = new UDPPacketBuffer(); if (!m_shutdownFlag) { try { // kick off an async read m_udpSocket.BeginReceiveFrom( //wrappedBuffer.Instance.Data, buf.Data, 0, UDPPacketBuffer.BUFFER_SIZE, SocketFlags.None, ref buf.RemoteEndPoint, AsyncEndReceive, //wrappedBuffer); buf); } catch (SocketException e) { if (e.SocketErrorCode == SocketError.ConnectionReset) { MainConsole.Instance.Warn( "[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort); bool salvaged = false; while (!salvaged) { try { m_udpSocket.BeginReceiveFrom( //wrappedBuffer.Instance.Data, buf.Data, 0, UDPPacketBuffer.BUFFER_SIZE, SocketFlags.None, ref buf.RemoteEndPoint, AsyncEndReceive, //wrappedBuffer); buf); salvaged = true; } catch (SocketException) { } catch (ObjectDisposedException) { return; } } MainConsole.Instance.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); } } catch (ObjectDisposedException) { } } }
protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent) { }
protected override void PacketReceived(UDPPacketBuffer buffer) { Packet packet = null; // Check if this packet came from the server we expected it to come from if (!ipEndPoint.Address.Equals(((IPEndPoint)buffer.RemoteEndPoint).Address)) { Logger.Log("Received " + buffer.DataLength + " bytes of data from unrecognized source " + ((IPEndPoint)buffer.RemoteEndPoint).ToString(), Helpers.LogLevel.Warning, Client); return; } // Update the disconnect flag so this sim doesn't time out DisconnectCandidate = false; #region Packet Decoding int packetEnd = buffer.DataLength - 1; try { packet = Packet.BuildPacket(buffer.Data, ref packetEnd, buffer.ZeroData); } catch (MalformedDataException) { Logger.Log(String.Format("Malformed data, cannot parse packet:\n{0}", Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)), Helpers.LogLevel.Error); } // Fail-safe check if (packet == null) { Logger.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning, Client); return; } Stats.RecvBytes += (ulong)buffer.DataLength; ++Stats.RecvPackets; #endregion Packet Decoding #region Reliable Handling if (packet.Header.Reliable) { // Add this packet to the list of ACKs that need to be sent out lock (PendingAcks) { uint sequence = (uint)packet.Header.Sequence; if (!PendingAcks.ContainsKey(sequence)) { PendingAcks[sequence] = sequence; } } // Send out ACKs if we have a lot of them if (PendingAcks.Count >= Client.Settings.MAX_PENDING_ACKS) { SendAcks(); } if (packet.Header.Resent) { ++Stats.ReceivedResends; } } #endregion Reliable Handling #region Inbox Insertion NetworkManager.IncomingPacket incomingPacket; incomingPacket.Simulator = this; incomingPacket.Packet = packet; // TODO: Prioritize the queue Network.PacketInbox.Enqueue(incomingPacket); #endregion Inbox Insertion }
private void AsyncEndReceive(IAsyncResult iar) { // Asynchronous receive operations will complete here through the call // to AsyncBeginReceive if (IsRunningInbound) { UdpReceives++; // Asynchronous mode will start another receive before the // callback for this packet is even fired. Very parallel :-) if (m_asyncPacketHandling) { AsyncBeginReceive(); } try { // get the buffer that was created in AsyncBeginReceive // this is the received data UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; int startTick = Util.EnvironmentTickCount(); // get the length of data actually read from the socket, store it with the // buffer buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); // call the abstract method PacketReceived(), passing the buffer that // has just been filled from the socket read. PacketReceived(buffer); // If more than one thread can be calling AsyncEndReceive() at once (e.g. if m_asyncPacketHandler) // then a particular stat may be inaccurate due to a race condition. We won't worry about this // since this should be rare and won't cause a runtime problem. if (m_currentReceiveTimeSamples >= s_receiveTimeSamples) { AverageReceiveTicksForLastSamplePeriod = (float)m_receiveTicksInCurrentSamplePeriod / s_receiveTimeSamples; m_receiveTicksInCurrentSamplePeriod = 0; m_currentReceiveTimeSamples = 0; } else { m_receiveTicksInCurrentSamplePeriod += Util.EnvironmentTickCountSubtract(startTick); m_currentReceiveTimeSamples++; } } catch (SocketException se) { m_log.Error( string.Format( "[UDPBASE]: Error processing UDP end receive {0}, socket error code {1}. Exception ", UdpReceives, se.ErrorCode), se); } catch (ObjectDisposedException e) { m_log.Error( string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e); } catch (Exception e) { m_log.Error( string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e); } finally { // if (UsePools) // Pool.ReturnObject(buffer); // Synchronous mode waits until the packet callback completes // before starting the receive to fetch another packet if (!m_asyncPacketHandling) { AsyncBeginReceive(); } } } }
/// <summary> /// This method is called when an incoming packet is received /// </summary> /// <param name="buffer">Incoming packet buffer</param> public abstract void PacketReceived(UDPPacketBuffer buffer);
/// <summary> /// Sends a packet directly to the simulator without queuing /// </summary> /// <param name="packet">Packet to be sent</param> /// <param name="setSequence">True to set the sequence number, false to /// leave it as is</param> public void SendPacketUnqueued(Packet packet, bool setSequence) { byte[] buffer; int bytes; // Set sequence implies that this is not a resent packet if (setSequence) { // Reset to zero if we've hit the upper sequence number limit Interlocked.CompareExchange(ref Sequence, 0, Settings.MAX_SEQUENCE); // Increment and fetch the current sequence number packet.Header.Sequence = (uint)Interlocked.Increment(ref Sequence); if (packet.Header.Reliable) { // Wrap this packet in a struct to track timeouts and resends NetworkManager.OutgoingPacket outgoing = new NetworkManager.OutgoingPacket(this, packet, true); // Keep track of when this packet was first sent out (right now) outgoing.TickCount = Environment.TickCount; // Add this packet to the list of ACK responses we are waiting on from the server lock (NeedAck) { NeedAck[packet.Header.Sequence] = outgoing; } if (packet.Header.Resent) { // This packet has already been sent out once, strip any appended ACKs // off it and reinsert them into the outgoing ACK queue under the // assumption that this packet will continually be rejected from the // server or that the appended ACKs are possibly making the delivery fail if (packet.Header.AckList.Length > 0) { Logger.DebugLog(String.Format("Purging ACKs from packet #{0} ({1}) which will be resent.", packet.Header.Sequence, packet.GetType())); lock (PendingAcks) { foreach (uint sequence in packet.Header.AckList) { if (!PendingAcks.ContainsKey(sequence)) { PendingAcks[sequence] = sequence; } } } packet.Header.AppendedAcks = false; packet.Header.AckList = new uint[0]; } // Update the sent time for this packet SetResentTime(packet.Header.Sequence); } else { // This packet is not a resend, check if the conditions are favorable // to ACK appending if (packet.Type != PacketType.PacketAck && packet.Type != PacketType.LogoutRequest) { lock (PendingAcks) { if (PendingAcks.Count > 0 && PendingAcks.Count < Client.Settings.MAX_APPENDED_ACKS) { // Append all of the queued up outgoing ACKs to this packet packet.Header.AckList = new uint[PendingAcks.Count]; for (int i = 0; i < PendingAcks.Count; i++) { packet.Header.AckList[i] = PendingAcks.Values[i]; } PendingAcks.Clear(); packet.Header.AppendedAcks = true; } } } } } else if (packet.Header.AckList.Length > 0) { // Sanity check for ACKS appended on an unreliable packet, this is bad form Logger.Log("Sending appended ACKs on an unreliable packet", Helpers.LogLevel.Warning); } } // Serialize the packet buffer = packet.ToBytes(); bytes = buffer.Length; Stats.SentBytes += (ulong)bytes; ++Stats.SentPackets; UDPPacketBuffer buf = new UDPPacketBuffer(ipEndPoint); // Zerocode if needed if (packet.Header.Zerocoded) { bytes = Helpers.ZeroEncode(buffer, bytes, buf.Data); } else { Buffer.BlockCopy(buffer, 0, buf.Data, 0, bytes); } buf.DataLength = bytes; AsyncBeginSend(buf); }