Socket编程详解:FrmTCPServer与FrmTCPClient的双向对话

news2024/11/24 19:08:49

目录

预备知识

视频教程

项目前准备知识点

1、服务器端程序的编写步骤

2、客户端程序编写步骤

代码部分 

      1、服务端FrmServer.cs文件

        2、客户端FrmClient.cs文件

        3、启动文件Program.cs

结果展示 


预备知识

请查阅博客http://t.csdnimg.cn/jE4Tp

视频教程

链接:https://pan.baidu.com/s/13fkwlppoP9aYcXHiFEbKGQ?pwd=cvzn 
提取码:cvzn

项目前准备知识点


1、服务器端程序的编写步骤

第一步:调用socket()函数创建一个用于通信的套接字。

第二步:给已经创建的套接字绑定一个端口号,这一般通过设置网络套接口地址和调用bind()函数来实现。
第三步:调用listen()函数使套接字成为一个监听套接字。
第四步:调用accept()函数来接受客户端的连接,这是就可以和客户端通信了。
第五步:处理客户端的连接请求

第六步:终止连接。

 



2、客户端程序编写步骤

第一步:调用socket()函数创建一个用于通信的套接字。

第二步:通过设置套接字地址结构,说明客户端与之通信的服务器的IP地址和端口号。
第三步:调用connect()函数来建立与服务器的连接。
第四步:调用读写函数发送或者接收数据。
第五步:终止连接。

 

代码部分 

      1、服务端FrmServer.cs文件

        FrmServer.cs窗体

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

namespace SocketTCP
{
    //声明委托
    delegate void AddOnLineDelegate(string str, bool bl);

    //声明委托
    delegate void RecMsgDelegate(string str);

    public partial class FrmTCPServer : Form
    {
        public FrmTCPServer()
        {
            InitializeComponent();
            myAddOnline = AddOnline;
            myRcvMsg = RecMsg;
            myFileSave = FileSave;
        }

        //创建套接字
        Socket sock = null;

        //创建负责监听客户端连接的线程
        Thread threadListen = null;

        //创建URL与Socket的字典集合
        Dictionary<string, Socket> DicSocket = new Dictionary<string, Socket>();

        AddOnLineDelegate myAddOnline;

        RecMsgDelegate myRcvMsg;

        FileSaveDelegate myFileSave;

        #region 开始监听
        /// <summary>
        /// 开始监听
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_StartServer_Click(object sender, EventArgs e)
        {
            //创建负责监听的套接字,注意其中参数:IPV4 字节流 TCP
            sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            IPAddress address = IPAddress.Parse(this.txt_IP.Text.Trim());

            //根据IPAddress以及端口号创建IPE对象
            IPEndPoint endpoint = new IPEndPoint(address, int.Parse(this.txt_Port.Text.Trim()));

            try
            {
                sock.Bind(endpoint);
                Invoke(myRcvMsg, "服务器开启成功!");
                MessageBox.Show("开启服务成功!", "打开服务");
            }
            catch (Exception ex)
            {
                MessageBox.Show("开启服务失败" + ex.Message, "打开服务");
                return;
            }

            sock.Listen(10);

            threadListen = new Thread(ListenConnecting);
            threadListen.IsBackground = true;
            threadListen.Start();
            this.btn_StartServer.Enabled = false;
        }
        #endregion

        #region 监听线程
        /// <summary>
        /// 监听线程
        /// </summary>
        private void ListenConnecting()
        {
            while (true)
            {
                //一旦监听到一个客户端的连接,将会创建一个与该客户端连接的套接字
                Socket sockClient = sock.Accept();

                string client = sockClient.RemoteEndPoint.ToString();

                DicSocket.Add(client, sockClient);

                Invoke(myAddOnline, client, true);
                Invoke(myRcvMsg, client + "上线了!");
                //开启接受线程
                Thread thr = new Thread(ReceiveMsg);
                thr.IsBackground = true;
                thr.Start(sockClient);

            }
        }
        #endregion

