/* Sog 游戏基础库 2016 by zouwei */ using System; using System.Net; using System.Net.Sockets; using System.Collections.Generic; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Sog { public class UdpClientSession:ClientSession { private const int SOCKET_BUFFER_SIZE = 1024 * 1024; private const int TMP_RECV_BUFFER_SIZE = 1500; protected UdpSession udpSession; private long lastUdpConnectTime; private int udpConnectNum; //todo 握手阶段优化 // 握手阶段用来临时收发信息的buffer,最多是8个字节的临时数据 private byte[] m_tmpRecvBuffer; private byte[] m_tmpSendBuffer; public UdpClientSession(ClientSessionSettings settings):base(settings, SessionNetSelectMode.Synchronous) { AllockSocket(); udpSession = new UdpSession(m_socketClient); NetSessionObj = udpSession; } private void AllockSocket() { m_socketClient = new Socket(settings.RemoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); // set recv buffer m_socketClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, SOCKET_BUFFER_SIZE); m_socketClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, SOCKET_BUFFER_SIZE); m_socketClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, (int)1); } public override void Connect() { TraceLog.Trace("UdpClientSession.Connect remote {0}", settings.RemoteEndPoint); m_isConnected = false; m_isConnecting = true; try { udpConnectNum++; lastUdpConnectTime = AppTime.GetNowSysMs(); m_socketClient.Connect(settings.RemoteEndPoint); if (m_tmpSendBuffer == null) { m_tmpSendBuffer = new byte[4]; } Kcp.ikcp_encode32u(m_tmpSendBuffer, 0, (uint) KcpProtocalType.SYN); // send will block socket m_socketClient.Send(m_tmpSendBuffer, 4, SocketFlags.None); } catch (SocketException ex) { TraceLog.Error("UdpClientSession.Connect {0}, SocketErrorCode {1} message {2}", settings.RemoteEndPoint, ex.SocketErrorCode, ex.Message); } catch (Exception ex) { TraceLog.Error("UdpClientSession.Connect {0} fail, exception {1}" , settings.RemoteEndPoint, ex); } } public override void Reconnect() { TraceLog.Trace("UdpClientSession.Reconnect remote {0}", settings.RemoteEndPoint); // 重连时m_socketClient如果不是null,要丢掉session中未发送和未解析的信息 if (m_socketClient != null) { TraceLog.Error("UdpClientSession.Reconnect close old socket"); CloseSession(); } //重连的时候重新allock socket,linux下必须如此 AllockSocket(); Connect(); } private void UpdateConnect(long nowMs) { if (!m_isConnecting) { return; } // 超时无应答connect失败 if (lastUdpConnectTime > 0 && nowMs >= lastUdpConnectTime + 1500) { SessionEventArgs arg = new SessionEventArgs(); arg.Session = NetSessionObj; OnConnectFail(arg); return; } try { bool bReadable = m_socketClient.Poll(0, SelectMode.SelectRead); if (bReadable) { if (m_tmpRecvBuffer == null) { m_tmpRecvBuffer = new byte[TMP_RECV_BUFFER_SIZE]; } int len = m_socketClient.Receive(m_tmpRecvBuffer); if (len == 8) { if (udpSession.GetKcpConvId() != 0) { TraceLog.Error("UdpConnect socket recv from {0} len {1} invalid session" , m_socketClient.RemoteEndPoint, len); return; } uint proto = 0; uint conv = 0; Kcp.ikcp_decode32u(m_tmpRecvBuffer, 0, ref proto); Kcp.ikcp_decode32u(m_tmpRecvBuffer, 4, ref conv); if (proto != 2) { TraceLog.Error("UdpConnect socket recv from {0} len {1} invalid proto {2}" , m_socketClient.RemoteEndPoint, len, proto); return; } udpSession.SetKcpConvId(conv); OnUdpConnectSucc(); } else { TraceLog.Error("UdpConnect socket recv from {0} invalid connect msg len {1}" , m_socketClient.RemoteEndPoint, len); } } } catch (Exception ex) { TraceLog.Exception(ex); } } protected void UpdateReadWrite(long nowMs) { if (! m_isConnected || NetSessionObj.IsSocketClosed) { return; } UpdateSynchronousMode(); udpSession.Update(nowMs); } protected virtual void UpdateSynchronousMode() { if (NetSessionObj.IsSocketClosed) { SocketErrorOnUpdate(); return; } bool bError = m_socketClient.Poll(0, SelectMode.SelectError); if (bError) { SocketErrorOnUpdate(); return; } bool bReadable = m_socketClient.Poll(0, SelectMode.SelectRead); if (bReadable) { NetSessionObj.RecvReadableSocket(); } //todo udp keepalive NetSessionObj.CheckSocketAndSendKeepAlive(); //write出错的情况下,write调用之后有可能socket会被关闭 if (NetSessionObj.IsSocketClosed) { SocketErrorOnUpdate(); return; } if (NetSessionObj.IsHaveDataNeedSend()) { NetSessionObj.WriteWriteableSocket(); } NetSessionObj.StartSendAsync(); //write出错的情况下,write调用之后有可能socket会被关闭 if (NetSessionObj.IsSocketClosed) { SocketErrorOnUpdate(); } } public override void Update(long nowMs) { UpdateConnect(nowMs); UpdateReadWrite(nowMs); } private void OnUdpConnectSucc() { m_tmpSendBuffer = null; m_tmpRecvBuffer = null; ConnectSucc(); } protected override void CloseSession() { TraceLog.Trace("UdpClientSession.CloseSession conv {0}", udpSession.GetKcpConvId()); SendDisconnectMsg(); m_isConnected = false; m_isConnecting = false; //丢弃正在发送和接收的数据,关闭socket NetSessionObj.Close(); m_socketClient = null; } private void SendDisconnectMsg() { uint conv = udpSession.GetKcpConvId(); if (conv > 0 && m_socketClient != null) { if (m_socketClient.Poll(0, SelectMode.SelectWrite)) { if (m_tmpSendBuffer == null) { m_tmpSendBuffer = new byte[4]; } Kcp.ikcp_encode32u(m_tmpSendBuffer, 0, (uint) KcpProtocalType.FIN); Kcp.ikcp_encode32u(m_tmpSendBuffer, 4, conv); // send will block socket m_socketClient.Send(m_tmpSendBuffer, 8, SocketFlags.None); } } } } }