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