当前位置: 首页 > news >正文

设计做网站哪家公司好怎么做百度推广平台

设计做网站哪家公司好,怎么做百度推广平台,免费ppt制作,wordpress增加产品纸上得来终觉浅,绝知此事要躬行~ Echo 网络上的两个程序通过一个双向的通信连接实现数据交换,这个连接的一端称为一个Socket “端口”是英文port的意译,是设备与外界通信交流的出口。每台计算机可以分配0到65535共65536个端口 每一条Sock…

纸上得来终觉浅,绝知此事要躬行~

Echo

网络上的两个程序通过一个双向的通信连接实现数据交换,这个连接的一端称为一个Socket

端口”是英文port的意译,是设备与外界通信交流的出口。每台计算机可以分配0到65535共65536个端口

每一条Socket连接代表着本地Socket→本地端口→网络介质→远程端口→远程Socket的链路

Socket通信的基本流程

  • 开启一个连接之前,需要创建一个Socket对象(使用API Socket)​,然后绑定本地使用的端口(使用API Bind)​。对客户端而言,连接时(使用API Connect)会由系统分配端口,可以省去绑定步骤。
  • 对客户端而言,连接时(使用API Connect)会由系统分配端口,可以省去绑定步骤。
  • 客户端连接服务器(使用API Connect)
  • 服务器接受连接(使用API Accept)
  • 客户端和服务端通过Send和Receive等API收发数据,操作系统会自动完成数据的确认、重传等步骤,确保传输的数据准确无误。
  • 某一方关闭连接(使用API Close)​,操作系统会执行“四次挥手”的步骤,关闭双方连接
    using System.Collections;using System.Collections.Generic;using UnityEngine;using System.Net.Sockets;using UnityEngine.UI;public class Echo : MonoBehaviour {//定义套接字Socket socket;//UGUIpublic InputField InputFeld;public Text text;//点击连接按钮//客户端通过socket.Connect(远程IP地址,远程端口)连接服务端。Connect是一个阻塞方法,程        //序会卡住直到服务端回应(接收、拒绝或超时)​。public void Connection(){//Socketsocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);//这一行用于创建一个Socket对象,它的三个参数分别代表地址族、套接字类型和协议。//Connectsocket.Connect("127.0.0.1", 8888);}//点击发送按钮//客户端通过socket.Send发送数据,这也是一个阻塞方法。该方法接受一个byte[​]类型的参数指明    //要发送的内容。Send的返回值指明发送数据的长度(例子中没有使用)​。程序用 //System.Text.Encoding.Default.GetBytes(字符串)把字符串转换成byte[​]数组,然后发送给服 //务端。public void Send(){//Sendstring sendStr = InputFeld.text;byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);socket.Send(sendBytes);//Recvbyte[] readBuff = new byte[1024];//客户端通过socket.Receive接收服务端数据。Receive也是阻塞方法,没有收到服务端数据 //时,程序将卡在Receive不会往下执行。Receive带有一个byte[​]类型的参数,它存储接收到 //的数据。Receive的返回值指明接收到数据的长度。之后使用System.Text.Encoding. //Default.GetString(readBuff,0, count)将byte[​]数组转换成字符串显示在屏幕上。int count = socket.Receive(readBuff);string recvStr = System.Text.Encoding.Default.GetString(readBuff, 0, count);text.text = recvStr;//Closesocket.Close();}}

此时运行游戏点击连接会出现

因为我们还没有启动服务器,所以属于正常现象

创建服务端程序

using System.Net;
using System.Net.Sockets;internal class Program
{private static void Main(string[] args){Console.WriteLine("Hello, World!");//SocketSocket listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress ipAdr = IPAddress.Parse("127.0.0.1");IPEndPoint ipEp = new IPEndPoint(ipAdr, 8888);listenfd.Bind(ipEp);//Listenlistenfd.Listen(0);Console.WriteLine("[服务器]启动成功");while (true){//AcceptSocket connfd = listenfd.Accept();Console.WriteLine("[服务器]Accept");//Receivebyte[] readBuff = new byte[1024];int count = connfd.Receive(readBuff);string readStr = System.Text.Encoding.Default.GetString(readBuff, 0, count);Console.WriteLine("[服务器接受]" + readStr);//Sendbyte[] sendBytes = System.Text.Encoding.Default.GetBytes(readStr);connfd.Send(sendBytes);}}
}

绑定 Bind

listenfd.Bind(ipEp)将给listenfd套接字绑定IP和端口。程序中绑定本地地址“127.0.0.1”和8888号端口。127.0.0.1是回送地址,指本地机,一般用于测试。读者也可以设置成真实的IP地址,然后在两台计算机上分别运行客户端和服务端程序

监听 Listen

服务端通过listenfd.Listen(backlog)开启监听,等待客户端连接。参数backlog指定队列中最多可容纳等待接受的连接数,0表示不限制。

应答 Accept

开启监听后,服务器调用listenfd.Accept()接收客户端连接。本例使用的所有Socket方法都是阻塞方法,也就是说当没有客户端连接时,服务器程序卡在listenfd.Accept()不会往下执行,直到接收了客户端的连接。Accept返回一个新客户端的Socket对象,对于服务器来说,它有一个监听Socket(例子中的listenfd)用来监听(Listen)和应答(Accept)客户端的连接,对每个客户端还有一个专门的Socket(例子中的connfd)用来处理该客户端的数据。

IPAddress 和 IPEndPoint

使用IPAddress指定IP地址,使用IPEndPoint指定IP和端口。

System.Text.Encoding.Default.GetString

Receive方法将接收到的字节流保存到readBuff上,readBuff是byte型数组。GetString方法可以将byte型数组转换成字符串。同理,System.Text.Encoding.Default.GetBytes可以将字符串转换成byte型数组。

测试 :

Socket类的一些常用方法

公网和局域网

把宽带连接到家里的路由器,再由路由器分发到多台计算机(校园网、公司局域网同理)​,在这种情况下,路由器会有公网和局域网两个IP

比如:路由器的公网IP是123.207.111.220,局域网IP为192.168.0.1,连接路由器的计算机只有内网IP,它们分别是192.168.0.10和192.168.0.12。。如果将服务端放到连接路由器的某台计算机上,因为它只有局域网IP,所以只有局域网内的计算机可以连接上。如果拥有路由器的控制权,可以使用一种叫“端口映射”的技术,即设置路由器,将路由器IP地址的一个端口映射到内网中的一台计算机,提供相应的服务。当用户访问该IP的这个端口时,路由器自动将请求映射到对应局域网内部的计算机上

异步和多路复用

上面的程序全部使用阻塞API(Connect、Send、Receive等)​,可称为同步Socket程序

一个简单的异步程序示例:

        using System.Collections;using System.Collections.Generic;using UnityEngine;using System.Threading;public class Async : MonoBehaviour {// Use this for initializationvoid Start () {//创建定时器Timer timer = new Timer(TimeOut, null, 5000, 0);//其他程序代码//……}//回调函数private void TimeOut(System.Object state){Debug.Log("铃铃铃");}}

异步Connect

每一个同步API(如Connect)对应着两个异步API,分别是在原名称前面加上Begin和End(如BeginConnect和EndConnect)

BeginConnect的函数原型如下:

public IAsyncResult BeginConnect( string host, int port, AsyncCallback requestCallback, object state )

修改代码:

        using System;//点击连接按钮public void Connection(){//Socketsocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);//Connectsocket.BeginConnect("127.0.0.1", 8888, ConnectCallback, socket);}//Connect回调public void ConnectCallback(IAsyncResult ar){try{Socket socket = (Socket) ar.AsyncState;socket.EndConnect(ar);Debug.Log("Socket Connect Succ");}catch (SocketException ex){Debug.Log("Socket Connect fail" + ex.ToString());}}

说明:

  • 由BeginConnect最后一个参数传入的socket,可由ar.AsyncState获取到。

异步Receive

public IAsyncResult BeginReceive ( byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state )

public int EndReceive( IAsyncResult asyncResult )  它的返回值代表接收到的字节数

修改客户端代码:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using UnityEngine;
using UnityEngine.UI;public class Echo : MonoBehaviour
{//UGUIpublic InputField inputField;public Text text;//定义套接字Socket socket;//接收缓冲区byte[] readBuff = new byte[1024];string recvStr = "";public void Connection(){socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);socket.BeginConnect("127.0.0.1", 8888,ConnectCallback,socket);}//Connect回调public void ConnectCallback(IAsyncResult ar){try{Socket socket = (Socket)ar.AsyncState;socket.EndConnect(ar);Debug.Log("Socket Connect Succ");socket.BeginReceive(readBuff,0,1024,0,ReceiveCallback,socket);}catch (SocketException ex){Debug.Log("Socket Connect fail" + ex.ToString());}}public void ReceiveCallback(IAsyncResult ar) {try{Socket socket = (Socket) ar.AsyncState;int count = socket.EndReceive(ar);recvStr = System.Text.Encoding.Default.GetString(readBuff,0,count);socket.BeginReceive(readBuff,0,1024,0,ReceiveCallback,socket);Debug.Log("ReceiveCallback" + recvStr);}catch (SocketException ex){Debug.Log("Socket Receive fail" + ex.ToString());}}public void Send(){string sendStr = inputField.text;byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);socket.Send(sendBytes);}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){text.text = recvStr;}
}

说明:

BeginReceive的参数

上述程序中,BeginReceive的参数为(readBuff, 0, 1024, 0, ReceiveCallback,socket)。第一个参数readBuff表示接收缓冲区;第二个参数0表示从readBuff第0位开始接收数据,这个参数和TCP粘包问题有关,第三个参数1024代表每次最多接收1024个字节的数据

BeginReceive的调用位置

程序在两个地方调用了BeginReceive:一个是ConnectCallback,在连接成功后,就开始接收数据,接收到数据后,回调函数ReceiveCallback被调用。另一个是BeginReceive内部,接收完一串数据后,等待下一串数据的到来

Update和recvStr

在Unity中,只有主线程可以操作UI组件。由于异步回调是在其他线程执行的,如果在BeginReceive给text.text赋值,Unity会弹出“get_isActiveAndEnabled can onlybe called from the main thread”的异常信息,所以程序只给变量recvStr赋值,在主线程执行的Update中再给text.text赋值

异步Send

Socket使用的协议、IP、端口属于用户层面的属性,可以直接修改;操作系统层面拥有“发送”和“接收”两个缓冲区,当调用Send方法时,程序将要发送的字节流写入到发送缓冲区中,再由操作系统完成数据的发送和确认

如果缓冲区满,那么Send就会阻塞,直到缓冲区的数据被确认腾出空间

值得注意的是,Send过程只是把数据写入到发送缓冲区,然后由操作系统负责重传、确认等步骤。Send方法返回只代表成功将数据放到发送缓存区中,对方可能还没有收到数据。

异步Send不会卡住程序,当数据成功写入输入缓冲区(或发生错误)时会调用回调函数。异步Send方法BeginSend的原型如下。

public IAsyncResult BeginSend( byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state )

public int EndSend ( IAsyncResult asyncResult )

修改客户端代码,使用异步发送:

    public void Send(){string sendStr = inputField.text;byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);for(int i = 0;i < 10000;i++) {socket.BeginSend(sendBytes,0,sendBytes.Length,0,SendCallback,socket);}}public void SendCallback(IAsyncResult ar) {try{Socket socket = (Socket) ar.AsyncState;int count = socket.EndSend(ar);Debug.Log("Socket Send succ" + count);}catch (SocketException ex){Debug.Log("Socket Send fail" + ex.ToString());}}

异步服务端

上面的同步服务端程序同一时间只能处理一个客户端的请求,因为它会一直阻塞,等待某一个客户端的数据,无暇接应其他客户端。使用异步方法,可以让服务端同时处理多个客户端的数据,及时响应

管理客户端

定义一个名为ClientState的类,用于保存一个客户端信息。ClientState包含TCP连接所需Socket,以及用于填充BeginReceive参数的读缓冲区readBuff

        //数据结构class ClientState {public Socket socket;public byte[] readBuff = new byte[1024];}static Dictionary<Socket, ClientState> clients =new Dictionary<Socket, ClientState>();

异步Accept

public IAsyncResult BeginAccept( AsyncCallback callback, object state )

public Socket EndAccept( IAsyncResult asyncResult )

程序结构:

修改代码:

using System.Net;
using System.Net.Sockets;class ClientState {public Socket socket;public byte[] readBuff = new byte[1024];
}internal class Program
{//监听Socketstatic Socket listenfd;//客户端Socket及状态信息static Dictionary<Socket,ClientState> clients = new Dictionary<Socket, ClientState>();private static void Main(string[] args){Console.WriteLine("Hello, World!");//Socketlistenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress ipAdr = IPAddress.Parse("127.0.0.1");IPEndPoint ipEp = new IPEndPoint(ipAdr, 8888);listenfd.Bind(ipEp);//Listenlistenfd.Listen(0);Console.WriteLine("[服务器]启动成功");listenfd.BeginAccept(AcceptCallback,listenfd);//等待Console.ReadLine();// while (true)// {//     //Accept//     Socket connfd = listenfd.Accept();//     Console.WriteLine("[服务器]Accept");//     //Receive//     byte[] readBuff = new byte[1024];//     int count = connfd.Receive(readBuff);//     string readStr = System.Text.Encoding.Default.GetString(readBuff, 0, count);//     Console.WriteLine("[服务器接受]" + readStr);//     //Send//     string sendStr = System.DateTime.Today.ToString();//     byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);//     connfd.Send(sendBytes);// }}//Accept回调public static void AcceptCallback(IAsyncResult ar) {try{Console.WriteLine("[服务器]Accept");Socket listenfd = (Socket) ar.AsyncState;Socket clientfd = listenfd.EndAccept(ar);//clients列表ClientState state = new ClientState();state.socket = clientfd;clients.Add(clientfd,state);//接收数据的BeginReceiveclientfd.BeginReceive(state.readBuff,0,1024,0,ReceiveCallback,state);//继续Acceptlistenfd.BeginAccept(AcceptCallback,listenfd);}catch (SocketException ex){Console.WriteLine("Socket Accept fail" + ex.ToString());}}//Receive回调public static void ReceiveCallback(IAsyncResult ar) {try{ClientState state = (ClientState) ar.AsyncState;Socket clientfd = state.socket;int count = clientfd.EndReceive(ar);//客户端关闭if(count == 0) {clientfd.Close();clients.Remove(clientfd);Console.WriteLine("Socket Close");return;}string recvStr = System.Text.Encoding.Default.GetString(state.readBuff,0,count);byte[] sendBytes = System.Text.Encoding.Default.GetBytes("echo" + recvStr);clientfd.Send(sendBytes); //减少代码量,不用异步clientfd.BeginReceive(state.readBuff,0,1024,0,ReceiveCallback,state);//注意BeginReceive的最后一个参数,这里以ClientState代替了原来的Socket。}catch (SocketException ex){Console.WriteLine("Socket Receive fail" + ex.ToString());throw;}}
}

AcceptCallback是BeginAccept的回调函数,它处理了三件事情:

  • 给新的连接分配ClientState,并把它添加到clients列表中;
  • 异步接收客户端数据;
  • 再次调用BeginAccept实现循环。

ReceiveCallback是BeginReceive的回调函数,它也处理了三件事情:

  • 服务端收到消息后,回应客户端;
  • 如果收到客户端关闭连接的信号“if(count == 0)”​,断开连接;
  • 继续调用BeginReceive接收下一个数据。

当Receive返回值小于等于0时,表示Socket连接断开,可以关闭Socket。

聊天室

在聊天室中,某个客户端发送聊天消息,所有在线的客户端都会收到这条消息。也就是会遍历在线的客户端,然后推送消息

修改服务端代码:

        //Receive回调public static void ReceiveCallback(IAsyncResult ar){try {……string recvStr = System.Text.Encoding.Default.GetString(state.readBuff,0, count);string sendStr = clientfd.RemoteEndPoint.ToString() + ":" + recvStr;byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);foreach (ClientState s in clients.Values){s.socket.Send(sendBytes);}clientfd.BeginReceive( state.readBuff, 0, 1024, 0,ReceiveCallback, state);}catch (SocketException ex){……}}

服务端整体代码:

using System.Net;
using System.Net.Sockets;class ClientState {public Socket socket;public byte[] readBuff = new byte[1024];
}internal class Program
{//监听Socketstatic Socket listenfd;//客户端Socket及状态信息static Dictionary<Socket,ClientState> clients = new Dictionary<Socket, ClientState>();private static void Main(string[] args){Console.WriteLine("Hello, World!");//Socketlistenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress ipAdr = IPAddress.Parse("127.0.0.1");IPEndPoint ipEp = new IPEndPoint(ipAdr, 8888);listenfd.Bind(ipEp);//Listenlistenfd.Listen(0);Console.WriteLine("[服务器]启动成功");listenfd.BeginAccept(AcceptCallback,listenfd);//等待Console.ReadLine();}//Accept回调public static void AcceptCallback(IAsyncResult ar) {try{Console.WriteLine("[服务器]Accept");Socket listenfd = (Socket) ar.AsyncState;Socket clientfd = listenfd.EndAccept(ar);//clients列表ClientState state = new ClientState();state.socket = clientfd;clients.Add(clientfd,state);//接收数据的BeginReceiveclientfd.BeginReceive(state.readBuff,0,1024,0,ReceiveCallback,state);//继续Acceptlistenfd.BeginAccept(AcceptCallback,listenfd);}catch (SocketException ex){Console.WriteLine("Socket Accept fail" + ex.ToString());}}//Receive回调public static void ReceiveCallback(IAsyncResult ar) {try{ClientState state = (ClientState) ar.AsyncState;Socket clientfd = state.socket;int count = clientfd.EndReceive(ar);//客户端关闭if(count == 0) {clientfd.Close();clients.Remove(clientfd);Console.WriteLine("Socket Close");return;}string recvStr = System.Text.Encoding.Default.GetString(state.readBuff,0,count);string sendStr = clientfd.RemoteEndPoint.ToString() + ":" + recvStr;byte[] sendBytes = System.Text.Encoding.Default.GetBytes("echo" + sendStr);foreach(ClientState s in clients.Values) {s.socket.Send(sendBytes);}clientfd.BeginReceive(state.readBuff,0,1024,0,ReceiveCallback,state);}catch (SocketException ex){Console.WriteLine("Socket Receive fail" + ex.ToString());throw;}}
}

修改客户端代码,显示历史聊天:

        //Receive回调public void ReceiveCallback(IAsyncResult ar){try {Socket socket = (Socket) ar.AsyncState;int count = socket.EndReceive(ar);string s = System.Text.Encoding.Default.GetString(readBuff, 0, count);recvStr = s + "\n" + recvStr;socket.BeginReceive( readBuff, 0, 1024, 0,ReceiveCallback, socket);}catch (SocketException ex){Debug.Log("Socket Receive fail" + ex.ToString());}}

客户端整体代码:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using UnityEngine;
using UnityEngine.UI;public class Echo : MonoBehaviour
{//UGUIpublic InputField inputField;public Text text;//定义套接字Socket socket;//接收缓冲区byte[] readBuff = new byte[1024];string recvStr = "";public void Connection(){socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);socket.BeginConnect("127.0.0.1", 8888,ConnectCallback,socket);}//Connect回调public void ConnectCallback(IAsyncResult ar){try{Socket socket = (Socket)ar.AsyncState;socket.EndConnect(ar);Debug.Log("Socket Connect Succ");socket.BeginReceive(readBuff,0,1024,0,ReceiveCallback,socket);}catch (SocketException ex){Debug.Log("Socket Connect fail" + ex.ToString());}}public void ReceiveCallback(IAsyncResult ar) {try{Socket socket = (Socket) ar.AsyncState;int count = socket.EndReceive(ar);string s = System.Text.Encoding.Default.GetString(readBuff,0,count);recvStr = s + "\n" + recvStr;socket.BeginReceive(readBuff,0,1024,0,ReceiveCallback,socket);Debug.Log("ReceiveCallback" + recvStr);}catch (SocketException ex){Debug.Log("Socket Receive fail" + ex.ToString());}}public void Send(){string sendStr = inputField.text;byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);socket.BeginSend(sendBytes,0,sendBytes.Length,0,SendCallback,socket);}public void SendCallback(IAsyncResult ar) {try{Socket socket = (Socket) ar.AsyncState;int count = socket.EndSend(ar);Debug.Log("Socket Send succ" + count);}catch (SocketException ex){Debug.Log("Socket Send fail" + ex.ToString());}}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){text.text = recvStr;}
}

效果: 

状态检测Poll

什么是Poll

处理阻塞的代码:

        if(socket有可读数据){socket.Receive()}if(socket缓冲区可写){socket.Send()}if(socket发生程序){错误处理}

public bool Poll ( int microSeconds, SelectMode mode )

防止单线程卡住程序的Poll方法

Poll方法将会检查Socket的状态。如果指定mode参数为SelectMode.SelectRead,则可确定Socket是否为可读;指定参数为SelectMode.SelectWrite,可确定Socket是否为可写;指定参数为SelectMode.SelectError,可以检测错误条件。Poll将在指定的时段(以微秒为单位)内阻止执行,如果希望无限期地等待响应,可将microSeconds设置为一个负整数;如果希望不阻塞,可将microSeconds设置为0。

        //省略各种usingpublic class Echo : MonoBehaviour {//定义套接字Socket socket;//UGUIpublic InputField InputFeld;public Text text;//点击连接按钮public void Connection(){//Socketsocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);//Connectsocket.Connect("127.0.0.1", 8888);}//点击发送按钮public void Send(){……//略}public void Update(){if(socket == null) {return;}if(socket.Poll(0, SelectMode.SelectRead)){byte[] readBuff = new byte[1024];int count = socket.Receive(readBuff);string recvStr =System.Text.Encoding.Default.GetString(readBuff, 0, count);text.text = recvStr;}}}

服务端代码:

        class MainClass{//监听Socketstatic Socket listenfd;//客户端Socket及状态信息static Dictionary<Socket, ClientState> clients =new Dictionary<Socket, ClientState>();public static void Main (string[] args){//Socketlistenfd = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);//BindIPAddress ipAdr = IPAddress.Parse("127.0.0.1");IPEndPoint ipEp = new IPEndPoint(ipAdr, 8888);listenfd.Bind(ipEp);//Listenlistenfd.Listen(0);Console.WriteLine("[服务器]启动成功");//主循环while(true){//检查listenfdif(listenfd.Poll(0, SelectMode.SelectRead)){ReadListenfd(listenfd);}//检查clientfdforeach (ClientState s in clients.Values){Socket clientfd = s.socket;if(clientfd.Poll(0, SelectMode.SelectRead)){if(! ReadClientfd(clientfd)){break;}}}//防止CPU占用过高System.Threading.Thread.Sleep(1);}}}//读取Listenfdpublic static void ReadListenfd(Socket listenfd){Console.WriteLine("Accept");Socket clientfd = listenfd.Accept();ClientState state = new ClientState();state.socket = clientfd;clients.Add(clientfd, state);}//读取Clientfdpublic static bool ReadClientfd(Socket clientfd){ClientState state = clients[clientfd];//接收int count = 0;try{count = clientfd.Receive(state.readBuff);}catch(SocketException ex){clientfd.Close();clients.Remove(clientfd);Console.WriteLine("Receive SocketException " + ex.ToString());return false;}//客户端关闭if(count == 0){clientfd.Close();clients.Remove(clientfd);Console.WriteLine("Socket Close");return false;}//广播string recvStr =System.Text.Encoding.Default.GetString(state.readBuff, 0, count);Console.WriteLine("Receive" + recvStr);string sendStr = clientfd.RemoteEndPoint.ToString() + ":" + recvStr;byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);foreach (ClientState cs in clients.Values){cs.socket.Send(sendBytes);}return true;}

多路复用Select(重点)

多路复用,就是同时处理多路信号,比如同时检测多个Socket的状态。

解决Poll服务端中CPU占用率过高的方法,那就是:同时检测多个Socket的状态。在设置要监听的Socket列表后,如果有一个(或多个)Socket可读(或可写,或发生错误信息)​,那就返回这些可读的Socket,如果没有可读的,那就阻塞。

public static void Select( IList checkRead, IList checkWrite, IList checkError, int microSeconds )

如图所示:把包含6个Socket的列表传给Select, Select方法将会阻塞,等到超时或某个(或多个)Socket可读时返回,并且修改checkRead列表,仅保存可读的socket A和socket C。当没有任何可读Socket时,程序将会阻塞,不占用CPU资源。

Select 服务端

        using System;using System.Net;using System.Net.Sockets;using System.Collections.Generic;class ClientState{public Socket socket;public byte[] readBuff = new byte[1024];}class MainClass{//监听Socketstatic Socket listenfd;//客户端Socket及状态信息static Dictionary<Socket, ClientState> clients =new Dictionary<Socket, ClientState>();public static void Main (string[] args){//Socketlistenfd = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);//BindIPAddress ipAdr = IPAddress.Parse("127.0.0.1");IPEndPoint ipEp = new IPEndPoint(ipAdr, 8888);listenfd.Bind(ipEp);//Listenlistenfd.Listen(0);Console.WriteLine("[服务器]启动成功");//checkReadList<Socket> checkRead = new List<Socket>();//主循环while(true){//填充checkRead列表checkRead.Clear();checkRead.Add(listenfd);foreach (ClientState s in clients.Values){checkRead.Add(s.socket);}//selectSocket.Select(checkRead, null, null, 1000);//检查可读对象foreach (Socket s in checkRead){if(s == listenfd){ReadListenfd(s);}else{ReadClientfd(s);}}}}}
  • 将监听Socket(listenfd)和客户端Socket(遍历clients列表)添加到待检测Socket可读状态的列表checkList中。
  • 调用Select,程序中设置超时时间为1秒,若1秒内没有任何可读信息,Select方法将checkList列表变成空列表,然后返回。
  • 对Select处理后的每个Socket做处理,如果监听Socket(listenfd)可读,说明有客户端连接,需调用Accept。如果客户端Socket可读,说明客户端发送了消息(或关闭)​,将消息广播给所有客户端。

Select客户端

            public void Update(){if(socket == null) {return;}//填充checkRead列表checkRead.Clear();checkRead.Add(socket);//selectSocket.Select(checkRead, null, null, 0);//checkforeach (Socket s in checkRead){byte[] readBuff = new byte[1024];int count = socket.Receive(readBuff);string recvStr =System.Text.Encoding.Default.GetString(readBuff, 0, count);text.text = recvStr;}}}

为了不卡住客户端,Select的超时时间设置为0,永不阻塞

参考书籍:《Unity3D网络游戏实战(第2版)》 (豆瓣) (douban.com)

http://www.hengruixuexiao.com/news/27535.html

相关文章:

  • 私人订制网站有哪些财经新闻每日财经报道
  • 王爷请自重seo流程
  • 做地方门户网站的排名个人网站制作模板主页
  • 手机做任务赚钱的网站seo代码优化
  • 沈阳网站建设工作seo营销是什么
  • thinkphp租房网站开发seo工具在线访问
  • 专门做情侣装的网站seo网站推广优化就找微源优化
  • 上海网站建设沪icp备百度搜索排名靠前
  • 临时域名用于网站调试网站自助建站系统
  • 快速搭建网站的工具有哪些平台网站开发公司
  • 电脑维护网站模板信阳百度推广公司电话
  • 政府网站建设的重要性台州网站建设推广
  • 青州住房和城乡建设网站河北网站推广公司
  • 想做网站策划怎么做百度有人工客服吗
  • 会议专属网站平台建设报价单广告优化
  • 织梦网站防黑怎么做网站功能优化的方法
  • 网络用户提要求找人帮忙做的网站网站建设推广多少钱
  • 两个域名同一个网站做优化今日重大事件
  • 怎么用建站系统建网站免费seo快速收录工具
  • 柳州做网站哪家好搜收录批量查询
  • 郑州网站建站网站怎么样第三方营销策划公司有哪些
  • 顺德品牌网站建设信息网络营销与直播电商专业学什么
  • 怎么投诉做网站的公司靠谱的影视后期培训班
  • thinkphp做的上线网站网络热词大全
  • 个人域名可以做公司网站么网页模板网站
  • 一个好的网站怎么建设网络公司经营范围
  • 海淀网站建设价格烟台网络推广
  • cloud域名注册网站seo网站系统
  • 商业空间设计调研搜索引擎seo如何优化
  • 惠州网络公司网站建设百度推广官网网站