简单Tcp的winform窗体

最近我们工作室的老师要过来检查了 算是正式大二了。这便是让我抛下医药项目,突击看了书本和网上的例子结合写的Tcp聊天工具。之前我对套接字还是很排斥的,看到就头疼 当初直接跳过 这次回过头来做这个项目也是对我自己的完善和检测。
先来看一下我的简单winform窗体的界面吧
这里写图片描述
* 注意:*
1首先注意的点就是 上面的Icon图标是可以改的,这样看起来好看点,这个也是我的队友告诉我的 我也是蠢的一比 啥也不知道
2还有标题 当初我本来是要用Paint绘制画板写的 ,后来发现可以用Label控件就ok
3.本文章的客户端是针对传送字符串操作的 和下面的服务器端有点冲突 因为 服务器端还可以传送文件等操作 所以 留意区别
TCP要用到的头文件
using System.IO;
using System.Net.Sockets;
using System.Net;
using System.Threading;
知识点
用到的知识点无非就是
1.Socket的套接字
2.线程(没有线程的话 当窗体在等待接收的时候 窗体是卡死的 不能进行其他操作 所以要对等待的事件进行后台线程操作)
3.skinEngine控件的皮肤控件 用来美化窗体(控件在其他文章里 自己找)
4键盘控制输入
5 首先要设置Socket GetSocket; 这个全局变量 不然容易忽视而造成无连接
6form窗体有个属性是AccepButton 绑定一个控件 比如button1 效果是如果按键盘的Enter相当于单击button1

7还有一个问题就是 当传输的内容过多 不进行操作的话 就会出现看不到消息 要手动拖动滑轮 解决方案如下:
单击richtextbox控件 进行操作

 private void richTextBox2_TextChanged(object sender, EventArgs e)
        {
            richTextBox2.SelectionStart = richTextBox2.Text.Length;
            richTextBox2.ScrollToCaret();//将控件内容滚到当前插入字符的位置
        }

对窗体进行初始化 窗体加载的时候要 加载皮肤 。其次还要对线程的检查设置为false 否则 线程无法建通。最后 要对窗体接受键盘按键的值设置为true。

 private void Form1_Load(object sender, EventArgs e)
        {
            this.skinEngine1.SkinFile = "Silver.ssk";
            Control.CheckForIllegalCrossThreadCalls = false;
            this.KeyPreview = true;
        }

单击连接按钮的代码 try{}catch{}是为了防止窗体因为服务端断来连接而造成的窗体卡死

 private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                Socket GetSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建了Socket连接
                IPAddress IP = IPAddress.Parse(textBox1.Text);//获取ip地址
                IPEndPoint point = new IPEndPoint(IP, Int32.Parse(label1.Text));  //是ip地址和端口号的集合
                GetSocket.Connect(point);//创建连接  GetSocket和服务器端建立连接
                Thread thread = new Thread(Recive);//创建线程将Recive函数作为传递给线程的委托
                thread.IsBackground = true; //设置线程为后台线程
                thread.Start(GetSocket);  
            }
            catch { }

        }

recive函数用来接受服务器端的消息

void Recive(object a)
        {
            GetSocket = a as Socket;
            try
            {
                while (true)
                {
                    byte[] buffer = new byte[1024 * 1024 * 2];//传输的时候都是用Byte字节来传输的   这是2MB
                    int number = GetSocket.Receive(buffer);//number是实际接受的长度  receive是接受服务器端传送的消息
                    if (number == 0)
                        break;  //这是防止意外的跳出
                    string ss = Encoding.UTF8.GetString(buffer, 0, number);  //这是byte转换成string类型 0表示从buffer下标为0开始 ,number表示转化的长度
                    ShowThing(ss);
                }
            }
            catch { }
        }

这是 文本框结尾追加 为了代码清晰所以分开写

  void ShowThing(string ss)
        {
            richTextBox2.AppendText(ss + "\n");
        }

