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),类型指针(每个类的实例的类型指针指向的类型对象,指向类型信息)