• Http协议、线程、线程池


    Socket模拟服务端运行代码:

    1:启动服务端监听的服务,并接受客户端的连接

       1.1 创建Socket

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

       1.2绑定端口和ip地址

       IPAddress ip = IPAddress.Parse(this.txtIp.Text);

          IPEndPoint endPoint=newIPEndPoint(ip,

                             int.Parse(this.txtPort.Text));

       listenSocket.Bind(endPoint);

          1.3开始监听

    2:启动开始接受客户端连接的线程,开始接受客户端的连接工作。

       Accept会阻塞当前主线程(用到多线程),直到有客户端连接上来,并返回  具体通信的Socket。我们必须让当前的监听的Socket不断地执行Accept方  法(放到while循环 中)才能够拿到通信的Socket。

       proxSocket 就是负责跟具体一个客户端的通信,可以通过proxSocket 拿   到远程客户端    的ip和端口。

       Socket proxSocket = listenSocket.Accept();

    3: 服务器端不停的监听客户端的发送来的消息。

       proxSocket.Receive();

       Receive:能阻塞执行此代码的线程。创建一个不断接受客户端连接发送来的消息的线程.   而且Receive方法跟流中Read非常相似,每次读取操作都是从上次读取的节点处继续往  下读取数据。

        Int realLen = proxSocket.Receive(buffer);//返回值是一个具体收到的字节数.可以通过      realLen的长度来半段客户端是否有信息返回,如果realLen的值为0, 代表客户端 退出   的一个信号关闭Socket ,关闭当前的Socket接受数据的线程在客户端集合中移除掉当的    代理对象。

         proxSocket.Shutdown(SocketShutdown.Both);

         proxSocket.Close();//关闭当前的Socket对象

    客户端:socket 如果直接关闭的话,可以直接通过异常来捕捉

    但是如果是通过shutdown的话,本地客户端不会立即关闭,而是往服务端发送空包,表示

    自己已经关闭,并等待服务端响应,会等一定的时间,超时后再关闭socket。

    服务端:可以通过获取的包的长度是0,也就是空包,则直接退出循环,并关闭socket

    扩展(发信息,抖动,文件,)

    发送数据中:第一个字节如果是0,代表字符串

                如果是1:代表闪屏

               如果是2:代表文件

                给数据数组添加 数据类型的标志位

             byte[] realData = new byte[data.Length +1];

                realData[0] = 0;设置是字符串类型

                把一个块拷贝到另外一个

                Buffer.BlockCopy(data,0,realData,1,data.Length);      

    客户端连接服务器:

    1.创建Socket

    clientSocket=newSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);直接连接服务器:只要下面的方法连接服务没有出现异常,那么就代表连接成功。

    clientSocket.Connect(IPAddress.Parse(this.txtIp.Text),int.Parse(this.txtPort.Text));

    2.接受服务器发送的消息:

    创建新线程不断接受服务器发来的消息(首先判断第一个字节是0,1,2)

    recieveThread = new Thread(new ParameterizedThreadStart(this.ProcessReciveData));

                recieveThread.IsBackground = true;

                recieveThread.Start(null);

    3.窗体关闭之前,先关闭Socket

                recieveThread.Abort();//关闭一直在接收数据的线程

                if (clientSocket.Connected)

                {

                    clientSocket.Shutdown(SocketShutdown.Both);                             

    clientSocket.Close();

                }

    HTTP协议简单流程:

    在浏览器中输入地址,将数据封装成了HTTP协议里面规定的请求数据的格式:

    GET/Login/LogOn HTTp/1.1

    Accept:text/html,application/xhtml+xml,*/*

    Accept-Language:zh-CN

    User-Agent:MOzilla/5.0(compatible;MSIE 9.0);

    Qdesk 2.5.1270.201; Windows NT 6.1; Trident/5.0)

    Accept-Encoding:gzip,deflate

    Host:localhost:8094

    Connection:Keep-Alive

    Cookie:txtName=ertertre;

    通过Socket发送到服务端,服务端对报文经行解析,处理,响应,再发送到客户端,

    客户端解析响应保温,然后渲染html页面和css,js等;

    托管和非托管

    托管:中间代码交给clr解析成编译成操作系统能识别的二进制码的代码

    非托管:直接将二进制码交给操作系统化内核执行的代码

    线程:

    初始化线程非常消耗资源(显示的东西少时,不建议创建线程,可以用线程池)

    前台和后台线程

    1.默认线程是一个主线程,是一个前台线程

    2.前台线程都执行完毕才能结束进程

    3.一般情况下使用的线程的时候都要用后台线程

    线程的一些方法

    1.设置后台线程

         thread.IsBackground=true;//如果设置为true表示当前线程为后台线程:当程序中所有的前台线程都退出之后,那么主进程就退出了,后台线程不阻塞退出

    2.给线程取名

         thread.Name="给开发人员用的,用来区分不同的线程";

    3.准备好,让操作系统执行

      thread.Start();//只是告诉操作系统:我怎备好了

    4.建议的设置线程的优先级(具体还的由操作系统决定,操作系统的线程优先级是最高的)

      thread.priority=Threadpriority.AbveNormal;//给当前线程设置一个优先级

    5. 关闭线程  (关闭的线程就不能在重新进行使用)

     第一种

    这种一般是在线程中出现了异常,正常情况不能关闭的时候(不建议使用)

    出现异常的时候会很大的影响程序的性能(这个关闭就不能在使用了)

             thread.Abort();//直接关闭线程:内部会抛出一个异常,尽量不要用,这样会导致系统内部抛出异常,但不会导致程序崩溃,clr帮我们捕获了此异常

    第二种

    控制信号体关闭线程,即设置一个标签控制线程的停止,而不是调用上面的那个方法,这样线程还能进行使用

    6.设置等待时间

         Thread.Sleep(2000);

    7.主线程等待子线程结束后在执行,可以填写参数规定等待时间

      thread.Join();//那个线程调用此方法,就让哪个线程等待thread线程。

    8.得到当前线程的id号

     Thread.currentThread.ManagedThreadId;

    线程的使用

    1.线程的简单的写法

       new Thread(()=>

        {

                 Console.writeLine("ddd");

         }).Start();

    2.带参数的线程

    2.1传入委托的类型

         ParameterizedThreadstrt

         //pubic delegate void ParameterizedThreadStart(object obj);

    2.2实例化一个

      Thread thread=new Thread(delegate(object obj){ Console.writeLine(obj.Tostring());});

    2.3通过start方法给委托传值

     thread.start(4);

    异步委托(使用的是线程池)

    异步委托就是相当于创建一个新的线程执行这个委托,因为创建一个线程的时候,委托的类型的固定的,异步委托,委托类型由自己决定,而且这个线程由线程池提供(是后台线程)节省资源

    异步委托的使用

    第一种

    1.声明一个委托类型

     public delegate int AddDel(int a,int b);

    2.定义一个这种委托类型的方法

     static int AddDemo(int a, int b)

     {

        return a+b;

    }

    3.让这个委托类型方法异步

           AddDel addDel=new AddDel(addDemo);

          //addDel(4,5);//这样是由当前执行此代码的线程执行 委托指向的方法

         var result= addDel.BeginInvoke(4,5,null,null);//启动一新线程【后台线程,此线程由线程池提供】,新的线程回调函数指向委托的方法

         //new Tread(AddDemo)

    4.Result是异步委托中的一些信息

     //去拿另外一个线程执行方法的结果

     int retval=addDel.EndInvoke(result);//阻塞当前主线程,直到子线程执行完成并返回值。

       
    第二种

    1.在异步中获取返回值 (异步委托函数运行完,执行回调函数)

           addDel.BeginInvoke(5,6,new AsyncCallback(ProcessAfterFunExcute),"s")

    2.回调函数的过程:把上边异步方法的返回值的相关的信息,传入到回调函数中作为参数

         static void ProcessAfterFunExcute(IAsyncResult result)

        {

                //在异步的委托的方法执行完成之后,自动来调用此方法。调用此方法的线程是新的线程

               AsyncResult funAsyncResult=(AsyncResult) result;

               AddDel del=(AddDel) funAsyncResult.AsyncDelegate;//拿到异步的委托的实例

              var retval=del.EndInvoke(funAsyncResult);

    3.打印"s"

              //拿到传来的值

              Console.writeLine(funAsynResult.Asyncstate.Tostring());

    并发和异步

    并发:同一时间执行一个代码或动作

    异步:并不是并发,是时间片轮转

    摇奖

    动态创建的控件默认只能由创建他的线程使用,但是可以设置,别的线程访问

       //禁止 线程操作控件进行检查

          Control.checkForIllegalCrossThreadCalls=false;

    也可以不设置检查,进行判断(专业的方式,判断当前的控件是否是当前的线程创建的)

    用invoke方法

              foreach(var label in lbList)

               {

                        if(label.InvokeRequired)//InvokeRequired属性就是帮我们判断当前线程跟控件之间的关系,如果为true 代表:当前控件不是线程创建的,那么需要进行invoke。如果为false,那么代表当前控件就是当前线程创建的,直接用就行了,不需要Invoke

                 {

                         //invoke方法对控件的操作

                         label.Invoke(new Action<Label,string>(this.setText),label,r.Next(0,10).ToString());//invoke后面的传来的方法体,由创建Lable的线程执行

                }

                    else

                       {

                             //如果当前lable控件就是执行当前代码的线程创建的话,没问题的

                            label.Text=r.Next(0,10).ToString();

                       }

                                   、、Control:跨线程方法,使用第一种方式,禁止校验跨线程访问的方式

                }

                        Thread.Sleep(100);

    线程池

    线程池:为了提高线程使用效率,节省创建线程消耗的资源,提高线程重复利用率,避免用户适应的线程每次都重新创建和分配线程

    线程池的内部结构

    包括,工作项队列(创建线程池时,传入的方法体委托其实放在工作项队列中,不使用的情况下是先进先出。但当调用到线程中执行后,工作项队列多种的委托执行的顺序就是不确定的,无序的),和线程(也包含线程的工作项队列,后进先出,当这个工作线程的栈空的时候,去公共的队列中去取几个工作项)

      线程池的使用

      Threadpool.QueueuserworkItem(0=>{console.writeLiine(o.Tostring());},“1”);

    模拟对象池

    生产者和消费者,利用lock的机制实现了同步,也就是一个线程在使用修改数据的时候别的线程不能使用,必须等解锁以后才能使用

    生产消费内部实现的过程

      在应用程序域的堆中,每个对象都有下面内容:类空间(自己的属性和方法,属于当前的对象),同步索引块(默认为-1,有了lock后相当于加上了一个索引值,指向同步索引数组中的一个变量,lock结束又变回-1),类型指针(每个类的实例的类型指针指向的类型对象,指向类型信息)

  • 相关阅读:
    delphi 静态3维数组。 严重占用堆栈 切记。 应该用动态数组, 非要用静态数组的话, 要在编译器里 把 堆栈 调大
    Softmax 函数的特点和作用是什么?
    笔记本按开机键没反应怎么办
    小鸡 模拟器 支持双手柄。
    windows7 玩 WinKawaks kof2002为什么提示couldn't initialise DirectSound?
    Delphi中堆栈区别
    最让人纠结的等式:0.999...=1
    求 主板型号 945GME
    电脑可以识别sd卡手机无法识别 的解决方法。 我成功了。 淘宝买的sd卡 不用退货了。 退的人肝疼
    【线段树】HDU 5443 The Water Problem
  • 原文地址:https://www.cnblogs.com/reganLi/p/3401375.html
Copyright © 2020-2023  润新知