• C#异步的Socket通信(重构局域网聊天小工具) [转]


    五一在家看了一会儿<重构手册>, 想拿以前写的代码尝试着改进改进, 想起去年暑假写的局域网聊天小工具, 现在看自己那时写的代码已经不堪入目, 最不可思议的当属用了"多线程"处理网络请求, 现在觉得应该使用异步方法.

    主要设计

    简要说明

    上图左边部分表示的是客户端的过程, 右边部分表示的是服务端的过程. 客户端相比服务端在建立连接之前步骤稍微少一些, 成功建立连接后客户端和服务端都有一个CommunicateSocket负责与对方通信, 如发送消息, 接收消息, 发送文件, 接收文件等.

    服务端, 声明ServerSocket, 绑定(Bind)一个IP并指定这个IP的通信端口, 比如是127.0.0.1:9050, ServerSocket可以监听来自多个IP发送的连接请求, 监听(Listen)方法的参数可以设置允许的最多连接请求个数. 然后调用异步接受请求的方法(BeginAccept), 如果接受到某个客户端发来连接请求, 这时定义一个新的CommunicateSocket专门负责与这个客户端通信. 然后可以通过CommunicateSocket.BeginSend()方法给客户端发送数据, CommunicateSocket.BeginReceive()可以接收客户端发来的数据.

    客户端, 有一个CommunicateSocket, 并绑定一个IP以及一个未被占用的端口, 定义IPEndPoint serverIP表示服务端Socket的IP和端口, 这样才可以进行端口对端口之间的通信, 接下来就可以尝试CommunicateSocket.BeginConnect(serverIP), 连接成功之后就可以发送和接收数据了, CommunicateSocket.BeginSend(), CommunicateSocket.BeginReceive().

    有些异步方法有两种实现方式, 如BeginAccept()和AcceptAsync(), 这两个方法有什么区别呢? 以 Begin 和 End 开头的方法是以 APM(Asynchronous Programming Model)设计方法实现的异步操作, 以 Async 结尾的方法是利用称为 EAP (Event-based Asynchronous Pattern) 的设计方法实现的异步操作.

    代码部分

    1. SocketFunc类

    SocketFunc是一个抽象类, 服务端和客户端只有建立连接的方法不同, 其它都相同, 所以把相同的部分放到这个类中.

     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    public abstract class SocketFunc
    {
    //不管是服务端还是客户端, 建立连接后用这个Socket进行通信
    public Socket communicateSocket = null;
     
    //服务端和客户端建立连接的方式稍有不同, 子类会重载
    public abstract void Access(string IP, System.Action AccessAciton);
     
    //发送消息的函数
    public void Send(string message)
    {
    if (communicateSocket.Connected == false)
    {
    throw new Exception("还没有建立连接, 不能发送消息");
    }
    Byte[] msg = Encoding.UTF8.GetBytes(message);
    communicateSocket.BeginSend(msg,0, msg.Length, SocketFlags.None,
    ar => {
     
    }, null);
    }
     
    //接受消息的函数
    public void Receive(System.Action<string> ReceiveAction)
    {
    //如果消息超过1024个字节, 收到的消息会分为(总字节长度/1024 +1)条显示
    Byte[] msg = new byte[1024];
    //异步的接受消息
    communicateSocket.BeginReceive(msg, 0, msg.Length, SocketFlags.None,
    ar => {
    //对方断开连接时, 这里抛出Socket Exception
    //An existing connection was forcibly closed by the remote host
    communicateSocket.EndReceive(ar);
    ReceiveAction(Encoding.UTF8.GetString(msg).Trim('\0',' '));
    Receive(ReceiveAction);
    }, null);
    }
    }

    2. ServerSocket:SocketFunc类

    继承自SocketFunc类, 类中重载了Access方法.

     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class ServerSocket:SocketFunc
    {
    //服务端重载Access函数
    public override void Access(string IP, System.Action AccessAciton)
    {
    Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    //本机预使用的IP和端口
    IPEndPoint serverIP = new IPEndPoint(IPAddress.Any, 9050);
    //绑定服务端设置的IP
    serverSocket.Bind(serverIP);
    //设置监听个数
    serverSocket.Listen(1);
    //异步接收连接请求
    serverSocket.BeginAccept(ar =>
    {
    base.communicateSocket = serverSocket.EndAccept(ar);
    AccessAciton();
    }, null);
    }
    }

    3. ClientSocket:SocketFunc类

    继承自SocketFunc类, 类中重载了Access方法.

     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class ClientSocket:SocketFunc
    {
    //客户端重载Access函数
    public override void Access(string IP, System.Action AccessAciton)
    {
    base.communicateSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    base.communicateSocket.Bind(new IPEndPoint(IPAddress.Any, 9051));
     
    //服务器的IP和端口
    IPEndPoint serverIP;
    try
    {
    serverIP = new IPEndPoint(IPAddress.Parse(IP), 9050);
    }
    catch
    {
    throw new Exception(String.Format("{0}不是一个有效的IP地址!", IP));
    }
     
    //客户端只用来向指定的服务器发送信息,不需要绑定本机的IP和端口,不需要监听
    try
    {
    base.communicateSocket.BeginConnect(serverIP, ar =>
    {
    AccessAciton();
    }, null);
    }
    catch
    {
    throw new Exception(string.Format("尝试连接{0}不成功!", IP));
    }
    }
    }

    程序截图

    源码下载

    http://www.cnblogs.com/technology/archive/2011/05/03/2035410.html

  • 相关阅读:
    文件上传,跨浏览器统一的样式
    JAVA与JSON的序列化、反序列化
    错误记录--更改tomcat端口号方法,Several ports (8005, 8080, 8009)
    45个非常有用的 Oracle 查询语句小结
    三分钟学会不吃球
    Linux命令:TOP
    【Oracle】Oracle官方文档
    【MySQL】MySQL官方文档
    【oracle】处理oracle用户密码中的特殊字符$和@
    【shell】整数运算,小数运算
  • 原文地址:https://www.cnblogs.com/saptechnique/p/2295865.html
Copyright © 2020-2023  润新知