博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
GJM : Socket TCP 通信连接(一)
阅读量:4558 次
发布时间:2019-06-08

本文共 10639 字,大约阅读时间需要 35 分钟。

本文章将讲解基于TCP连接的Socket通讯,使用Socket异步功能,并且无粘包现象,通过事件驱动使用。

在编写Socket代码之前,我们得要定义一下Socket的基本功能。

作为一个TCP连接,不论是客户端还是服务器端,它都得有以下接口:

public interface ISocket{    ///     /// 获取是否已连接。    ///     bool IsConnected { get; }    ///     /// 发送数据。    ///     /// 要发送的数据。    void Send(byte[] data);    ///     /// 异步发送数据。    ///     /// 要发送的数据。    void SendAsync(byte[] data);    ///     /// 断开连接。    ///     void Disconnect();    ///     /// 异步断开连接。    ///     void DisconnectAsync();            ///     /// 断开完成时引发事件。    ///     event EventHandler
DisconnectCompleted; ///
/// 接收完成时引发事件。 /// event EventHandler
ReceiveCompleted; ///
/// 发送完成时引发事件。 /// event EventHandler
SendCompleted;}

用到的事件参数SocketEventArgs。

/// /// Socket事件参数/// public class SocketEventArgs : EventArgs{    ///     /// 实例化Socket事件参数    ///     /// 相关Socket    /// 操作类型    public SocketEventArgs(ISocket socket, SocketAsyncOperation operation)    {        if (socket == null)            throw new ArgumentNullException("socket");        Socket = socket;        Operation = operation;    }    ///     /// 获取或设置事件相关数据。    ///     public byte[] Data { get; set; }    ///     /// 获取数据长度。    ///     public int DataLength { get { return Data == null ? 0 : Data.Length; } }    ///     /// 获取事件相关Socket    ///     public ISocket Socket { get; private set; }    ///     /// 获取事件操作类型。    ///     public SocketAsyncOperation Operation { get; private set; }}

因为作为客户端只管收发,比较简单,所以这里从客户端开始做起。

定义类TCPClient继承接口ISocket和IDisposable

/// /// TCP客户端/// public class TCPClient : ISocket, IDisposable{    ///     /// 获取是否已连接。    ///     public bool IsConnected { get; }    ///     /// 发送数据。    ///     /// 要发送的数据。    public void Send(byte[] data)    {    }    ///     /// 异步发送数据。    ///     /// 要发送的数据。    public void SendAsync(byte[] data)    {    }    ///     /// 断开连接。    ///     public void Disconnect()    {    }    ///     /// 异步断开连接。    ///     public void DisconnectAsync()    {    }          ///     /// 断开完成时引发事件。    ///     public event EventHandler
DisconnectCompleted; ///
/// 接收完成时引发事件。 /// public event EventHandler
ReceiveCompleted; ///
/// 发送完成时引发事件。 /// public event EventHandler
SendCompleted; ///
/// 释放资源。 /// public void Dispose() { }}

并在此之上,增加以下方法

///     /// 连接至服务器。    ///     /// 服务器终结点。    public void Connect(IPEndPoint endpoint)    {    }    ///     /// 异步连接至服务器。    ///     ///     public void ConnectAsync(IPEndPoint endpoint)    {    }

下面我们开始编写构造函数,实例化一个Socket并保存到私有变量里。

把IsConnected指向Socket.Connected。

private Socket Socket;    private Stream Stream;    ///     /// 实例化TCP客户端。    ///     public TCPClient()    {        Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);    }    ///     /// 获取是否已连接。    ///     public bool IsConnected { get { return Socket.Connected; } }

因为接下来我们开始编写Socket的异步功能,所以在此之前,我们要做一个状态类,用来保存异步状态。

