阅读目录:数据库
在现今软件开发中,网络编程是很是重要的一部分,本文简要介绍下网络编程的概念和实践。
Socket是一种网络编程接口,它是对传输层TCP、UDP通讯协议的一层封装,经过友好的API暴露出去,方便在进程或多台机器间进行网络通讯。编程
在网络编程中分客户端和服务端两种角色,好比经过打开浏览器访问到挂在Web软件上的网页,从程序角度上来看,即客户端(浏览器)发起了一个Socket请求到服务器端,服务器把网页内容返回到浏览器解析后展现。在客户端和服务端数据通讯前,会进行三次确认才会正式创建链接,也便是三次握手。 windows
TCP/IP协议是网络间通讯的基础协议,在不一样编程语言及不一样操做系统下暴露的Socket接口用法也大同小异,仅是其内部实现有所不一样,好比Linux下的epoll和windows下的IOCP。数组
IPEndPoint ip = new IPEndPoint(IPAddress.Any, 6389); Socket listenSocket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp); listenSocket.Bind(ip); listenSocket.Listen(100); listenSocket.Accept();
listen函数中有个int类型参数,它表示最大等待处理链接的数量,表示已创建链接但还未处理的数量,每调用Accept函数一下即从这个等待队列中拿出一个链接。 一般服务端要服务多个客户端请求的链接,因此会循环从等待队列中拿出链接,进行接收发送。 浏览器
while (true) { var accept= listenSocket.Accept(); accept.Receive(); accept.Send(); }
上面的服务端程序处理接收和发送消息都是在当前线程下完成的,这意味着要处理完一个客户端链接后才能去处理下一个链接,若是当前链接是进行数据库或者文件读取写入等IO操做,那会极大浪费服务器的CPU资源,下降了服务器吞吐量。服务器
while (true) { var accept = listenSocket.Accept(); ThreadPool.QueueUserWorkItem((obj) => { byte[] receive = new byte[100]; accept.Receive(receive); byte[] send = new byte[100]; accept.Send(receive); }); }
如例子中,当监听到有新链接请求过来时,调用Accept()取出当前链接的socket,使用新的线程去处理接收和发送信息,这样服务端就能实现并发处理多个客户端了。 上述代码中,在高并发下实际上是有问题的,若是客户端链接请求成千上万个,那线程数量也会有这么多,每一个线程的栈空间都须要消耗部份内存,再加上线程上下文切换,容易致使服务器负载太高,吞吐量大大降低,严重时会引发宕机。 当前例子中使用系统ThreadPool的话,线程数量会固定在一个数量上,默认是1000,不会无限制开线程,会把处理超出线程数量的请求放到线程池中的队列上面。
网络
在unix下相似的实现有2种:多线程
fork一个新进程去处理客户端的链接:并发
var connfd = Accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len); var m = fork(); if(m == 0) { //do something }
建立一个新的线程处理限流:socket
var *clientsockfd = accept(serversockfd,(struct sockaddr *)&clientaddress, (socklent *)&clientlen); if(pthreadcreate(&thread, NULL, recdata, clientsockfd)!=0) { //do something }
上述例子中使用的便是该模型,使用起来简单方便。
while (true) { var accept = listenSocket.Accept(); byte[] receive = new byte[100]; accept.Receive(receive); byte[] send = new byte[100]; accept.Send(receive); }
从调用Receive函数起到接受到客户端发过来的数据期间,该函数会一直阻塞等待着,这个阻塞期间处理流程以下:
至此处理成功,开始处理下一个链接请求。 调用发送函数一样会阻塞在当前,而后把用户缓冲区(send字节数组)数据拷贝到内核中TCP发送缓冲区中。 TCP的发送缓冲区也有必定的大小限制,若是发送的数据大于该限制,send函数会一直等待发送缓冲区有空闲时彻底拷贝完才会返回,继续处理后续链接请求。