private async Task DoSendAsync(PayloadData payload, CancellationToken cancellationToken) { var bytesSent = 0; var data = payload.ArraySegment; const int maxBytesToSend = 16777215; int bytesToSend; do { // break payload into packets of at most (2^24)-1 bytes bytesToSend = Math.Min(data.Count - bytesSent, maxBytesToSend); // write four-byte packet header; https://dev.mysql.com/doc/internals/en/mysql-packet.html SerializationUtility.WriteUInt32((uint)bytesToSend, m_buffer, 0, 3); m_buffer[3] = (byte)m_sequenceId; m_sequenceId++; if (bytesToSend <= m_buffer.Length - 4) { Buffer.BlockCopy(data.Array, data.Offset, m_buffer, 4, bytesToSend); m_socketAwaitable.EventArgs.SetBuffer(0, bytesToSend + 4); await m_socket.SendAsync(m_socketAwaitable); } else { m_socketAwaitable.EventArgs.SetBuffer(null, 0, 0); m_socketAwaitable.EventArgs.BufferList = new[] { new ArraySegment <byte>(m_buffer, 0, 4), data }; await m_socket.SendAsync(m_socketAwaitable); m_socketAwaitable.EventArgs.BufferList = null; m_socketAwaitable.EventArgs.SetBuffer(m_buffer, 0, 0); } bytesSent += bytesToSend; } while (bytesToSend == maxBytesToSend); }
private async Task <bool> OpenTcpSocketAsync(ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken) { foreach (var hostname in cs.Hostnames) { IPAddress[] ipAddresses; try { #if NETSTANDARD1_3 // Dns.GetHostAddresses isn't available until netstandard 2.0: https://github.com/dotnet/corefx/pull/11950 ipAddresses = await Dns.GetHostAddressesAsync(hostname).ConfigureAwait(false); #else if (ioBehavior == IOBehavior.Asynchronous) { ipAddresses = await Dns.GetHostAddressesAsync(hostname).ConfigureAwait(false); } else { ipAddresses = Dns.GetHostAddresses(hostname); } #endif } catch (SocketException) { // name couldn't be resolved continue; } // need to try IP Addresses one at a time: https://github.com/dotnet/corefx/issues/5829 foreach (var ipAddress in ipAddresses) { TcpClient tcpClient = null; try { tcpClient = new TcpClient(ipAddress.AddressFamily); using (cancellationToken.Register(() => tcpClient?.Client?.Dispose())) { try { if (ioBehavior == IOBehavior.Asynchronous) { await tcpClient.ConnectAsync(ipAddress, cs.Port).ConfigureAwait(false); } else { #if NETSTANDARD1_3 await tcpClient.ConnectAsync(ipAddress, cs.Port).ConfigureAwait(false); #else if (Utility.IsWindows()) { tcpClient.Connect(ipAddress, cs.Port); } else { // non-windows platforms block on synchronous connect, use send/receive timeouts: https://github.com/dotnet/corefx/issues/20954 var originalSendTimeout = tcpClient.Client.SendTimeout; var originalReceiveTimeout = tcpClient.Client.ReceiveTimeout; tcpClient.Client.SendTimeout = cs.ConnectionTimeoutMilliseconds; tcpClient.Client.ReceiveTimeout = cs.ConnectionTimeoutMilliseconds; tcpClient.Connect(ipAddress, cs.Port); tcpClient.Client.SendTimeout = originalSendTimeout; tcpClient.Client.ReceiveTimeout = originalReceiveTimeout; } #endif } } catch (ObjectDisposedException ex) when(cancellationToken.IsCancellationRequested) { throw new MySqlException("Connect Timeout expired.", ex); } } } catch (SocketException) { tcpClient?.Client?.Dispose(); continue; } m_hostname = hostname; m_tcpClient = tcpClient; m_socket = m_tcpClient.Client; m_networkStream = m_tcpClient.GetStream(); SerializationUtility.SetKeepalive(m_socket, cs.Keepalive); lock (m_lock) m_state = State.Connected; return(true); } } return(false); }
private async Task <PayloadData> DoReceiveAsync2(CancellationToken cancellationToken, bool optional = false) { if (m_end - m_offset < 4) { if (m_end - m_offset > 0) { Buffer.BlockCopy(m_buffer, m_offset, m_buffer, 0, m_end - m_offset); } m_end -= m_offset; m_offset = 0; } // read packet header int offset = m_end; int count = m_buffer.Length - m_end; while (m_end - m_offset < 4) { m_socketAwaitable.EventArgs.SetBuffer(offset, count); await m_socket.ReceiveAsync(m_socketAwaitable); int bytesRead = m_socketAwaitable.EventArgs.BytesTransferred; if (bytesRead <= 0) { if (optional) { return(null); } throw new EndOfStreamException(); } offset += bytesRead; m_end += bytesRead; count -= bytesRead; } // decode packet header int payloadLength = (int)SerializationUtility.ReadUInt32(m_buffer, m_offset, 3); if (m_buffer[m_offset + 3] != (byte)(m_sequenceId & 0xFF)) { if (optional) { return(null); } throw new InvalidOperationException("Packet received out-of-order. Expected {0}; got {1}.".FormatInvariant(m_sequenceId & 0xFF, m_buffer[3])); } m_sequenceId++; m_offset += 4; if (m_end - m_offset >= payloadLength) { offset = m_offset; m_offset += payloadLength; return(new PayloadData(new ArraySegment <byte>(m_buffer, offset, payloadLength))); } // allocate a larger buffer if necessary var readData = m_buffer; if (payloadLength > m_buffer.Length) { readData = new byte[payloadLength]; m_socketAwaitable.EventArgs.SetBuffer(readData, 0, 0); } Buffer.BlockCopy(m_buffer, m_offset, readData, 0, m_end - m_offset); m_end -= m_offset; m_offset = 0; // read payload offset = m_end; count = readData.Length - m_end; while (m_end < payloadLength) { m_socketAwaitable.EventArgs.SetBuffer(offset, count); await m_socket.ReceiveAsync(m_socketAwaitable); int bytesRead = m_socketAwaitable.EventArgs.BytesTransferred; if (bytesRead <= 0) { throw new EndOfStreamException(); } offset += bytesRead; m_end += bytesRead; count -= bytesRead; } // switch back to original buffer if a larger one was allocated if (payloadLength > m_buffer.Length) { m_socketAwaitable.EventArgs.SetBuffer(m_buffer, 0, 0); m_end = 0; } if (payloadLength <= m_buffer.Length) { m_offset = payloadLength; } return(new PayloadData(new ArraySegment <byte>(readData, 0, payloadLength))); }