        #region 接收线程
        /// <summary>
        /// 接收线程
        /// </summary>
        /// <param name="sockClient"></param>
        private void ReceiveMsg(object sockClient)
        {
            Socket sckclient = sockClient as Socket;
            while (true)
            {
                //定义一个2M缓冲区
                byte[] arrMsgRec = new byte[1024 * 1024 * 2];

                int length = -1;

                try
                {
                    length = sckclient.Receive(arrMsgRec);
                }
                catch (Exception)
                {
                    string str = sckclient.RemoteEndPoint.ToString();
                    Invoke(myRcvMsg, str + "下线了!");
                    //从列表中移除URL
                    Invoke(myAddOnline, str, false);
                    DicSocket.Remove(str);
                    break;
                }

                if (length == 0)
                {
                    string str = sckclient.RemoteEndPoint.ToString();
                    Invoke(myRcvMsg, str + "下线了!");
                    //从列表中移除URL
                    Invoke(myAddOnline, str, false);
                    DicSocket.Remove(str);
                    break;
                }
                else
                {
                    if (arrMsgRec[0] == 0)
                    {
                        string strMsg = Encoding.UTF8.GetString(arrMsgRec, 1, length-1);
                        string Msg = "[接收]     " + sckclient.RemoteEndPoint.ToString() + "     " + strMsg;
                        Invoke(myRcvMsg, Msg);
                    }
                    if (arrMsgRec[0] == 1)
                    {
                        Invoke(myFileSave, arrMsgRec,length);

                    }
                }
            }
        }
        #endregion

        #region 委托方法体
        private void AddOnline(string url, bool bl)
        {
            if (bl)
            {
                this.lbOnline.Items.Add(url);
            }
            else
            {
                this.lbOnline.Items.Remove(url);
            }
        }

        private void RecMsg(string str)
        {
            this.txt_Rcv.AppendText(str + Environment.NewLine);
        }

        private void FileSave(byte[] bt, int length)
        {
            try
            {
                SaveFileDialog sfd = new SaveFileDialog();
                sfd.Filter = "word files(*.docx)|*.docx|txt files(*.txt)|*.txt|xls files(*.xls)|*.xls|All files(*.*)|*.*";
                if (sfd.ShowDialog() == DialogResult.OK)
                {
                    string fileSavePath = sfd.FileName;

                    using (FileStream fs = new FileStream(fileSavePath, FileMode.Create))
                    {
                        fs.Write(bt, 1, length - 1);
                        Invoke(new Action(() => this.txt_Rcv.AppendText("[保存]     保存文件成功" + fileSavePath + Environment.NewLine)));
                    }

                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("保存异常" + ex.Message, "保存文件出现异常");
            }
        }

        #endregion

        #region 发送消息
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_SendToSingle_Click(object sender, EventArgs e)
        {
            string StrMsg = this.txt_Send.Text.Trim();
            byte[] arrMsg = Encoding.UTF8.GetBytes(StrMsg);

            byte[] arrSend = new byte[arrMsg.Length + 1];
            arrSend[0] = 0;
            Buffer.BlockCopy(arrMsg, 0, arrSend, 1, arrMsg.Length);


            if (this.lbOnline.SelectedItems.Count == 0)
            {
                MessageBox.Show("请选择你要发送的对象!", "发送提示");
                return;
            }
            else
            {
                foreach (string item in this.lbOnline.SelectedItems)
                {
                    DicSocket[item].Send(arrSend);

                    string Msg = "[发送]     " + item + "     " + StrMsg;

                    Invoke(myRcvMsg, Msg);
                }
            }
        }
        #endregion

        #region 群发消息
        /// <summary>
        /// 群发消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_SendToAll_Click(object sender, EventArgs e)
        {
            string StrMsg = this.txt_Send.Text.Trim();
            byte[] arrMsg = Encoding.UTF8.GetBytes(StrMsg);

            byte[] arrSend = new byte[arrMsg.Length + 1];
            arrSend[0] = 0;
            Buffer.BlockCopy(arrMsg, 0, arrSend, 1, arrMsg.Length);

            foreach (string item in this.DicSocket.Keys)
            {
                DicSocket[item].Send(arrSend);

                string Msg = "[发送]     " + item + "     " + StrMsg;

                Invoke(myRcvMsg, Msg);
            }
            Invoke(myRcvMsg, "[群发]     群发完毕!");
        }
        #endregion

        #region 打开客户端
        /// <summary>
        /// 打开客户端
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Client_Click(object sender, EventArgs e)
        {
            FrmTCPClient objFrm = new FrmTCPClient();
            objFrm.Show();
        }
        #endregion

        #region 选择文件
        /// <summary>
        /// 选择文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_SelectFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.InitialDirectory = "D:\\";
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                this.txt_SelectFile.Text = ofd.FileName;
            }
        }
        #endregion

