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

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方法.

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方法.

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

程序截图

   

源码下载

       

posted @ 2011-05-03 16:12  Create Chen  阅读(19605)  评论(40编辑  收藏  举报