这是客户端发送给服务器端的按钮控件

  private void button2_Click(object sender, EventArgs e)
        {
            try
            {
                byte[] buffer = new byte[1024 * 1024 * 2];
                string name = "李四--->" + richTextBox1.Text;
                buffer = Encoding.UTF8.GetBytes(name);
                GetSocket.Send(buffer);  //文本框转化byte的内容
                ShowThing(name);
                richTextBox1.Clear();//清空文本框
            }
            catch
            {

            }
        }

这是窗体接受键盘按钮值的操作 在这里 发送按钮不用Enter 因为 如果这样子虽然也可以达到发送效果 但是文件框会换行 如果 case Key.S的话 也可以达到对应的效果,但是文本框结束后会残留S值 意思就是 虽然按键实现了效果但是按键的 值也施加给了text文本

 private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)  //按下的值 
            { 
                case Keys.F1: button1_Click(sender, e); break;//当按下F1的时候 系统就默认单击button1
                case Keys.Escape: button3_Click(sender, e); break;
            }

        }

以上是客户端的操作 接下来是 服务器端的操作
这个是服务器端的界面 没有进行排版有点简陋
这里写图片描述

实现了 服务器端对多个不同的可以客户端进行收发的效果 还有传送文件的操作 而不是简单的
我就直接复制代码过来好了

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
namespace WindowsFormsApplication12
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Control.CheckForIllegalCrossThreadCalls = false;
        }
        private void ShowMsg(string str)
        {
            richTextBox1.AppendText(str + "\r\n");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                Form2 xx =new Form2();
                xx.Show();
                Form2 xx1 = new Form2();
                xx1.Show();
                Form2 xx2 = new Form2();
                xx2.Show();
                Socket sockWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint point = new IPEndPoint(IPAddress.Any, int.Parse(textBox2.Text));
                sockWatch.Bind(point); //监听程序
                sockWatch.Listen(10);//等待加入的客户端 限制是10个以内
                Thread thread = new Thread(Listen);
                thread.IsBackground = true;
                thread.Start(sockWatch);
            }
            catch
            {

            }
        }
        Socket socketSend;
        Dictionary<string, Socket> SocketDic =new Dictionary<string, Socket>();//这个是为了 实现服务器端对多个客户端进行收发消息 名字对应连接
        void Recive(object o)//监听等待进入的客户端
        {
             socketSend = o as Socket;   //线程要用object  然后转换成需要的类型
            while (true)
            {
                byte[] buffer = new byte[1024 * 1024 * 2];
                int r=socketSend.Receive(buffer);
                if (r == 0)
                    break;
                string str = Encoding.UTF8.GetString(buffer, 0, r);
                ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + str);//RemoteEndPoint获取socketSend终端的ip地址和端口号信息

            }
        }
        void Listen(object o) //接受客户端传信息
        {
            try
            {
                Socket socketWatch = o as Socket;
                while (true)
                {
                    socketSend = socketWatch.Accept();
                    ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + "  ok");
                    string name = socketSend.RemoteEndPoint.ToString();
                    SocketDic.Add(name, socketSend);//键值对传值
                    comboBox1.Items.Add(name);
                    Thread thread = new Thread(Recive);
                    thread.IsBackground = true;
                    thread.Start(socketSend);
                }
            }
            catch
            {

            }
        }
        //发送字符串消息这个实现了  判断传入的消息是字符串类型
        private void button2_Click(object sender, EventArgs e)
        {
            byte[] buffer = Encoding.UTF8.GetBytes(richTextBox2.Text);
            List<byte> list = new List<byte>();  
            list.Add(1); 
            list.AddRange(buffer);  
            byte[] newbuffer = list.ToArray();

            string name=comboBox1.SelectedItem.ToString();
            SocketDic[name].Send(newbuffer);
           // MessageBox.Show(socketSend.RemoteEndPoint.ToString());
        }

        private void button3_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "选择打开文件";
            ofd.Filter = "suoyouwenjian|*.*";
            ofd.ShowDialog();
            textBox3.Text = ofd.FileName;
        }

        private void button4_Click(object sender, EventArgs e)
        {
            string name = textBox3.Text;
            using (FileStream fs = new FileStream(name,FileMode.OpenOrCreate,FileAccess.ReadWrite))
            {
                byte[] buffer = new byte[1024 * 1024 * 3];
                int r= fs.Read(buffer,0,buffer.Length);
                List<byte> list = new List<byte>();
                list.Add(2);
                list.AddRange(buffer);
                byte[] newbuffer = list.ToArray();
                SocketDic[comboBox1.SelectedItem.ToString()].Send(newbuffer);
            }
        }