        #region 发送文件
        /// <summary>
        /// 发送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_SendFile_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(txt_SelectFile.Text))
            {
                MessageBox.Show("请选择您要发送的文件!", "发送文件提示");
                return;
            }
            string online = this.lbOnline.Text.Trim();
            if (string.IsNullOrEmpty(online))
            {
                MessageBox.Show("请选择您要发送的对象!", "发送文件提示");
                return;
            }
            using (FileStream fs = new FileStream(txt_SelectFile.Text, FileMode.Open))
            {
                string filename = Path.GetFileName(txt_SelectFile.Text);
                string StrMsg = "发送文件为:" + filename;
                byte[] arrMsg = Encoding.UTF8.GetBytes(StrMsg);

                byte[] arrSend= new byte[arrMsg.Length + 1];
                arrSend[0] =0;
                Buffer.BlockCopy(arrMsg, 0, arrSend, 1, arrMsg.Length);

                DicSocket[online].Send(arrSend);

                byte[] arrfileSend = new byte[1024 * 1024 * 2];
                int length = fs.Read(arrfileSend, 0, arrfileSend.Length);

                byte[] arrfile = new byte[length + 1];
                arrfile[0] = 1;
                Buffer.BlockCopy(arrfileSend, 0, arrfile, 1, length);
          
                DicSocket[online].Send(arrfile);
            }

        }
        #endregion

    }
}

        2、客户端FrmClient.cs文件

           FrmClient.cs窗体

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

namespace SocketTCP
{
    delegate void FileSaveDelegate(byte[] bt,int length);
    public partial class FrmTCPClient : Form
    {
        public FrmTCPClient()
        {
            InitializeComponent();
            MyFileSave = FileSave;
        }

        //Socket对象
        Socket sockClient = null;

        //接收线程
        Thread thrClient = null;

        //运行标志位
        private bool IsRunning = true;

        //文件保存委托对象
        FileSaveDelegate MyFileSave;

        #region 连接服务器
        /// <summary>
        /// 连接服务器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Connect_Click(object sender, EventArgs e)
        {
            IPAddress address = IPAddress.Parse(this.txt_IP.Text.Trim());

            IPEndPoint Ipe = new IPEndPoint(address, int.Parse(this.txt_Port.Text.Trim()));

            sockClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                this.txt_Rcv.AppendText("与服务器连接中......" + Environment.NewLine);
                sockClient.Connect(Ipe);
            }
            catch (Exception ex)
            {
                MessageBox.Show("连接失败" + ex.Message, "建立连接");
                return;
            }

            this.txt_Rcv.AppendText("与服务器连接成功" + Environment.NewLine);
            this.btn_Connect.Enabled = false;

            thrClient = new Thread(ReceiceMsg);
            thrClient.IsBackground = true;
            thrClient.Start();
        }

        #endregion

