You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

273 lines
8.4 KiB

/*
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);
}
}
}
}
}