C# Socket进程通信- - - -长连接实现方式的理解(Tcp)

TCP长连接Demo

网络连接:1、官方解释自行百度
解释(for me):网络连接有客户端和服务端两部分组成,一个服务端接收多条客户端数据并进行回复的操作。客户端收到数据和客户端请求都有对应状态,并且是并发执行,故需要多线程进行监听调用。每一个客户端的请求都是需要经过多条路由后连接到服务端,反之接收数据同理。

tcp长连接形式会导致服务端很容易崩溃,比如100个线程同时运行时,32位系统会直接崩溃,故实际项目中不建议使用,以下只做为客户端和服务端的初步了解。
以下仅对网络连接进行模拟接收数据的基本操作,做一个基本的了解,首先建立服务端:
1、服务端样图如下(namespace下的所有代码
):
在这里插入图片描述

public partial class LongServerForm : Form
{
    public LongServerForm()
    {
        InitializeComponent();
    }

    //声明socket
    private Socket server = null;
    //用于存储线程个数及相应信息     至少有一个新的空间,不能设置为空,设置为空你是收不到数据的。
    private List<Socket> listClientIP = new List<Socket>();
   //声明启动的线程
    private Thread acceptThread = null;

    /// <summary>
    /// 启动端口的按钮    实现打开固定端口的服务端
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnStart_Click(object sender, EventArgs e)
    {
        try
        {
            //创建socket对象     InterNetwork:网络连接形式   Stream:流的请求形式   协议类型:tcp
            server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //创建特定的IP地址和端口组合
            IPEndPoint point = new IPEndPoint(IPAddress.Any, Convert.ToInt32(txtServerPort.Text.Trim()));
            server.Bind(point);//绑定端口
            server.Listen(10);//监听数为10
            labDynamicMes.Text = "启动成功,等待客户端连接中";

            //启动线程执行 执行具体方法
            acceptThread = new Thread(new ThreadStart(Accpet));
            //设置主线程结束时子线程跟着结束
            acceptThread.IsBackground = true;
            acceptThread.Start();

            //关闭按钮,防止通信再次启动
            btnStart.Enabled = false;                
        }
        catch (Exception ex)
        {
            //Thread.Sleep(5000);
        }
    }
    /// <summary>
    /// 多线程重点方法
    /// </summary>
    /// <param name="col"></param>
    /// <param name="method"></param>
    public void InvokeMethod(Control col, MethodInvoker method)
    {
        //是否被创建控件   未创建返回空
        if (!col.IsHandleCreated) return;
        //是否占用(释放)  占用返回空
        if (col.IsDisposed) return;
        //多线程请求调用(多开一个线程)  
        if (col.InvokeRequired) col.Invoke(method);
        else method();
    }
    //接收客户端连接
    private void Accpet()
    {
        while (true)
        {
            try
            {
                Socket client = server.Accept();//设置客户端的请求允许连接
                listClientIP.Add(client);//记录

                //允许连接后需要创建接收数据的线程,保证数据可接收,有返回   注意方法的含有object的参数
                Thread clientThread = new Thread(new ParameterizedThreadStart(ReviceData));
                //设置主线程结束时子线程跟着结束
                clientThread.IsBackground = true;
                clientThread.Start(client);

                //请输入基本的逻辑操作  注意一定使用方法InvokeMethod  本质避免系统窗体卡死等问题    哈哈哈  不信你试试
                InvokeMethod(this,delegate
                {
                    labDynamicMes.Text = "有" + listClientIP.Count.ToString() + "个客户端连接!";
                    lbcIPPort.Items.Clear();
                    foreach (Socket item in listClientIP)
                    {
                        lbcIPPort.Items.Add(item.RemoteEndPoint.ToString());
                    }
                });
                Thread.Sleep(2000);//断开后一秒自动获取客户端                    
            }
            catch (Exception ex)
            {
                //Thread.Sleep(5000);
            }
        }
    }
    /// <summary>
    /// 接收数据
    /// </summary>
    /// <param name="obj"></param>
    private void ReviceData(object obj)
    {
        while (true)
        {
            Socket revicesocker = obj as Socket;//客户端对象
            if (obj == null)
                continue;
            try
            {
                byte[] data = new byte[1024 * 1024 * 10];
                //创建数据缓存
                int length = revicesocker.Receive(data);
                if (length == 0) continue;
                //如果取到数据则执行逻辑上的操作   嘿嘿,注意方法InvokeMethed
                InvokeMethod(this,delegate{
                    lbcClientMes.Items.Add(Encoding.Default.GetString(data));
                });
            }
            //
            catch (Exception ex)
            {
                //断开代码
                listClientIP.Remove(revicesocker);
                //InvokeMethed说 :没错又是我,我是代码核心
                InvokeMethod(this, delegate
                {
                    labDynamicMes.Text = "有" + listClientIP.Count.ToString() + "个客户端连接!";
                    lbcIPPort.Items.Clear();
                    foreach (Socket k in listClientIP)
                    {
                        lbcIPPort.Items.Add(k.RemoteEndPoint.ToString());
                    }
                });
            }
        }
    }
    /// <summary>
    /// 发送按钮触发事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnServerSend_Click(object sender, EventArgs e)
    {
        if (lbcIPPort.SelectedIndex < 0) return;
        Socket client = listClientIP[lbcIPPort.SelectedIndex];
        if (client == null) return;
        if (txtServerSend.Text.Trim() == "")
        {
            MessageBox.Show("别闹,请在左侧文本框中输入数据后进行发送操作");
        }
        if (client.Connected == false)
        {
            MessageBox.Show("客户端已断开无法发送数据!"); return;
        }
        client.Send(Encoding.Default.GetBytes(txtServerSend.Text.Trim()));
    }

}