        #region 接收消息
        /// <summary>
        /// 接收消息
        /// </summary>
        private void ReceiceMsg()
        {
            while (IsRunning)
            {
                //定义一个2M缓冲区
                byte[] arrMsgRec = new byte[1024 * 1024 * 2];

                int length = -1;

                try
                {
                    length = sockClient.Receive(arrMsgRec);
                }
                catch (SocketException)
                {
                    break;
                }
                catch (Exception ex)
                {
                    Invoke(new Action(() => this.txt_Rcv.AppendText("断开连接" + ex.Message + Environment.NewLine)));
                    break;
                }

                if (length > 0)
                {
                    //表示接受到的为消息类型
                    if (arrMsgRec[0] == 0)
                    {
                        string strMsg = Encoding.UTF8.GetString(arrMsgRec, 1, length-1);
                        string Msg = "[接收]     " + strMsg + Environment.NewLine;
                        Invoke(new Action(() => this.txt_Rcv.AppendText(Msg)));
                    }
                    //表示接收到的为文件类型
                    if (arrMsgRec[0] == 1)
                    {
                        Invoke(MyFileSave, arrMsgRec,length);
                    }
                }
            }
        }

        #endregion

        #region 委托方法体
        private void FileSave(byte[] bt, int length)
        {
            try
            {
                SaveFileDialog sfd = new SaveFileDialog();
                sfd.Filter = "word files(*.docx)|*.docx|txt files(*.txt)|*.txt|xls files(*.xls)|*.xls|All files(*.*)|*.*";
                if (sfd.ShowDialog() == DialogResult.OK)
                {
                    string fileSavePath = sfd.FileName;

                    using (FileStream fs = new FileStream(fileSavePath, FileMode.Create))
                    {
                        fs.Write(bt, 1, length - 1);
                        Invoke(new Action(() => this.txt_Rcv.AppendText("[保存]     保存文件成功" + fileSavePath+Environment.NewLine)));
                    }

                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("保存异常" + ex.Message, "保存文件出现异常");
            }
        }
        #endregion

        #region 发送消息
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Send_Click(object sender, EventArgs e)
        {
            string strMsg = "来自" + this.txt_Name.Text.Trim() + ":  " + this.txt_Send.Text.Trim();
            byte[] arrMsg = Encoding.UTF8.GetBytes(strMsg);

            byte[] arrSend = new byte[arrMsg.Length + 1];
            arrSend[0] = 0;
            Buffer.BlockCopy(arrMsg, 0, arrSend, 1, arrMsg.Length);

            sockClient.Send(arrSend);
            Invoke(new Action(() => this.txt_Rcv.AppendText("[发送]     " + this.txt_Send.Text.Trim() + Environment.NewLine)));
        }
        #endregion

        #region 窗体关闭事件
        /// <summary>
        /// 窗体关闭事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FrmTCPClient_FormClosing(object sender, FormClosingEventArgs e)
        {
            IsRunning = false;
            sockClient?.Close();
        }
        #endregion

        #region 选择文件
        /// <summary>
        /// 选择文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_SelectFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.InitialDirectory = "D:\\";
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                this.txt_SelectFile.Text = ofd.FileName;
            }
        }
        #endregion

        #region 发送文件
        /// <summary>
        /// 发送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_SendFile_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(txt_SelectFile.Text))
            {
                MessageBox.Show("请选择您要发送的文件!", "发送文件提示");
                return;
            }

            using (FileStream fs = new FileStream(txt_SelectFile.Text, FileMode.Open))
            {
                string filename = Path.GetFileName(txt_SelectFile.Text);
                string StrMsg = "发送文件为:" + filename;
                byte[] arrMsg = Encoding.UTF8.GetBytes(StrMsg);

                byte[] arrSend = new byte[arrMsg.Length + 1];
                arrSend[0] = 0;
                Buffer.BlockCopy(arrMsg, 0, arrSend, 1, arrMsg.Length);

                sockClient.Send(arrSend);


                byte[] arrfileSend = new byte[1024 * 1024 * 2];
                int length = fs.Read(arrfileSend, 0, arrfileSend.Length);

                byte[] arrfile = new byte[length + 1];
                arrfile[0] = 1;
                Buffer.BlockCopy(arrfileSend, 0, arrfile, 1, length);

                sockClient.Send(arrfile);
            }
        }
        #endregion

    }
}

        3、启动文件Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SocketTCP
{
    static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new FrmTCPServer());
        }
    }
}

