C# TCP客户端窗体应用程序异步接收方式

1. 程序界面设计

大概设计一个如下的简单界面。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rq7GgnZv-1658637960222)(../picture/image-20220724124105799.png)]

2. 各部分核心代码

2.1连接服务端

当请求连接按钮按下时,会触发点击事件,然后进行连接服务端请求。

private void btnConnect_Click(object sender, EventArgs e)
{
    try
    {
        if (IsConnected == false)
        {
            tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);    // 创建一个Socket套接字               
            IPAddress ipaddress = IPAddress.Parse(cbbHostIP.Text);
            EndPoint point = new IPEndPoint(ipaddress, int.Parse(tbHostPort.Text));

            tcpClient.Connect(point);

            tcpClient.BeginReceive(recieveBuffer, 0, recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
            IsConnected = true;
            btnConnect.Text = "断开连接";
        }
        else
        {
            tcpClient.Disconnect(false);
            IsConnected = false;
            btnConnect.Text = "请求连接";
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

按下连接时,先创建一个socket套接字,然后从控件中获得用户输入的服务端IP和端口,然后进行连接。

最重要的是,这里进行了异步接收设计,设置了接收回调函数 ReceiveCallback。

2.2 发送数据

因为发送数据,是用户主动按下“发送”按钮发送的,所以比较简单,只要有按下发送按钮的点击事件,就发送数据。

private void btnSend_Click(object sender, EventArgs e)
{
    if (IsConnected == true)
    {
        tcpClient.Send(tbSend.Text.GetBytes("GBK"));
    }
    else
    {
        MessageBox.Show("请先连接服务端");
    }
}

2.3 接收数据

接收数据采用异步接收方式,因为无法确定服务端什么时候会发送数据给客户端。

private void ReceiveCallback(IAsyncResult AR)
{
    // Check how much bytes are recieved and call EndRecieve to finalize handshake
    try
    {
        int recieved = tcpClient.EndReceive(AR);

        if (recieved <= 0)
            return;

        string message = Encoding.GetEncoding("GBK").GetString(recieveBuffer, 0, recieved);     // 只将接收到的数据进行转化

        Print(message);

        // Start receiving again
        tcpClient.BeginReceive(recieveBuffer, 0, recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
    }
    catch (SocketException ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

其中,ReceiveCallback 函数是接收的回调函数。如果有数据接收,那么真正接收数据的处理过程,是在这个回调函数完成的。

3. 整个工程源码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;

namespace Net_assistance
{
    public partial class Form1 : Form
    {
        public static Socket tcpClient;
        bool IsConnected = false;
        private byte[] recieveBuffer = new byte[8142];

        public Form1()
        {
            InitializeComponent();
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // 获取本地可用的IP地址
            string strHostIP = Dns.GetHostName();
            IPAddress[] ipadrlist = Dns.GetHostAddresses(strHostIP);
            foreach (IPAddress ipaddr in ipadrlist)
            {
                if (ipaddr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    cbbHostIP.Items.Add(ipaddr.ToString());
            }
            cbbHostIP.Items.Add("127.0.0.1");   // 最后加上回环测试地址
            cbbHostIP.SelectedIndex = 0;

            tbHostPort.Text = "8080";

            tbSend.Text = "hello world!";
        }

        private void Print(string msg)
        {
            tbRecv.AppendText($"[{DateTime.Now.ToString("yyy-MM-dd HH.mm.ss.fff")}] {msg}\r\n");
        }


        private void ReceiveCallback(IAsyncResult AR)
        {
            // Check how much bytes are recieved and call EndRecieve to finalize handshake
            try
            {
                int recieved = tcpClient.EndReceive(AR);

                if (recieved <= 0)
                    return;

                string message = Encoding.GetEncoding("GBK").GetString(recieveBuffer, 0, recieved);     // 只将接收到的数据进行转化

                Print(message);

                // Start receiving again
                tcpClient.BeginReceive(recieveBuffer, 0, recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
            }
            catch (SocketException ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            try
            {
                if (IsConnected == false)
                {
                    tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);    // 创建一个Socket套接字               
                    IPAddress ipaddress = IPAddress.Parse(cbbHostIP.Text);
                    EndPoint point = new IPEndPoint(ipaddress, int.Parse(tbHostPort.Text));

                    tcpClient.Connect(point);

                    tcpClient.BeginReceive(recieveBuffer, 0, recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
                    IsConnected = true;
                    btnConnect.Text = "断开连接";
                }
                else
                {
                    tcpClient.Disconnect(false);
                    IsConnected = false;
                    btnConnect.Text = "请求连接";
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            if (IsConnected == true)
            {
                tcpClient.Send(tbSend.Text.GetBytes("GBK"));
            }
            else
            {
                MessageBox.Show("请先连接服务端");
            }
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (IsConnected == true)
            {// 关闭窗口时,如果正在连接着服务端,那么断开连接后再退出
                tcpClient.Disconnect(false);
            }
        }
    }
}

4. 测试

先用网络调试助手打开一个服务端。如下:
在这里插入图片描述
然后打开我们自己写的客户端程序,输入服务端的IP和端口后,点击连接。这时可以看到服务端监测到有有客户端的接入了,这时就可以互相收发数据了,效果如下:
在这里插入图片描述

  • 3
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值