所有的

c#异步Socket服务器与客户端
78
2013-3-11


  • Socket连接(Socket Connection)

  • Socket服务(Socket Service)

  • 连接主机(Connection Host)

  • 加密与压缩(Encrypt与Compress)

  • 请求入队(Enqueuing Requests)

  • 确保发送和接收(Ensure send and recieve)

  • 检查消息头(Check message header)

  • 检查空闲连接(Checking idle connections)

  • 加密服务

  • SSL认证(SSL authentication)

  • 对称认证(Symmetric authentication)

  • 连接创建者(Connection Creator)

  • Socket服务器与Socket侦听者(SocketServer and SocketListener)

  • Socket服务器构造函数与方法(SocketServer constructor and methods)

  • Socket客户端与Socket连接者(SocketClient and SocketConnector)

  • Socket客户端构造函数与方法(SocketClient constructor and methods)

  • 应答演示项目(Echo Demo Project)

  • 主机(Hosts)

  • 服务(Services)

  • 结语(Conclusion)

  • 版本历史(History)

本文仅实现一个相对简单的异步Socket服务器与客户端通信示例。

首先需要说明如下2个问题

1.同步、异步、多线程是什么关系?答:同步是等待返回,相当于阻塞式;异步是不等待返回,是非阻塞式,可以用多线程实现。

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

界面简单如下:

主要代码如下:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
   
namespace Chatting
{
    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);
        }
    }
   
   
    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);
        }
    }
   
    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));
            }
        }
    }
}


相关的事件处理程序,如下:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Net.Sockets;
  
namespace Chatting
{
    public partial class MainForm : Form
    {
  
        public MainForm()
        {
            InitializeComponent();
        }
  
        SocketFunc socket;
        System.Action<string> ReceiveAction;
        System.Action AccessAction;
  
        private void MainForm_Load(object sender, EventArgs e)
        {
            //异步建立连接回调
            AccessAction = () =>
            {
                this.Invoke((MethodInvoker)delegate()
                {
                    lblFriendIP.Visible = false;
                    txtIP.Visible = false;
                    btnConnect.Visible = false;
                    btnWaitAccess.Visible = false;
  
                    String friendIP = socket.communicateSocket.RemoteEndPoint.ToString();
                    lblState.Text = String.Format("连接成功. 对方IP:{0}", friendIP);
  
                    try
                    {
                        socket.Receive(ReceiveAction);
                    }
                    catch (Exception exp)
                    {
                        MessageBox.Show(exp.Message, "错误");
                    }
                });
  
            };
            //异步接收消息回调
            ReceiveAction = msg =>
            {
                txtGetMsg.Invoke((MethodInvoker)delegate()
                {
                    UpdateGetMsgTextBox(false, msg, Color.Red);
                });
            };
        }
  
        private void btnWaitAccess_Click(object sender, EventArgs e)
        {
            this.socket = new ServerSocket();
            try
            {
                this.socket.Access("", this.AccessAction);
            }
            catch (Exception ecp)
            {
                MessageBox.Show(ecp.Message, "错误");
            }
  
            lblState.Text = "等待对方连接...";
        }
  
        private void btnConnect_Click(object sender, EventArgs e)
        {
            this.socket = new ClientSocket();
            try
            {
                this.socket.Access(txtIP.Text, this.AccessAction);
            }
            catch (Exception ecp)
            {
                MessageBox.Show(ecp.Message, "错误");
            }
            lblState.Text = "正在连接对方...";
        }
  
        private void btnSendMsg_Click(object sender, EventArgs e)
        {
            string message = txtSendMsg.Text.Trim();
            if (string.IsNullOrEmpty(message))
            {
                MessageBox.Show("消息内容不能为空!", "错误");
                txtSendMsg.Focus();
                return;
            }
  
            try
            {
                socket.Send(message);
            }
            catch(Exception ecp)
            {
                MessageBox.Show(ecp.Message, "错误");
                return;
            }
  
            UpdateGetMsgTextBox(true, message, Color.Blue);
            txtSendMsg.Text = "";
        }
  
        private void UpdateGetMsgTextBox(bool sendMsg, string message, Color color)
        {
            string appendText;
            if (sendMsg == true)
            {
                appendText = "Client:           " + System.DateTime.Now.ToString()
                    + Environment.NewLine
                    + message + Environment.NewLine;
            }
            else
            {
                appendText = "Server:           " + System.DateTime.Now.ToString()
                    + Environment.NewLine
                    + message + Environment.NewLine;
            }
            int txtGetMsgLength = txtGetMsg.Text.Length;
            txtGetMsg.AppendText(appendText);
            txtGetMsg.Select(txtGetMsgLength, appendText.Length - Environment.NewLine.Length*2 -message.Length);
            txtGetMsg.SelectionColor = color;
  
            txtGetMsg.ScrollToCaret();
        }
  
        private void txtSendMsg_Click(object sender, EventArgs e)
        {
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            BackColor = Color.FromArgb(50, 40, 60, 82);
        }
    }
}


效果,如下:


全部评论
(1)
评论速度
mores++

还可以输入 2000 个字符
添加表情