C#多线程和Socket网络编程学习

进程:我们每一个应用程序都是一个进程,而进程又是由多个线程组成的。

//获取计算机所有进程
        Process[] pros = Process.GetProcesses();
        foreach(var item in pros)
        {
            Console.WriteLine(item);
        }
        //获取当前进程
        Console.WriteLine(Process.GetCurrentProcess().ToString());
        //启动画图进程
        Process.Start("mspaint");

线程分为前台线程和后台线程。

前台线程:只有所有的前台线程都关闭,才能完成程序关闭。

后台线程:只要所有的前台线程结束,后台线程就会自动结束。

Winform窗体示例如下:

using System;
using System.Threading;
using System.Windows.Forms;

namespace Test_窗体
{
    public partial class Form1 : Form
    {
        Thread th;

        public Form1()
        {
            InitializeComponent();
        }

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

        private void button1_Click(object sender, EventArgs e)
        {
            //开启一个新的线程帮助我们执行Test方法
            th = new Thread(Test);
            //设置成后台线程
            th.IsBackground = true;
            //启动线程(注意:线程一旦被关闭,就不能再被打开了)
            th.Start();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            //当关闭窗体的时候,同时关闭我的后台线程
            if (th != null)
            {
                //终止线程
                th.Abort();
            }
        }

        void Test()
        {
            for(int i=0;i<10000;i++)
            {
                textBox1.Text = i.ToString();
            }
        }
    }
}

为什么要用多线程?

1. 让计算机同时做多件事情,节约时间;

2. 后台运行程序,提高程序运行效率,也不会使主界面出现无响应的情况。

注意:Thread.Sleep(3000);可以让当前线程停止3秒后 再运行。

多线程执行带参数的方法:

using System;
using System.Threading;
using System.Windows.Forms;

namespace Test_多线程执行带参数的方法
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //线程执行的方法 如果有参数,那么这个参数必须是object类型
            Thread th = new Thread(Test);
            th.IsBackground = true;
            th.Start("123");
        }

        public void Test(object str)
        {
            string s = (string)str;
            MessageBox.Show(s);
        }

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

使用多线程来实现一个摇奖机

using System;
using System.Threading;
using System.Windows.Forms;

namespace Test_摇奖机
{
    public partial class Form1 : Form
    {
        Thread th;
        bool b = false;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (b == false)
            {
                th = new Thread(GameStart);
                th.IsBackground = true;
                th.Start();
                b = true;
                button1.Text = "暂停";
            }
            else
            {
                b = false;
                button1.Text = "开始";
            }
        }

        public void GameStart()
        {
            Random r = new Random();
            while(b)
            {
                try
                {
                    label1.Text = r.Next(0, 10).ToString();
                    label2.Text = r.Next(0, 10).ToString();
                    label3.Text = r.Next(0, 10).ToString();
                }
                catch
                {

                }
            }
        }

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

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if(th!=null)
            {
                th.Abort();
            }
        }
    }
}

运行结果为:

Socket网络编程

人通过【电话】通信;

程序通过【Socket】通信;

故而,Socket(套接字)就是程序间的电话机。

我和另一个人打电话,在电话中规定好的、都听得懂的语言:汉语;

电脑和另一台电脑进行联系(通信),在Socket中 规定好的、都听得懂的语言:HTTP协议。

Socket相关概念:

Socket翻译过来的意思是“孔” “插座”。作为进程通信机制,就选取后一种意思,通常也被称作“套接字”,用于描述IP地址和端口号,是一个通信链的句柄(其实就是两个程序互相通信用的)。

 在Internet上有很多这样的主机,这些主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务(应用程序)。

例如:http协议使用80端口,ftp协议使用21端口,smtp协议使用25端口。我们自己使用端口的时候一般采用50000以后的端口,避免重用端口。

Socket有两种类型:

1. 流式Socket(Stream):是一种面向连接的Socket,针对于面向连接的TCP服务应用,安全但是效率低;

2. 数据报式Socket(Datagram):是一种无连接的Socket,对应于无连接的UDP服务应用,不安全(易丢包,顺序混乱,在接收端要分析重排及要求重发),但效率高。

 服务端的Socket(至少需要两个):

1. 一个负责接收客户端连接请求(但不负责与客户端通信);

2. 每成功接收到一个客户端的连接请求,便在服务端产生一个对应的 负责通信的Socket;

