public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 100;
public byte[] buffer = new byte[BufferSize];
public List<byte> buffers = new List<byte>();
//是不是和服務(wù)器的鏈接
public bool IsServerCon = false;
}
/// <summary>
/// 打洞節(jié)點(diǎn)客戶端 實(shí)現(xiàn)的功能:
/// 連接服務(wù)器獲取對(duì)方節(jié)點(diǎn)ip
/// 請(qǐng)求對(duì)方ip(打洞)
/// 根據(jù)條件判斷是監(jiān)聽連接還是監(jiān)聽等待連接
/// </summary>
public class PeerClient : IPeerClient
{
//ManualResetEvent xxxxDone = new ManualResetEvent(false);
//Semaphore
/// <summary>
/// 當(dāng)前鏈接
/// </summary>
public Socket Client { get;private set; }
#region 服務(wù)端
public string ServerHostName { get;private set; }
public int ServerPort { get; private set; }
#endregion
#region 接收和通知事件
public delegate void EventMsg(object sender, string e);
//接收事件
public event EventMsg ReceivedMsg;
//通知消息
public event EventMsg NoticeMsg;
#endregion
//本地綁定的節(jié)點(diǎn)
private IPEndPoint LocalEP;
public PeerClient(string hostname, int port) {
Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this.ServerHostName = hostname;
this.ServerPort = port;
}
/// <summary>
/// 初始化客戶端(包括啟動(dòng))
/// </summary>
public void Init() {
try
{
Client.Connect(ServerHostName, ServerPort);
}
catch (SocketException ex)
{
NoticeMsg?.Invoke(Client, $"連接服務(wù)器失敗!{ex}!\r\n");
throw;
}
catch (Exception ex)
{
NoticeMsg?.Invoke(Client, $"連接服務(wù)器失??!{ex}!\r\n");
throw;
}
NoticeMsg?.Invoke(Client, $"連接上服務(wù)器了!\r\n");
var _localEndPoint = Client.LocalEndPoint.ToString();
LocalEP = new IPEndPoint(IPAddress.Parse(_localEndPoint.Split(':')[0])
, int.Parse(_localEndPoint.Split(':')[1]));
Receive(Client);
}
private void Receive(Socket client) {
try
{
StateObject state = new StateObject();
state.workSocket = client;
state.IsServerCon = true;
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
NoticeMsg?.Invoke(Client, $"接收消息出錯(cuò)了{(lán)e}!\r\n");
}
}
private void ReceiveCallback(IAsyncResult ar) {
try
{
var state = (StateObject)ar.AsyncState;
Socket _client = state.workSocket;
//因?yàn)榈竭@邊的經(jīng)常Connected 還是true
//if (!_client.Connected)
//{
// _client.Close();
// return;
//}
SocketError error = SocketError.Success;
int bytesRead = _client.EndReceive(ar,out error);
if (error == SocketError.ConnectionReset)
{
NoticeMsg?.Invoke(Client, $"鏈接已經(jīng)釋放!\r\n");
_client.Close();
_client.Dispose();
return;
}
if (SocketError.Success!= error)
{
throw new SocketException((int)error);
}
var arr = state.buffer.AsQueryable().Take(bytesRead).ToArray();
state.buffers.AddRange(arr);
if (bytesRead >= state.buffer.Length)
{
_client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
state.buffers.CopyTo(state.buffers.Count, state.buffer, 0, bytesRead);
//_client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
// new AsyncCallback(ReceiveCallback), state);
}
else
{
var _msg = Encoding.UTF8.GetString(state.buffers.ToArray());
ReceivedMsg?.Invoke(_client, _msg);
if (state.IsServerCon)
{
_client.Shutdown(SocketShutdown.Both);
_client.Close();
int retryCon = _msg.Contains("_1") ? 1 : 100;
_msg = _msg.Replace("_1", "").Replace("_2", "");
TryConnection(_msg.Split(':')[0], int.Parse(_msg.Split(':')[1]), retryCon);
return;
}
state = new StateObject();
state.IsServerCon = false;
state.workSocket = _client;
_client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
}
catch (SocketException ex)
{
//10054
NoticeMsg?.Invoke(Client, $"鏈接已經(jīng)釋放!{ex}!\r\n");
}
catch (Exception e)
{
NoticeMsg?.Invoke(Client, $"接收消息出錯(cuò)了2{e}!\r\n");
}
}
/// <summary>
/// 打洞或者嘗試鏈接
/// </summary>
private void TryConnection(string remoteHostname, int remotePort,int retryCon) {
Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
var _iPRemotePoint = new IPEndPoint(IPAddress.Parse(remoteHostname), remotePort);
Client.Bind(LocalEP);
System.Threading.Thread.Sleep(retryCon==1?1:3*1000);
for (int i = 0; i < retryCon; i++)
{
try
{
Client.Connect(_iPRemotePoint);
NoticeMsg?.Invoke(Client, $"已經(jīng)連接上:{remoteHostname}:{remotePort}!\r\n");
StateObject state = new StateObject();
state.workSocket = Client;
state.IsServerCon = false;
Client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
return;
}
catch
{
NoticeMsg?.Invoke(Client, $"嘗試第{i+1}次鏈接:{remoteHostname}:{remotePort}!\r\n");
}
}
if (retryCon==1)
{
Listening(LocalEP.Port);
return;
}
NoticeMsg?.Invoke(Client, $"嘗試了{(lán)retryCon}次都沒有辦法連接到:{remoteHostname}:{remotePort},涼了!\r\n"); }
/// <summary>
/// 如果連接不成功,因?yàn)槭孪扔写蚨催^了,根據(jù)條件監(jiān)聽 等待對(duì)方連接來
/// </summary>
private void Listening(int Port) {
try
{
Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
Client.Bind(new IPEndPoint(IPAddress.Any, Port));Client.Listen((int)SocketOptionName.MaxConnections);
NoticeMsg?.Invoke(Client, $"開始偵聽斷開等待鏈接過來!\r\n");
StateObject state = new StateObject();
state.IsServerCon = false;
var _socket = Client.Accept();//只有一個(gè)鏈接 不用BeginAccept
Client.Close();//關(guān)系現(xiàn)有偵聽
Client = _socket;
state.workSocket = Client;
NoticeMsg?.Invoke(Client, $"接收到來自{Client.RemoteEndPoint}的連接!\r\n");
Client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception ex)
{
NoticeMsg?.Invoke(Client, $"監(jiān)聽出錯(cuò)了{(lán)ex}涼了!\r\n");
}
//scoket.send
}
/// <summary>
/// 本例子只存在一個(gè)成功的鏈接,對(duì)成功的連接發(fā)送消息!
/// </summary>
/// <param name="strMsg"></param>
public void Send(string strMsg) {
byte[] bytes = Encoding.UTF8.GetBytes(strMsg);
Client.BeginSend(bytes, 0, bytes.Length, 0,
new AsyncCallback(SendCallback), Client);
}
private void SendCallback(IAsyncResult ar) {
try
{
Socket _socket = (Socket)ar.AsyncState;
//if(ar.IsCompleted)
_socket.EndSend(ar);
}
catch (Exception e)
{
NoticeMsg?.Invoke(Client, $"發(fā)送消息出錯(cuò)了{(lán)e}!\r\n");
}
}
}