C#知识点-13(进程、多线程、使用Socket实现服务器与客户端通信)

news2024/11/15 5:04:56

进程

定义:每一个正在运行的应用程序,都是一个进程 
进程不等于正在运行的应用程序。而是为应用程序的运行构建一个运行环境

            Process[] pros = Process.GetProcesses();//获取电脑中所有正在运行的进程

            //通过进程,直接打开文件
            //告诉进程,要打开的文件路径,通过PSI对象进行封装
            ProcessStartInfo psi = new ProcessStartInfo(@"C:\Users\ThinkPad\Desktop\1.txt");
            Process p = new Process();
            p.StartInfo = psi;
            p.Start();

多线程

        private void button1_Click(object sender, EventArgs e)
        {
            Test();
        }
        private void Test()
        {
            for (int i = 0; i < 100000; i++)
            {
                textBox1.Text = i.ToString();
            }
        }

这段代码在执行完成之前,程序会被卡死(不能操作程序,包括关闭窗口)。因为我们程序在做一些耗时操作的时候,如果主线程去执行某段代码,就没有其余的“精力”去完成其他的操作了。
这时候,我们就需要用到多线程,再新建一个线程来完成耗时操作

        private void button1_Click(object sender, EventArgs e)
        {
            Thread th = new Thread(Test);
            th.Start();
        }
        private void Test(object str)//如果线程执行的方法,需要参数,我们要求参数的类型必须是object类型
        {
            for (int i = 0; i < 100000; i++)
            {
                textBox1.Text = i.ToString();
            }
        }

但是使用多线程,也会有很多需要注意的地方。这段代码执行时会提示异常,显示“线程间操作无效: 从不是创建控件“textBox1”的线程访问它。”
因为创建textBox1的是主线程,而你创建了一个新的线程th,th调用Test方法会访问主线程创建的控件,这个操作默认是不允许的,我们不允许跨线程的访问,因为这是不安全的。

而如果强制要跨线程访问的话,使用下面这段代码在主窗体加载的时候

        private void Form1_Load(object sender, EventArgs e)
        {
            //取消跨线程访问的检查
            Control.CheckForIllegalCrossThreadCalls = false;
        }

但是这样还是有问题,当你运行程序的时候,点击按钮开始计数。如果你在计数途中点击叉号关闭程序,程序还是再运行。这是因为你关闭了主线程(主窗体),但是另一个线程th还在执行。
这时候我们把th这个线程由前台线程转换为后台线程。
前台线程:只有所有的前台线程关闭,程序才关闭
后台线程:只要所有的前台线程结束,后台线程自动结束

        private void button1_Click(object sender, EventArgs e)
        {
            Thread th = new Thread(Test);
            th.IsBackground = true;//将th线程设置为后台线程
            th.Start();
        }

这时候,没有完成计数时关闭程序后会显示异常“创建窗口句柄时出错。”这是因为关闭程序的时候,主窗体这个前台线程关闭了,程序会调用Dispose这个方法进行线程的资源释放。但是Test方法可能还没执行完,还在使用主线程提供的textBox1这个空间资源,这时候Test方法发现找不到这个资源了,程序就会抛异常。
按理说不是已经把th变成后台线程了吗,不是只要所有的前台线程结束,后台线程就结束吗?为什么还是会抛这个异常。原因是我们的cpu不一定能及时的解决处理线程的操作,因为线程是告诉cpu,“我这个线程准备好了,随时可以操作”,但是具体啥时候操作,程序员说了不算,还要看cpu的“心情”。
我们想让程序不抛这个异常,只能是在关闭窗口的时候,强制关闭后台线程

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            th.Abort();//程序关闭时,强制后台线程关闭
        }

使用Socket实现服务器与客户端之间的通信