(1)在接收到客户端连接时创建;

(2)为每个连接成功的客户端请求 在服务端创建一个对应的Socket(负责和客户端通信)。

客户端的Socket:

1. 必须指定要连接的服务端地址IP和端口号port;

2. 通过创建一个Socket对象来初始化一个到服务端的TCP连接。

Socket相关类和方法

相关类:

IPAddress类:包含了一个IP地址;

IPEndPoint类:包含了一对IP地址和端口号;

方法:

Socket(); //创建一个Socket;

Bind(); //绑定一个本地的IP和端口号(IPEndPoint);

Listen(); //让Socket侦听传入的连接,尝试并指定侦听队列的容量;

Connect(); //初始化 与另一个Socket的连接;

Accept(); //接收连接并返回一个新的socket;

Send(); //输出数据到Socket;

Receive(); //从Socket中读取数据;

Close(); 关闭Socket(销毁连接);

示例一:使用TCP协议实现服务端与n个客户端(n>=1)进行通信(互相收发数据),0-发送普通文本,1-发送文件,2-发送震动;

服务端代码:

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace Server
{
    public partial class Form1 : Form
    {
        Socket socketSend;

        //将远程连接的客户端的IP地址和Socket存入集合中
        Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();

        public Form1()
        {
            InitializeComponent();
        }

        private void btn_Start_Click(object sender, EventArgs e)
        {
            try
            {
                //创建一个负责监听的Socket
                Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //创建IP地址和端口号
                IPAddress ip = IPAddress.Any;//IPAddress.Parse(txt_IP.Text);
                IPEndPoint endPoint = new IPEndPoint(ip, Convert.ToInt32(txt_Port.Text));
                //让负责监听的Socket绑定IP地址和端口号
                socketWatch.Bind(endPoint);
                ShowMsg("监听成功");
                btn_Start.Enabled = false;
                //设置监听队列
                socketWatch.Listen(10);

                Thread th = new Thread(Listen);
                th.IsBackground = true;
                th.Start(socketWatch);
            }
            catch 
            { }
        }

        /// <summary>
        /// 等待客户端的连接,并创建与之通信用的Socket
        /// </summary>
        void Listen(object o)
        {
            Socket socketWatch = o as Socket;
            //等待客户端的连接 并创建一个负责通信的Socket
            while (true)
            {
                try
                {
                    //负责跟客户端通信的Socket
                    socketSend = socketWatch.Accept();
                    //将远程连接的客户端的IP地址和Socket存入集合中
                    dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);
                    //将远程连接的客户端的IP地址和端口号存入下拉框中
                    comboBox_SelectItem.Items.Add(socketSend.RemoteEndPoint.ToString());

                    ShowMsg(socketSend.RemoteEndPoint.ToString() + "连接成功");
                    //开启一个线程:不停地接收客户端发来的消息
                    Thread th = new Thread(Receive);
                    th.IsBackground = true;
                    th.Start(socketSend);
                }
                catch
                { }
            }
        }

        /// <summary>
        /// 服务端不停地接收客户端发来的消息
        /// </summary>
        /// <param name="o"></param>
        void Receive(object o)
        {
            Socket socketSend = o as Socket;
            while (true)
            {
                try
                {
                    //客户端连接成功后,服务端应该接收客户端发来的消息
                    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 + ":" + str);
                }
                catch 
                { }
            }
        }

        void ShowMsg(string str)
        {
            txt_Log.AppendText(str + "\r\n");
        }

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

        /// <summary>
        /// 服务端给客户端发送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Send_Click(object sender, EventArgs e)
        {
            string str = txt_Msg.Text;
            byte[] buffer = Encoding.UTF8.GetBytes(str);
            List<byte> list = new List<byte>();
            list.Add(0);
            list.AddRange(buffer);
            //将泛型集合转换为数组
            byte[] newBuffer = list.ToArray();

            //根据下拉框是否被选择,来确定:是把消息发给单个客户端,还是所有客户端
            if (comboBox_SelectItem.SelectedIndex==-1)
            {
                //向所有客户端发数据
                foreach(var item in comboBox_SelectItem.Items)
                {
                    string ip = item.ToString();
                    dicSocket[ip].Send(newBuffer);
                }
            }
            else
            {
                //向选择的指定客户端发数据(获得用户在下拉框中选中的IP地址)
                string ip = comboBox_SelectItem.SelectedItem.ToString();
                dicSocket[ip].Send(newBuffer);
            }
        }

        /// <summary>
        /// 选择要发送的文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Select_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.InitialDirectory = @"D:\Program Files";
            ofd.Title = "请选择要发送的文件";
            ofd.Filter = "所有文件|*.*";
            ofd.ShowDialog();
            txt_Path.Text = ofd.FileName;
        }

        /// <summary>
        /// 发送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_SendFile_Click(object sender, EventArgs e)
        {
            //获得要发送的文件路径
            string path = txt_Path.Text;
            using (FileStream fsRead=new FileStream(path,FileMode.Open,FileAccess.Read))
            {
                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);
                byte[] newBuffer = list.ToArray();
                dicSocket[comboBox_SelectItem.SelectedItem.ToString()].Send(newBuffer,0,r+1,SocketFlags.None);

            }
        }

        /// <summary>
        /// 发送震动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Shake_Click(object sender, EventArgs e)
        {
            byte[] buffer = new byte[1];
            buffer[0] = 2;
            dicSocket[comboBox_SelectItem.SelectedItem.ToString()].Send(buffer);
        }
    }
}