结果展示 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1872570.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

面经总结系列(六): 奇安信技术研究院算法工程师

&#x1f468;‍&#x1f4bb;作者简介&#xff1a; CSDN、阿里云人工智能领域博客专家&#xff0c;新星计划计算机视觉导师&#xff0c;百度飞桨PPDE&#xff0c;专注大数据与AI知识分享。✨公众号&#xff1a;GoAI的学习小屋 &#xff0c;免费分享书籍、简历、导图等&#xf…

健身馆预约小程序定制搭建会员管理系统次卡核销充值年卡saas账号

健身馆预约小程序定制搭建&#xff1a;打造高效会员管理系统 &#x1f3cb;️ 一、引言&#xff1a;为何需要健身馆预约小程序&#xff1f; 随着健康意识的提高&#xff0c;越来越多的人选择到健身馆进行锻炼。然而&#xff0c;传统的健身馆预约方式往往存在诸多不便&#xff…

(上位机APP开发)调用华为云命令API接口给设备下发命令

一、功能说明 通过调用华为云IOT提供的命令下发API接口,实现下面界面上相同的功能。调用API接口给设备下发命令。 二、JavaScript代码 function sendUnlockCommand() {var requestUrl = "https://9bcf4cfd30.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/60…

reactjs18 中使用路由技巧

react18 版本中&#xff0c;路由的用法发生了变化&#xff0c;react18 版本中&#xff0c;路由由 react-router-dom 包提供。与 react-router 包不同的是&#xff0c;react-router-dom 包提供了 createBrowserRouter 方法&#xff0c;该方法可以创建路由对象。总之&#xff0c;…

汽车尾灯(转向灯)电路设计

即当汽车进行转弯时,司机打开转向灯,尾灯会根据转向依次被点亮,经过一定的间隔后,再全部被消灭。不停地重复,直到司机关闭转向灯。 该效果可由以下电路实现: 完整电路图: 02—电路设计要点 延时电路的要点主要有两个: 一、当转向开关被按下时,LED需要逐个亮起; 二、LED被逐…

商业智能(BI)实战项目

商业智能&#xff08;BI&#xff09;实战项目 期待您的关注 ☀大数据学习笔记 1.实现的功能 2.数据库操作步骤 创建数据库&#xff1a;create database card;创建表&#xff1a;create table card_apply ( cid bigint primary key auto_increment ,apply_uid bigint ,apply_ent…

我的北航MEM成长之旅

领完毕业证&#xff0c;2年的学业生涯到此结束。为了方便大家理解后续的内容&#xff0c;这里我们先解释下基本信息&#xff0c;比如MEM到底是个啥&#xff1f;以及北航的MEM都学什么&#xff1f; 1 MEM解读 1.1 MEM是什么&#xff1f; MEM是"Master of Engineering Ma…

[数据集][目标检测]城市街道井盖破损未盖丢失检测数据集VOC+YOLO格式4404张5类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4404 标注数量(xml文件个数)&#xff1a;4404 标注数量(txt文件个数)&#xff1a;4404 标注…

CentOS 7.9 CDH6.3.2集群生产环境实战部署指南

一、环境准备 1、系统环境&#xff1a; # cat /etc/os-release 2、准备工作&#xff1a; 部署资源分配 节点centos 7.9&#xff08;生产&#xff09;节点规划Postgresql部署组件备注pgsql32c、128G、2TB国产数据库Postgresql&#xff08;翰高&#xff09;可根据实际情况调整…

通达信趋势动能资金加速异动幅图指标公式源码

通达信趋势动能资金加速异动幅图指标公式源码&#xff1a; B:SUM(AMOUNT*CLOSE,1)/SUM(AMOUNT,1); B1:EMA(B,5); TDX5:(B-B1)*100/B,NODRAW,COLORRED; TDX6:TDX5!DRAWNULL; TDX7:(CLOSE-LLV(LOW,13))/(HHV(HIGH,13)-LLV(LOW,13))*100; TDX8:SMA(TDX7,4,1); TDX9:SMA(TDX8,3,1)…

Git Flow 工作流学习要点

Git Flow 工作流学习要点 Git Flow — 流程图Git Flow — 操作指令优点&#xff1a;缺点&#xff1a;Git Flow 分支类型Git Flow 工作流程简述关于 feature 分支关于 Release 分支关于 hotfix 分支 总结 Git Flow — 流程图 图片来源&#xff1a;https://nvie.com/posts/a-succ…

电子名片小程序源码系统 前后端分离 带完整的安装代码包以及搭建教程

系统概述 电子名片小程序源码系统是一款基于前后端分离架构的综合性平台&#xff0c;旨在为用户提供一个集销售名片和企业商城于一体的解决方案。该系统采用先进的技术手段&#xff0c;实现了个性化名片设计、便捷的销售功能、企业商城模块等一系列实用功能。同时&#xff0c;…

惠普笔记本双指触摸不滚屏

查看笔记本型号 一般在笔记本背面很小的字那里 进入惠普官网 笔记本、台式机、打印机、墨盒与硒鼓 | 中国惠普 (hp.com) 选择“支持”>“解决问题”>“软件与驱动程序” 选择笔记本 输入型号&#xff0c;选择操作系统 下载驱动进行完整 重启之后进行测试

HBase与Hive数据交互

一、hbase数据导入hive hive通过建立外部表和普通表加载hbase表数据到hive表中。 两种方式加载hbase中的表到hive中&#xff0c;一是hive创建外部表关联hbase表数据&#xff0c;是hive创建普通表将hbase的数据加载到本地。 1.创建外部表 hbase中创建test表&#xff0c;且插入…

stylelint 配置

1.vscode 安装插件Stylelint 2.项目安装插件 pnpm i stylelint stylelint-config-standard stylelint-config-recommended-scss stylelint-config-recommended-vue postcss postcss-html postcss-scss stylelint-config-recess-order stylelint-config-html -D 依赖 说明 备…

python笔记----少儿编程课程

第1课&#xff1a; 认识新朋友-python 知识点&#xff1a; 1、在英文状态下编写Python语句。 2、内置函数print()将结果输出到标准的控制台上&#xff0c;它的基本语法格式如下&#xff1a; print("即将输出的内容") #输出的内容要用引号引起来&#xff0c;可…

一加Ace3 刷机救砖简化说明

注意&#xff1a;工具使用英文目录&#xff0c;支持救砖和降级。PJE110国行版&#xff0c;CPH2609国际版。目前国行版不能完美转换国际版&#xff0c;每次升级都需要刷oplusstanvbk&#xff0c;不建议使用。跨国转换或ROOT一定先解锁Bootloader&#xff0c;可以使用“一加全能工…

重温react-10(函数组件和类组件的ref获取方式)

App.js的代码 06是函数组件 07是类组件 import React, { useEffect, useRef } from react; import LearnFunction06 from ./LearnFunction06; // 函数组件和类组件的ref使用方式 import LearnFunction07 from ./LearnFunction07; // 函数组件和类组件的ref使用方式 export de…

Vite: 插件开发

概述 说到自定义的能力&#xff0c;肯定很容易想到 插件机制 &#xff0c;利用一个个插件来扩展构建工具自身的能力虽然 Vite 的插件机制是基于 Rollup 来设计的&#xff0c;但实际上 Vite 的插件机制也包含了自己独有的一部分&#xff0c;与Rollup 的各个插件 Hook 并非完全兼…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第50课-姿式识别控制机器人

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第50课-姿式识别控制机器人 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界…