2、客户端的连接窗体及代码如下(namespace下的所有):
在这里插入图片描述

public partial class LongClientForm : Form
{
    public LongClientForm()
    {
        InitializeComponent();
    }

    private Socket clientsocket = null;

    public class StateObject
    {
        public Socket workSocket = null;
        public const int BUFFER_SIZE = 1024 * 1024 * 5;
        public byte[] buffer = new byte[BUFFER_SIZE];
        public StringBuilder sb = new StringBuilder();
    }
    /// <summary>
    /// 连接按钮事件  
    /// 连接服务器端
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnConnect_Click(object sender, EventArgs e)
    {
        try
        {
            clientsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            clientsocket.ReceiveTimeout = 5000;
            clientsocket.ReceiveBufferSize = 1024 * 1024 * 5;
            //创建特定IP地址和端口组合
            IPEndPoint ippoint = new IPEndPoint(IPAddress.Parse(txtIP.Text.Trim()), Convert.ToInt32(txtPort.Text.Trim()));
            //创建好连接
            clientsocket.Connect(ippoint);

            //防止线程池阻塞,调用BeginReceive执行回调函数  直到EndReceive从socket的缓冲区中读到数据或者socket引发异常。
            StateObject stateobject = new StateObject();
            stateobject.workSocket = clientsocket;
            clientsocket.BeginReceive(stateobject.buffer, 0, StateObject.BUFFER_SIZE, SocketFlags.None, new AsyncCallback(TcpIpClientRead_Callback), stateobject);

            btnSend.Enabled = true;
        }
        catch (Exception ex)
        {
            InvokeMethod(this, delegate
            {
                lbcBack.Items.Add("连接服务器失败" + ex.Message);
            });
        }
    }
    /// <summary>
    /// 回调函数主体
    /// </summary>
    /// <param name="ar"></param>
    private void TcpIpClientRead_Callback(IAsyncResult ar)
    {
        StateObject stateobject = (StateObject)ar.AsyncState;
        Socket s = stateobject.workSocket;
        int read = 0;
        try
        {
            read = clientsocket.EndReceive(ar);

            string temp = System.Text.Encoding.Default.GetString(stateobject.buffer, 0, read);

            InvokeMethod(this, delegate
            {
                lbcBack.Items.Add("收到数据:" + temp);
            });

            StateObject so1 = new StateObject();
            so1.workSocket = s;
            s.BeginReceive(so1.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(TcpIpClientRead_Callback), so1);
        }
        catch (Exception ex)
        {               
          //InvokeMethod(this, delegate
          //  {
          //     lbcBack.Items.Add("接收数据发生未知错误,信息:" + ex.Message);
          //  });
        }
    }
    /// <summary>
    /// 向服务器发送有数据的请求
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnSend_Click(object sender, EventArgs e)
    {
        if (clientsocket == null) return;
        if (txtSendMes.Text.Trim() == "")
        {
            MessageBox.Show("你都没写请求谁呢!让我如何做着实很无奈");
        }
        clientsocket.Send(System.Text.Encoding.Default.GetBytes(txtSendMes.Text.Trim()));
    }
    /// <summary>
    /// 多线程重点方法
    /// </summary>
    /// <param name="col"></param>
    /// <param name="method"></param>
    public void InvokeMethod(Control col, MethodInvoker method)
    {
        //是否被创建控件   未创建返回空
        if (!col.IsHandleCreated) return;
        //是否占用(释放)  占用返回空
        if (col.IsDisposed) return;
        //多线程请求调用(多开一个线程)  
        if (col.InvokeRequired) col.Invoke(method);
        else method();
    }
  
}

存在一个小问题:服务端接收客户端发送的文字信息在服务端不展示,虽然加入到队列,但是数据为空,但是服务器端在对固定IP返回文字信息的数据时却可以展示完整的信息,具体原因还未找到,找到后会另行补充。

运行步骤:一个解决方案中建立两个项目,先启动服务端程序,后启动客户端程序,先开启服务端端口后,在客户端对服务端点击按钮连接进行连接,可输入要传入的信息后进行发送,发送的具体内容会展示在服务端的listbox中。相反,服务端返回的数据输入后,指定要返回的IP地址,会将返回的信息展示在客户端的listbox上面。

注意:开发过程中不建议使用,使用后很容易导致服务器崩溃,占用资源极大。下期我会更新常用进程通信形式,简单模拟银行叫号系统服务与客户的连接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值