internal class SocketAsyncState{    ///     /// 是否完成。    ///     public bool Completed { get; set; }    ///     /// 数据    ///     public byte[] Data { get; set; }///     /// 是否异步    ///     public bool IsAsync { get; set; }}

下面我们开始编写TCP连接功能。

///     /// 连接至服务器。    ///     /// 服务器终结点。    public void Connect(IPEndPoint endpoint)    {        //判断是否已连接        if (IsConnected)            throw new InvalidOperationException("已连接至服务器。");        if (endpoint == null)            throw new ArgumentNullException("endpoint");        //锁定自己,避免多线程同时操作        lock (this)        {            SocketAsyncState state = new SocketAsyncState();            //Socket异步连接            Socket.BeginConnect(endpoint, EndConnect, state).AsyncWaitHandle.WaitOne();            //等待异步全部处理完成            while (!state.Completed) { }        }    }    ///     /// 异步连接至服务器。    ///     ///     public void ConnectAsync(IPEndPoint endpoint)    {        //判断是否已连接        if (IsConnected)            throw new InvalidOperationException("已连接至服务器。");        if (endpoint == null)            throw new ArgumentNullException("endpoint");        //锁定自己,避免多线程同时操作        lock (this)        {            SocketAsyncState state = new SocketAsyncState();            //设置状态为异步            state.IsAsync = true;            //Socket异步连接            Socket.BeginConnect(endpoint, EndConnect, state);        }    }    private void EndConnect(IAsyncResult result)    {        SocketAsyncState state = (SocketAsyncState)result.AsyncState;            try        {            Socket.EndConnect(result);        }        catch        {            //出现异常,连接失败。            state.Completed = true;            //判断是否为异步,异步则引发事件            if (state.IsAsync && ConnectCompleted != null)                ConnectCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Connect));            return;        }            //连接成功。        //创建Socket网络流        Stream = new NetworkStream(Socket);        //连接完成        state.Completed = true;        if (state.IsAsync && ConnectCompleted != null)        {            ConnectCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Connect));        }                //开始接收数据        Handler.BeginReceive(Stream, EndReceive, state);    }     ///      /// 连接完成时引发事件。     ///      public event EventHandler
ConnectCompleted;

以上为连接服务器的代码,EndConnect中最后的Handler为一个处理IO收发的类,这留到后面再说。

接下来我们开始做断开服务器的方法。

///     /// 断开与服务器的连接。    ///     public void Disconnect()    {        //判断是否已连接        if (!IsConnected)            throw new InvalidOperationException("未连接至服务器。");        lock (this)        {            //Socket异步断开并等待完成            Socket.BeginDisconnect(true, EndDisconnect, true).AsyncWaitHandle.WaitOne();        }    }    ///     /// 异步断开与服务器的连接。    ///     public void DisconnectAsync()    {        //判断是否已连接        if (!IsConnected)            throw new InvalidOperationException("未连接至服务器。");        lock (this)        {            //Socket异步断开            Socket.BeginDisconnect(true, EndDisconnect, false);        }    }    private void EndDisconnect(IAsyncResult result)    {        try        {            Socket.EndDisconnect(result);        }        catch        {        }        //是否同步        bool sync = (bool)result.AsyncState;                if (!sync && DisconnectCompleted!=null)        {            DisconnectCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Disconnect));        }    }    //这是一个给收发异常准备的断开引发事件方法    private void Disconnected(bool raiseEvent)    {        if (raiseEvent && DisconnectCompleted != null)            DisconnectCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Disconnect));    }

至此,我们已经完成了客户端的连接于断开功能。

现在我们开始写客户端的发送接收功能。

对于Socket的发送与接收,在大量数据吞吐的时候,容易造成粘包问题,要解决这个问题,我们先定义一个ISocketHandler接口。

该接口定义了Socket的发送与接收。

public interface ISocketHandler{    ///     /// 开始接收    ///     /// Socket网络流    /// 回调函数    /// 自定义状态    /// 
异步结果
IAsyncResult BeginReceive(Stream stream, AsyncCallback callback, object state); /// /// 结束接收 /// /// 异步结果 ///
接收到的数据
byte[] EndReceive(IAsyncResult asyncResult); /// /// 开始发送 /// /// 要发送的数据 /// 数据偏移 /// 发送长度 /// Socket网络流 /// 回调函数 /// 自定义状态 ///
异步结果
IAsyncResult BeginSend(byte[] data, int offset, int count, Stream stream, AsyncCallback callback, object state); /// /// 结束发送 /// /// 异步结果 ///
发送是否成功
bool EndSend(IAsyncResult asyncResult);}

在TCPClient中添加一个属性。

///     /// Socket处理程序    ///     public ISocketHandler Handler { get; set; }

这个ISocketHandler在上面的EndConnect里有使用到BeginReceive()。

而使用BeginReceive的回调函数则是这个。

private void EndReceive(IAsyncResult result)    {        SocketAsyncState state = (SocketAsyncState)result.AsyncState;        //接收到的数据        byte[] data = Handler.EndReceive(result);        //如果数据长度为0,则断开Socket连接        if (data.Length == 0)        {            Disconnected(true);            return;        }        //再次开始接收数据        Handler.BeginReceive(Stream, EndReceive, state);        //引发接收完成事件        if (ReceiveCompleted != null)            ReceiveCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Receive) { Data = data });    }

有了这个回调函数,我们的客户端就能持续的接收数据。

现在剩下发送数据的功能要完成。

///     /// 发送数据。    ///     /// 要发送的数据。    public void Send(byte[] data)    {        //是否已连接        if (!IsConnected)            throw new SocketException(10057);        //发送的数据不能为null        if (data == null)            throw new ArgumentNullException("data");        //发送的数据长度不能为0        if (data.Length == 0)            throw new ArgumentException("data的长度不能为0");        //设置异步状态        SocketAsyncState state = new SocketAsyncState();        state.IsAsync = false;        state.Data = data;        try        {            //开始发送数据            Handler.BeginSend(data, 0, data.Length, Stream, EndSend, state).AsyncWaitHandle.WaitOne();        }        catch        {            //出现异常则断开Socket连接            Disconnected(true);        }    }    ///     /// 异步发送数据。    ///     /// 要发送的数据。    public void SendAsync(byte[] data)    {        //是否已连接        if (!IsConnected)            throw new SocketException(10057);        //发送的数据不能为null        if (data == null)            throw new ArgumentNullException("data");        //发送的数据长度不能为0        if (data.Length == 0)            throw new ArgumentException("data的长度不能为0");        //设置异步状态        SocketAsyncState state = new SocketAsyncState();        state.IsAsync = true;        state.Data = data;        try        {            //开始发送数据并等待完成            Handler.BeginSend(data, 0, data.Length, Stream, EndSend, state);        }        catch        {            //出现异常则断开Socket连接            Disconnected(true);        }    }    private void EndSend(IAsyncResult result)    {        SocketAsyncState state = (SocketAsyncState)result.AsyncState;                //是否完成        state.Completed = Handler.EndSend(result);        //没有完成则断开Socket连接        if (!state.Completed)            Disconnected(true);        //引发发送结束事件        if (state.IsAsync && SendCompleted != null)        {            SendCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Send) { Data = state.Data });        }    }

至此,客户端的发送接收也完成了。

我们再写一下释放资源的方法。

///     /// 释放资源    ///     public void Dispose()    {        lock (this)        {            if (IsConnected)                Socket.Disconnect(false);            Socket.Close();        }    }

整个客户端编写完成。

将讲解ISocketHandler的实现方法,用ISocketHandler来完成Socket的IO工作。

你可以在ISocketHandler中定义你自己的Socket通讯协议。

 

原文地址:

转载于:https://www.cnblogs.com/TDou/p/6478805.html

你可能感兴趣的文章
2015.04.23,外语,读书笔记-《Word Power Made Easy》 12 “如何奉承朋友” SESSION 33
查看>>
Spring+SpringMVC+JDBC实现登录
查看>>
生与死之间
查看>>
NEFU 109
查看>>
HDU 5435
查看>>
git从已有分支拉新分支开发
查看>>
滚动条隐藏兼容写法
查看>>
SQL2005查询所有表的大小
查看>>
Shell 正则表达式
查看>>
Docker run命令参数整理
查看>>
qt-opencv配置mingw编译器
查看>>
CSS之Medial Queries的另一用法:实现IE hack的方法
查看>>
linux-CentOS6.4下安装oracle11g详解
查看>>
实力为王 八年DBA经验谈
查看>>
2-sat 问题 【例题 Flags(2-sat+线段树优化建图)】
查看>>
ext3.2 右击动态添加node的treepanel
查看>>
Database links
查看>>
1035 插入与归并(25 分)
查看>>
STL中排序函数的用法(Qsort,Sort,Stable_sort,Partial_sort,List::sort)
查看>>
如何解决php 生成验证码图片不显示问题
查看>>