服务器:
服务器样式截图:

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 Server
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            //1、创建一个监听连接的Socket对象socketWatch,使用IPv4,流式传输,Tcp协议
            Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //2、创建一个ip地址
            IPAddress ip = IPAddress.Parse(txtServer.Text);
            //2.1、创建一个端口号
            IPEndPoint point = new IPEndPoint(ip,int.Parse(txtPort.Text));
            //3、将端口号绑定到socketWatch
            socketWatch.Bind(point);
            //4、设置监听队列(同一时刻最多有几台设备同时连接)
            socketWatch.Listen(10);
            ShowMsg("正在等待客户端的连接");
            //5、创建一个新线程th,创建线程用于使用新创建的Socket
            Thread th = new Thread(MyAccept);
            //6、设置th为后台线程
            th.IsBackground = true;
            //7、开启线程th
            th.Start(socketWatch);
        }

        //客户端的IP地址&端口号,服务器与客户端通讯的Socket
        Dictionary<string,Socket> dicSocket = new Dictionary<string,Socket>();
        /// <summary>
        /// 实现客户端与服务器的通讯
        /// </summary>
        /// <param name="o"></param>
        void MyAccept(object o)
        {
            //不停的接收客户端的连接
            while (true)
            {
                //o墙砖为Socket
                Socket socketWatch = o as Socket;
                //为新建连接创建新的与之通信的Socket
                Socket socketTX = socketWatch.Accept();
                //把客户端的IP地址&端口号和与客户端通信的Socket存储到键值对集合中
                dicSocket.Add(socketTX.RemoteEndPoint.ToString(), socketTX);
                //把客户端的ip地址和端口号,存储到下拉框中
                cboUsers.Items.Add(socketTX.RemoteEndPoint.ToString());
                //展示连接的ip地址和端口号
                ShowMsg(socketTX.RemoteEndPoint.ToString() + "连接成功");
                Thread th = new Thread(RecData);
                th.IsBackground = true;
                th.Start(socketTX);
            }
        }
        /// <summary>
        /// 不停的接收客户端的消息
        /// </summary>
        /// <param name="o"></param>
        void RecData(object o)
        {
            Socket socketTX = o as Socket;
            while(true)
            {
                //创建缓存区
                byte[] buffer = new byte[1024 * 1024 * 5];
                //r表示实际接受到的字节数
                int r = socketTX.Receive(buffer);
                //将接收到的字节数组使用系统默认编码格式转换为字符串
                string msg = Encoding.Default.GetString(buffer, 0, r);
                //展示接收到的信息
                ShowMsg(socketTX.RemoteEndPoint.ToString() + ":" + msg);
            }
        }
        /// <summary>
        /// 在文本框中展示信息
        /// </summary>
        /// <param name="msg"></param>
        public void ShowMsg(string msg)
        {
            txtLog.AppendText(msg + "\r\n");
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        //服务器给客户端发消息
        private void btnSend_Click(object sender, EventArgs e)
        {
            string msg = txtMsg.Text.Trim();
            byte[] buffer = Encoding.Default.GetBytes(msg);
            //制作自己的协议 0:文字 1:文件  2:震动
            List<byte> listByte = new List<byte>();
            listByte.Add(0);
            listByte.AddRange(buffer);
            //以字节形式发送个客户端的数据,第一个字节是0代表发的是文字
            buffer = listByte.ToArray();
            //获取服务器选择的客户端的ip地址
            string ip = cboUsers.SelectedItem.ToString();
            //拿着ip去找对应的socket,然后发送
            dicSocket[ip].Send(buffer);
        }

        //发送震动
        private void btnZD_Click(object sender, EventArgs e)
        {
            byte[] buffer = new byte[1];
            buffer[0] = 2;
            string ip = cboUsers.SelectedItem.ToString();
            dicSocket[ip].Send(buffer);
        }

        //选择文件
        private void btnSelect_Click(object sender, EventArgs e)
        {
            //创建打开文件对话框
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "请选择要发送的文件";
            ofd.Filter = "文本文件|*.txt|多媒体文件|*.wmv|所有文件|*.*";
            //初始化路径
            ofd.InitialDirectory = "E:\\123";
            //设置不允许多选
            ofd.Multiselect = false;
            ofd.ShowDialog();
            //获取用户选择文件的全路径
            string path = ofd.FileName;
            //放到窗体展示出来
            txtPath.Text = path;

        }

        //点击发送文件
        private void btnSendFile_Click(object sender, EventArgs e)
        {
            //获取要发送文件的路径
            string path = txtPath.Text.Trim();
            using (FileStream fsRead = new FileStream(path,FileMode.Open,FileAccess.Read))
            {
                try
                {
                    byte[] buffer = new byte[1024 * 1024 * 5];
                    int r = fsRead.Read(buffer, 0, buffer.Length);
                    List<byte> list = new List<byte>();
                    list.Add(1);
                    list.AddRange(buffer);
                    buffer = list.ToArray();
                    //调用跟客户端通信的socket,发送字节数据
                    string ip = cboUsers.SelectedItem.ToString();
                    dicSocket[ip].Send(buffer, 0, r + 1, SocketFlags.None);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
                
            }
        }
    }
}

客户端
客户端样式截图:

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

