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

这篇具有很好参考价值的文章主要介绍了C#知识点-13(进程、多线程、使用Socket实现服务器与客户端通信)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

进程

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

            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实现服务器与客户端之间的通信

服务器:
服务器样式截图:C#知识点-13(进程、多线程、使用Socket实现服务器与客户端通信),c#,开发语言

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);
                }
                
            }
        }
    }
}

客户端
客户端样式截图:
C#知识点-13(进程、多线程、使用Socket实现服务器与客户端通信),c#,开发语言文章来源地址https://www.toymoban.com/news/detail-835250.html

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;
        }
    }
}

到了这里,关于C#知识点-13(进程、多线程、使用Socket实现服务器与客户端通信)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux知识点 -- 进程间通信(二)

    先在内存中申请空间,然后将这段空间映射到不同进程的地址空间中,这就叫做共享内存; 一般都是映射在进程的堆栈之间的共享区; 共享内存不属于任何一个进程,它属于操作系统; 操作系统对共享内存的管理,是先描述再组织,先通过内核数据结构描述共享内存的属性

    2024年02月14日
    浏览(50)
  • 关于“Python”的核心知识点整理大全13

    目录 6.4.3 在字典中存储字典 6.5 小结 第7章 用户输入和while循环 7.1 函数 input()的工作原理 7.1.1 编写清晰的程序 7.1.2 使用 int()来获取数值输入 7.1.3 求模运算符 7.1.4 在 Python 2.7 中获取输入 7.2 while 循环简介 7.2.1 使用 while 循环 往期快速传送门👆(在文章最后): 6.4.3 在字典中

    2024年02月04日
    浏览(50)
  • 多线程知识点

    例如:一个短视频,一个线程复制管理视频,一个线程负责管理声音,一个线程负责管理弹幕 进程:Process,程序一旦开始运行就是是一个进程 线程:Thread,一个程序运行后,里面就包含了多个线程 真正的多线程是指有多个cpu,即多核。如果是模拟的多线程,即只有一个cpu,在

    2024年02月11日
    浏览(49)
  • 多线程面试相关知识点

    程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的。 当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一

    2024年02月08日
    浏览(45)
  • 多线程基础知识点梳理

    进程(process):进程是计算机中的一个任务,比如打开浏览器、IntelliJ IDEA。 线程(thread):线程是进程内部的子任务。比如IDEA在敲代码的同时还能自动保存、自动导包,都是子线程做的。 进程和线程的关系就是一个进程包含一个或多个线程。 线程是操作系统调度的最小任

    2024年02月04日
    浏览(56)
  • java基础之线程知识点

    操作系统(OS)中并发(同时)执行的多个程序任务 宏观并行,微观串行 在一个时间段内,CPU会将时间段划分为若干个时间片,一个时间片是能被一个程序拥有,且只有拥有时间片的程序才能执行自身内容,所以当时间片的划分足够细小,交替频率足够快,就会形成并行的假象,时间上仍然是

    2024年02月01日
    浏览(50)
  • 【Linux】环境变量与进程优先级知识点

    环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。 PATH : 指定命令的搜索路径 HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录) SHELL : 当前Shell,它的值通常是/bin/bash USER:记录了当前登录的用户是谁 主要以PATH进行学习

    2023年04月23日
    浏览(53)
  • C#基础知识点记录

    视频 calculator.repoort() 加括号表示调用方法 calculator.repoort 表示方法名 c# 了解 1.net 运行平台上,运行vb,c#,c++语言(.net环境为vb,c#,c++封装了各种工具包) .net core跨平台开发(可运行win linux) 相当于java开发工具包jdk上,包含java运行环境(jre,jvm+java系统类库)和java工具 2.c#工具

    2024年02月11日
    浏览(52)
  • 知识点13--spring boot整合elasticsearch以及ES高亮

    本章知识点沿用知识点12的项目,介绍如何使用spring boot整合ES,没有ES的去我主页 各类型大数据集群搭建文档--大数据原生集群本地测试环境搭建三 中可以看到ES如何搭建 不管你有没有ES,最好是没有,因为一定要知道一点,一定要去官网查一下你当前用的spring boot data es的版

    2024年02月12日
    浏览(55)
  • 面试知识点:notify是随机唤醒线程吗(唤醒线程顺序)?

    做 Java 开发的小伙伴,对 wait 方法和 notify 方法应该都比较熟悉,这两个方法在线程通讯中使用的频率非常高,但对于 notify 方法的唤醒顺序,有很多小伙伴的理解都是错误的,有很多人会认为 notify 是随机唤醒的,但它真的是随机唤醒的吗? 带着这个疑问,我们尝试休眠 1

    2024年01月24日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包