//震动按钮
        private void button5_Click(object sender, EventArgs e)
        {   
            byte[] buffer=new byte[1];
            buffer[0]=2;
            SocketDic[comboBox1.SelectedItem.ToString()].Send(buffer);
        }
    }
}
接收器主要代码
 void recive()
        {
            while (true)
            {
                byte[] buffer = new byte[1024*1024*2];
                int r = sockSend.Receive(buffer);


                if (r == 0)
                    break;
                else if (buffer[0] == 1)
                {
                    string ss = Encoding.UTF8.GetString(buffer,1,r-1); 
                    if(ss!="")
                        showMsg(sockSend.RemoteEndPoint.ToString() + ":" + ss);
                }
                else if (buffer[0] == 2)
                {
                    SaveFileDialog sdg = new SaveFileDialog();
                    sdg.Title = "请选择保存路径";
                    sdg.Filter="text|*.txt*";
                    sdg.InitialDirectory = @"C:\Users\蒋家聪\Desktop";
                    sdg.ShowDialog(this);//不加this会报错
                    using (FileStream write = new FileStream(sdg.FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
                    {
                        write.Write(buffer, 1, r - 1);
                        MessageBox.Show("保存成功!");
                    }
                }
            }
        }
<think>我们被要求使用中文回答,并且需要按照给定的格式要求(如数学表达式的格式)。用户的问题是关于在C# WinForm中实现Socket通信的方法。根据引用内容,我们可以总结出实现Socket通信的基本步骤,包括服务端和客户端的创建、连接、发送和接收数据等。注意:用户要求的是WinForm窗体应用,所以我们需要考虑在窗体应用中如何实现,包括事件处理(如按钮点击事件)和线程问题(因为网络操作会阻塞,需要在单独的线程中处理以避免界面卡顿)。我们将分步骤说明:1.服务端实现2.客户端实现3.注意事项(如跨线程更新UI、异常处理等)同时,根据引用[5],我们还可以考虑实现多个客户端连接、指定客户端发送消息等功能,但用户问题中并未详细要求,因此我们先实现基本功能,然后可以扩展。由于用户问题中没有明确要求服务端和客户端的具体功能,我们假设实现一个简单的服务端和客户端,能够发送和接收文本消息。我们将分别给出服务端和客户端的代码示例,并解释关键步骤。注意:在WinForm中,网络通信通常需要在后台线程中进行,然后在主线程(UI线程)中更新界面。因此,我们需要使用委托(delegate)来更新UI。引用[1]和[2]提供了Socket的基本用法,引用[3]使用了TcpClient(它是Socket的封装),我们可以选择使用Socket类或TcpClient类。这里我们使用Socket类,因为引用[1]和[2]都是直接使用Socket的。但是,引用[3]使用了TcpClient,它更简单。为了更底层地展示,我们使用Socket类。步骤:服务端:1.创建Socket对象。2.绑定IP地址和端口。3.开始监听。4.接受客户端连接(异步或在新线程中循环接受)。5.接收和发送数据。客户端:1.创建Socket对象。2.连接服务器。3.发送和接收数据。由于WinForm的界面响应,我们需要将网络操作放在新线程中,避免阻塞UI线程。我们参考引用[1]和[2]的代码,并注意跨线程更新UI的问题(使用委托)。下面我们分别给出服务端和客户端的代码示例。注意:代码中需要处理异常,并且当关闭窗体时,需要关闭Socket和线程。服务端窗体(假设有一个按钮btnStartServer启动服务,一个文本框txtLog用于显示日志,一个文本框txtMessage用于输入发送消息,一个按钮btnSend用于发送消息,以及一个列表框listClients用于显示已连接的客户端,以便选择发送对象,但简单起见我们先实现广播给所有客户端):但是,根据引用[5],我们可以存储多个客户端,并选择发送。这里我们先实现一个简单的服务端,只处理一个客户端(为了简化),然后扩展为多个客户端。我们先实现一个客户端连接,然后再扩展。由于用户问题没有明确,我们先实现基本的一对一通信,然后说明如何扩展为多个客户端。服务端代码(WinForm):</think>### C# WinForm Socket 通信实现方法 在 C# WinForm 中实现 Socket 通信需要分别创建服务端和客户端程序,主要流程如下: #### 服务端实现步骤 1. **创建 Socket 对象** 使用 TCP 协议创建服务端 Socket: ```csharp Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ``` 2. **绑定 IP 和端口** 绑定本地终结点并开始监听: ```csharp IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 9999); // 端口号 serverSocket.Bind(endPoint); serverSocket.Listen(10); // 允许最大连接数 ``` 3. **接受客户端连接(异步)** 在新线程中处理客户端连接: ```csharp private void StartListening() { Thread listenThread = new Thread(() => { while (true) { Socket clientSocket = serverSocket.Accept(); // 将客户端添加到集合 clientList.Add(clientSocket); // 启动接收线程 Thread receiveThread = new Thread(() => ReceiveData(clientSocket)); receiveThread.Start(); } }); listenThread.IsBackground = true; listenThread.Start(); } ``` 4. **接收客户端数据** 实现数据接收方法: ```csharp private void ReceiveData(Socket clientSocket) { byte[] buffer = new byte[1024]; while (true) { int bytesRead = clientSocket.Receive(buffer); if (bytesRead == 0) break; // 客户端断开 string message = Encoding.UTF8.GetString(buffer, 0, bytesRead); // 跨线程更新UI(使用委托) this.Invoke((MethodInvoker)delegate { txtLog.AppendText($"收到消息: {message}\n"); }); } } ``` #### 客户端实现步骤 1. **连接服务端** 通过按钮事件触发连接: ```csharp private void btnConnect_Click(object sender, EventArgs e) { clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999); clientSocket.Connect(endPoint); // 启动接收线程 Thread receiveThread = new Thread(ReceiveData); receiveThread.IsBackground = true; receiveThread.Start(); } ``` 2. **发送消息** 文本消息发送方法: ```csharp private void btnSend_Click(object sender, EventArgs e) { string message = txtMessage.Text; byte[] data = Encoding.UTF8.GetBytes(message); clientSocket.Send(data); } ``` 3. **接收服务端响应** 客户端接收线程: ```csharp private void ReceiveData() { byte[] buffer = new byte[1024]; while (true) { int bytesRead = clientSocket.Receive(buffer); string response = Encoding.UTF8.GetString(buffer, 0, bytesRead); this.Invoke((MethodInvoker)delegate { txtResponse.AppendText($"服务端响应: {response}\n"); }); } } ``` #### 关键注意事项 1. **跨线程更新 UI** 必须通过 `Control.Invoke` 方法更新 UI 控件,避免跨线程冲突: ```csharp this.Invoke((MethodInvoker)delegate { // 更新UI代码 }); ``` 2. **异常处理** 所有网络操作需包裹在 try-catch 中: ```csharp try { clientSocket.Connect(endPoint); } catch (SocketException ex) { MessageBox.Show($"连接失败: {ex.Message}"); } ``` 3. **资源释放** 窗体关闭时释放 Socket: ```csharp protected override void OnFormClosing(FormClosingEventArgs e) { clientSocket?.Close(); base.OnFormClosing(e); } ``` 4. **多客户端管理** 服务端可使用 `List<Socket>` 存储客户端引用,实现定向消息发送: ```csharp // 发送给特定客户端 clientList[index].Send(data); ``` #### 完整示例结构 ``` 服务端窗体控件: - txtLog (TextBox): 显示通信日志 - btnStart (Button): 启动服务 - listClients (ListBox): 显示已连接客户端 客户端窗体控件: - txtMessage (TextBox): 输入发送消息 - txtResponse (TextBox): 显示接收消息 - btnConnect (Button): 连接服务端 - btnSend (Button): 发送消息 ``` > 实现效果:服务端可同时处理多个客户端连接,客户端与服务端可双向通信。实际应用时需添加心跳机制、数据校验等增强功能[^3][^5]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值