namespace Client
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        Socket socket;
        private void btnStart_Click(object sender, EventArgs e)
        {
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ip = IPAddress.Parse(txtServer.Text);
            IPEndPoint point = new IPEndPoint(ip,int.Parse(txtPort.Text));
            socket.Connect(point);
            ShowMsg("连接成功");

            //不停的接收服务器发送过来的消息
            Thread th = new Thread(RecServerData);
            th.IsBackground = true;
            th.Start();
        }

        //不停地接收服务器发送过来的消息
        void RecServerData()
        {
            while (true)
            {
                //连接成功后,接收服务器发送过来的消息
                byte[] buffer = new byte[1024 * 1024 * 5];
                int r = socket.Receive(buffer);
                byte b = buffer[0];
                //对面发送过来的是文字
                if (b==0)
                {
                    string msg = Encoding.Default.GetString(buffer,1,r-1);
                    ShowMsg(msg);
                }
                else if (b==2)//对面发送过来的是震动
                {
                    ZhenDong();
                    SoundPlayer sp = new SoundPlayer();
                    sp.Play();
                }
                else if (b == 1)//对面发送过来的是文件
                {
                    //1、弹出来一个保存文件的对话框
                    SaveFileDialog sfd = new SaveFileDialog();
                    sfd.InitialDirectory = @"E:\123";
                    sfd.Title = "请选择要保存的文件路径";
                    sfd.Filter = "文本文件|*.txt|媒体文件|*.wmv|所有文件|*.*";
                    sfd.ShowDialog(this);//展示保存对话框
                    //2、用户在对话框中选择要保存文件的路径
                    string savePath = sfd.FileName;
                    //3、FileStream把数据写入到指定的路径下
                    using (FileStream fsWrite = new FileStream(savePath, FileMode.Create, FileAccess.Write))
                    {
                        fsWrite.Write(buffer, 1, r - 1);
                        MessageBox.Show("保存成功!!!!");
                    }
                }
            }
        }

        //窗体震动
        void ZhenDong()
        {
            for (int i = 0; i < 1000; i++)
            {
                this.Location = new Point(300, 300);
                this.Location = new Point(320, 320);
            }
        }
        void ShowMsg(string msg)
        {
            txtLog.AppendText(msg+"\r\n");
        }
        //客户端给服务器发送消息
        private void btnSend_Click(object sender, EventArgs e)
        {
            string msg = txtMsg.Text.Trim();
            byte[] buffer = Encoding.Default.GetBytes(msg);
            socket.Send(buffer);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Control.CheckForIllegalCrossThreadCalls = false;
        }
    }
}

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

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

相关文章

ChatGPT丨成像光谱遥感技术中的AI革命:ChatGPT应用指南

遥感技术主要通过卫星和飞机从远处观察和测量我们的环境&#xff0c;是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型&#xff0c;在理解和生成人类语言方面表现出了非凡的能力。本课程重点介绍ChatGPT在遥感中的应用&#xff0c;人工智…

前端构建效率优化之路

项目背景 我们的系统&#xff08;一个 ToB 的 Web 单页应用&#xff09;前端单页应用经过多年的迭代&#xff0c;目前已经累积有大几十万行的业务代码&#xff0c;30 路由模块&#xff0c;整体的代码量和复杂度还是比较高的。 项目整体是基于 Vue TypeScirpt&#xff0c;而构…

18. 四数之和 - 力扣(LeetCode)

问题描述 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#xff09;&#xff1a; …

如何实现多级缓存?

冗余设计是在系统或设备完成任务起关键作用的地方&#xff0c;增加一套以上完成相同功能的功能通道&#xff08;or 系统&#xff09;、工作元件或部件&#xff0c;以保证当该部分出现故障时&#xff0c;系统或设备仍能正常工作&#xff0c;以减少系统或者设备的故障概率&#x…

板块一 Servlet编程:第七节 ServletContext对象全解与Servlet三大域对象总结 来自【汤米尼克的JAVAEE全套教程专栏】

板块一 Servlet编程&#xff1a;第七节 ServletContext对象全解与Servlet三大域对象总结 一、什么是ServletContext对象二、获取ServletContext对象及常用方法&#xff08;1&#xff09;获取 ServletContext 对象&#xff08;2&#xff09;ServletContext对象提供的方法 三、se…

第六十六天 API安全-接口安全阿里云KEY%postmanDVWSXEE鉴权泄露

第66天 API安全-接口安全&阿里云KEY%postman&DVWS&XEE&鉴权&泄露 知识点 1.HTTP类接口-测评 2.RPC类接口-测评 3.Web Service类-测评 参考链接&#xff1a;https://www.jianshu.com/p/e48db27d7c70 内容点&#xff1a; SOAP(Simple Object Access Prot…

深度学习基础——GAN生成对抗网络