客户端代码:

using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace Client
{
    public partial class Form1 : Form
    {
        Socket socketSend;

        public Form1()
        {
            InitializeComponent();
        }

        private void btn_Start_Click(object sender, EventArgs e)
        {
            try
            {
                //创建负责通信的Socket
                socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPAddress ip = IPAddress.Parse(txt_IP.Text);
                IPEndPoint endPoint = new IPEndPoint(ip, Convert.ToInt32(txt_Port.Text));
                //获得要连接的远程服务端应用程序的IP地址和端口号
                socketSend.Connect(endPoint);
                ShowMsg("连接成功");
                btn_Start.Enabled = false;

                //开启一个新的线程 不停地接收服务端发来的消息
                Thread th = new Thread(Receive);
                th.IsBackground = true;
                th.Start();
            }
            catch
            { }
        }

        /// <summary>
        /// 不停地接收服务端发来的消息
        /// </summary>
        void Receive()
        {
            while(true)
            {
                try
                {
                    byte[] buffer = new byte[1024 * 1024 * 3];
                    //实际接收到的有效字节数
                    int r = socketSend.Receive(buffer);
                    if (r == 0)
                    {
                        break;
                    }
                    switch (buffer[0])
                    {
                        case 0://0:表示服务端发送来的是文字类信息
                            string s = Encoding.UTF8.GetString(buffer, 1, r - 1);
                            ShowMsg(socketSend.RemoteEndPoint + ":" + s);
                            break;
                        case 1://1:表示服务端发送来的是文件类信息
                            SaveFileDialog sfd = new SaveFileDialog();
                            sfd.InitialDirectory = @"D:\Program Files";
                            sfd.Title = "请选择要保存的文件";
                            sfd.Filter = "所有文件|*.*";
                            sfd.ShowDialog(this);
                            string path = sfd.FileName;
                            using (FileStream fsWrite=new FileStream(path,FileMode.OpenOrCreate,FileAccess.Write))
                            {
                                fsWrite.Write(buffer, 1, r - 1);
                            }
                            MessageBox.Show("保存成功");
                                break;
                        case 2://2:表示服务端发送来的是震动信息
                            Shake();
                            break;
                    }
                }
                catch
                { }
            }
        }

        /// <summary>
        /// 震动
        /// </summary>
        void Shake()
        {
            for(int i=0;i<500;i++)
            {
                this.Location = new Point(200, 200);
                this.Location = new Point(280, 280);
            }
        }

        void ShowMsg(string str)
        {
            txt_Log.AppendText(str + "\r\n");
        }

        /// <summary>
        /// 客户端给服务端发送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Send_Click(object sender, EventArgs e)
        {
            string str = txt_Msg.Text.Trim();
            byte[] buffer = Encoding.UTF8.GetBytes(str);
            socketSend.Send(buffer);
        }

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

运行结果为:

(1)收发文本类数据

(2)收发文件类数据

(3)收发震动数据

源码链接:https://pan.baidu.com/s/1vsPxxwvqWwVPxgxuQHvRnQ
提取码:15sv

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林枫依依

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值