生成对抗网络(GAN)的简介 生成对抗网络GAN(Generative adversarial networks)是Goodfellow等在2014年提出的一种生成式模型。GAN在结构上受博弈论中的二元零和博弈(即二元的利益之和为零&#xff0c;一方的所得正是另一方的所失)的启发&#xff0c;系统由一个生成器和一个判别器…

Linux Docker 关闭开机启动

说说自己为什么需要关闭自启动&#xff1a;Linux中安装Docker后&#xff0c;自启动会占用80和443端口&#xff0c;然后使用自己的SSL认证&#xff0c;导致自己Nginx配置的SSL认证失效&#xff0c;网站通过https打开显示不安全。 Docker是一个容器化平台&#xff0c;它可以让开…

Android全新UI框架之常用ComposeUI组件

在Compose中&#xff0c;每个组件都是一个带有Composable注解的函数&#xff0c;被称为Composable。Compose已经预置了很多基于MD设计规范的Composable组件。 在布局方面&#xff0c;Compose提供了Column、Row、Box三种布局组件(感觉跟flutter差不多)&#xff0c;类似于传统视图…

【Linux】日志命令行练习(持续更新)

文章目录 前言环境情景1. 获取实时日志2. 关键字定位3. 关键字取并集4. 关键字取交集5. 关键字取差集6. 关键字实时日志捕获7. 关键词上下文打印8. 关键词滚动搜索9. 看最早的日志信息 前言 公司生产问题需要登录堡垒机排查。 没有日志平台的情况下&#xff0c;生产问题同样要…

maven工程打包引入本地jar包

1、通过maven生成本地区仓库包 mvn install:install-file --settings D:\lkx\download\apache-maven-3.6.3\conf\settings.xml -Dfileaspose-cad-21.8.jar -DartifactIdaspose-cad -DgroupIdsystem.core -Dversion21.8 -Dpackagingjar -DgeneratePomtrue # --settings&#xf…

迈向三维:vue3+Cesium.js三维WebGIS项目实战--持续更新中

写在前面&#xff1a;随着市场对数字孪生的需求日益增多&#xff0c;对于前端从业者的能力从对框架vue、react的要求&#xff0c;逐步扩展到2D、3D空间的交互&#xff0c;为用户提供更紧密的立体交互。近年来前端对GIS的需求日益增多。本文将记录WebGIS的学习之旅&#xff0c;从…

回归分析中的异方差性

在简单线性回归或多元线性回归中&#xff0c;我们对误差项做了一些基本假设。 简单线性回归&#xff1a; 多元线性回归&#xff1a; 假设条件&#xff1a; 1.误差均值为零 2.误差具有恒定方差 3.误差不相关 4.误差呈正态分布 第2个假设称为同方差性&#xff0c;因此&…

【开源】JAVA+Vue.js实现大病保险管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统配置维护2.2 系统参保管理2.3 大病保险管理2.4 大病登记管理2.5 保险审核管理 三、系统详细设计3.1 系统整体配置功能设计3.2 大病人员模块设计3.3 大病保险模块设计3.4 大病登记模块设计3.5 保险审核模块设计 四、…

使用JDBC操作数据库(IDEA编译器)

目录 JDBC的本质 ​ JDBC好处 JDBC操作MySQL数据库 1.创建工程导入驱动jar包 2.编写测试代码 ​相关问题 JDBC的本质 官方(sun公司) 定义的一套操作所有关系型数据库的规则&#xff0c;即接口各个数据库厂商去实现这套接口&#xff0c;提供数据库驱动jar包我们可以使用这…

Docker Desktop 4.27.1 Windows 10 安装 教程

Docker Desktop 4.27.1 Windows 10 安装 版本要求windows 版本要求wsl 版本要求docker desktop 版本 安装首先确保系统版本符合要求前提下安装wsl安装 Dockers Desktop安装说明 安装问题docker Desktop 无法正常启动&#xff0c;提示wsl 相关信息wsl --install 执行输出帮助日志…

Stable Diffusion 模型下载:A-Zovya RPG Artist Tools(RPG 大师工具箱)

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八 下载地址 模型介绍 A-Zovya RPG Artist Tools 模型是一个针对 RPG 训练的一个模型&#xff0c;可以生成一些 R…

【深度学习】微调Qwen1.8B

1.前言 使用地址数据微调Qwen1.8B。Qwen提供了预构建的Docker镜像&#xff0c;在使用时获取镜像只需安装驱动、下载模型文件即可启动Demo、部署OpenAI API以及进行微调。 github地址&#xff1a;GitHub - QwenLM/Qwen: The official repo of Qwen (通义千问) chat & pretr…

Maven的下载安装配置教程

一、简单了解一下什么是Maven Maven就是一款帮助程序员构建项目的工具&#xff0c;我们只需要告诉Maven需要哪些Jar 包&#xff0c;它会帮助我们下载所有的Jar&#xff0c;极大提升开发效率。 1.Maven翻译为“专家“&#xff0c; ”内行”的意思&#xff0c;是著名Apache公司下…

通过盲注脚本复习sqllabs第46关order by 注入

在MySQL支持使用ORDER BY语句对查询结果集进行排序处理&#xff0c;使用ORDER BY语句不仅支持对单列数据的排序&#xff0c;还支持对数据表中多列数据的排序。语法格式如下 select * from 表名 order by 列名(或者数字) asc&#xff1b;升序(默认升序) select * from 表名 or…