首页 > 编程语言 >C#实现异步阻塞TCP(SocketAsyncEventArgs,SendAsync,ReceiveAsync,AcceptAsync,ConnectAsync)

C#实现异步阻塞TCP(SocketAsyncEventArgs,SendAsync,ReceiveAsync,AcceptAsync,ConnectAsync)

时间:2023-08-15 12:44:21浏览次数:47  
标签:ReceiveAsync SocketAsyncEventArgs C# private tcp new null public

// This class creates a single large buffer which can be divided up
// and assigned to SocketAsyncEventArgs objects for use with each
// socket I/O operation.
// This enables bufffers to be easily reused and guards against
// fragmenting heap memory.
//
// The operations exposed on the BufferManager class are not thread safe.
public class BufferManager
{
//buffer缓冲区大小
private int m_numBytes;
//缓冲区
private byte[] m_buffer;
private Stack<int> m_freeIndexPool;
private int m_currentIndex;
private int m_bufferSize;
public BufferManager(int totalBytes, int bufferSize)
{
m_numBytes = totalBytes;
m_currentIndex = 0;
m_bufferSize = bufferSize;
m_freeIndexPool = new Stack<int>();
}
/// <summary>
/// 给buffer分配缓冲区
/// </summary>
public void InitBuffer()
{
m_buffer = new byte[m_numBytes];
}
/// <summary>
/// 将buffer添加到args的IO缓冲区中,并设置offset
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public bool SetBuffer(SocketAsyncEventArgs args)
{
if (m_freeIndexPool.Count > 0)
{
args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);
}
else
{
if ((m_numBytes - m_bufferSize) < m_currentIndex)
{
return false;
}
args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);
m_currentIndex += m_bufferSize;
}
return true;
}
/// <summary>
/// 将buffer从args的IO缓冲区中释放
/// </summary>
/// <param name="args"></param>
public void FreeBuffer(SocketAsyncEventArgs args)
{
m_freeIndexPool.Push(args.Offset);
args.SetBuffer(null, 0, 0);
}
/// <summary>
/// 释放全部buffer缓存
/// </summary>
public void FreeAllBuffer()
{
m_freeIndexPool.Clear();
m_currentIndex = 0;
m_buffer = null;
}
}

// Represents a collection of reusable SocketAsyncEventArgs objects.
public class SocketAsyncEventArgsPool
{
private Stack<SocketAsyncEventArgs> m_pool;
// Initializes the object pool to the specified size
//
// The "capacity" parameter is the maximum number of
// SocketAsyncEventArgs objects the pool can hold
public SocketAsyncEventArgsPool(int capacity)
{
m_pool = new Stack<SocketAsyncEventArgs>(capacity);
}
// Add a SocketAsyncEventArg instance to the pool
//
//The "item" parameter is the SocketAsyncEventArgs instance
// to add to the pool
public void Push(SocketAsyncEventArgs item)
{
if (item == null) { throw new ArgumentNullException("Items added to a SocketAsyncEventArgsPool cannot be null"); }
lock (m_pool)
{
m_pool.Push(item);
}
}
// Removes a SocketAsyncEventArgs instance from the pool
// and returns the object removed from the pool
public SocketAsyncEventArgs Pop()
{
lock (m_pool)
{
return m_pool.Pop();
}
}
/// <summary>
/// 清空栈中元素
/// </summary>
public void Clear()
{
lock (m_pool)
{
m_pool.Clear();
}
}
// The number of SocketAsyncEventArgs instances in the pool
public int Count
{
get { return m_pool.Count; }
}
}

public class AsyncUserToken
{
private Socket socket = null;
public Socket Socket { get => socket; set => socket = value; }
}

/// <summary>
/// 服务端
/// </summary>
public class TcpServiceSocketAsync
{
//接收数据事件
public Action<string> recvMessageEvent = null;
//发送结果事件
public Action<int> sendResultEvent = null;
//监听socket
private Socket listenSocket = null;
//允许连接到tcp服务器的tcp客户端数量
private int numConnections = 1024;
//用于socket发送和接受的缓存区大小
private int bufferSize = 1024*1024*8;
//socket缓冲区管理对象
private BufferManager bufferManager = null;
//SocketAsyncEventArgs池
private SocketAsyncEventArgsPool socketAsyncEventArgsPool = null;
//当前连接的tcp客户端数量
private int numberAcceptedClients = 0;
//控制tcp客户端连接数量的信号量
private Semaphore maxNumberAcceptedClients = null;
//用于socket发送数据的SocketAsyncEventArgs集合
private List<SocketAsyncEventArgs> sendAsyncEventArgs = null;
//tcp服务器ip
private string ip = "";
//tcp服务器端口
private int port = 0;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="numConnections">允许连接到tcp服务器的tcp客户端数量</param>
/// <param name="bufferSize">用于socket发送和接受的缓存区大小</param>
public TcpServiceSocketAsync(int numConnections = 10, int bufferSize = 1024)
{
if (numConnections <= 0 || numConnections > int.MaxValue)
throw new ArgumentOutOfRangeException("_numConnections is out of range");
if (bufferSize <= 0 || bufferSize > int.MaxValue)
throw new ArgumentOutOfRangeException("_receiveBufferSize is out of range");
this.numConnections = numConnections;
this.bufferSize = bufferSize;
bufferManager = new BufferManager(numConnections * bufferSize * 2, bufferSize);
socketAsyncEventArgsPool = new SocketAsyncEventArgsPool(numConnections);
maxNumberAcceptedClients = new Semaphore(numConnections, numConnections);
sendAsyncEventArgs = new List<SocketAsyncEventArgs>();
}
/// <summary>
/// 初始化数据(bufferManager,socketAsyncEventArgsPool)
/// </summary>
public void Init()
{
numberAcceptedClients = 0;
bufferManager.InitBuffer();
SocketAsyncEventArgs readWriteEventArg;
for (int i = 0; i < numConnections * 2; i++)
{
readWriteEventArg = new SocketAsyncEventArgs();
readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
readWriteEventArg.UserToken = new AsyncUserToken();
bufferManager.SetBuffer(readWriteEventArg);
socketAsyncEventArgsPool.Push(readWriteEventArg);
}
}
/// <summary>
/// 开启tcp服务器,等待tcp客户端连接
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
public void Start(string ip, int port)
{
if (string.IsNullOrEmpty(ip))
throw new ArgumentNullException("ip cannot be null");
if (port < 1 || port > 65535)
throw new ArgumentOutOfRangeException("port is out of range");
this.ip = ip;
this.port = port;
try
{
listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress address = IPAddress.Parse(ip);
IPEndPoint endpoint = new IPEndPoint(address, port);
listenSocket.Bind(endpoint);//绑定地址
listenSocket.Listen(int.MaxValue);//开始监听
StartAccept(null);
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 关闭tcp服务器
/// </summary>
public void CloseSocket()
{
if (listenSocket == null)
return;
try
{
foreach (var e in sendAsyncEventArgs)
{
((AsyncUserToken)e.UserToken).Socket.Shutdown(SocketShutdown.Both);
}
listenSocket.Shutdown(SocketShutdown.Both);
}
catch { }
try
{
foreach (var e in sendAsyncEventArgs)
{
((AsyncUserToken)e.UserToken).Socket.Close();
}
listenSocket.Close();
}
catch { }
try
{
foreach (var e in sendAsyncEventArgs)
{
e.Dispose();
}
}
catch { }
sendAsyncEventArgs.Clear();
socketAsyncEventArgsPool.Clear();
bufferManager.FreeAllBuffer();
maxNumberAcceptedClients.Release(numberAcceptedClients);
}
/// <summary>
/// 重新启动tcp服务器
/// </summary>
public void Restart()
{
CloseSocket();
Init();
Start(ip, port);
}
/// <summary>
/// 开始等待tcp客户端连接
/// </summary>
/// <param name="acceptEventArg"></param>
private void StartAccept(SocketAsyncEventArgs acceptEventArg)
{
if (acceptEventArg == null)
{
acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
}
else
{
// socket must be cleared since the context object is being reused
acceptEventArg.AcceptSocket = null;
}
maxNumberAcceptedClients.WaitOne();
bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg);
if (!willRaiseEvent)
{
ProcessAccept(acceptEventArg);
}
}
/// <summary>
/// Socket.AcceptAsync完成回调函数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
{
ProcessAccept(e);
}
/// <summary>
/// 接受到tcp客户端连接,进行处理
/// </summary>
/// <param name="e"></param>
private void ProcessAccept(SocketAsyncEventArgs e)
{
Interlocked.Increment(ref numberAcceptedClients);
//设置用于接收的SocketAsyncEventArgs的socket,可以接受数据
SocketAsyncEventArgs recvEventArgs = socketAsyncEventArgsPool.Pop();
((AsyncUserToken)recvEventArgs.UserToken).Socket = e.AcceptSocket;
//recvEventArgs.UserToken = e.AcceptSocket;
//设置用于发送的SocketAsyncEventArgs的socket,可以发送数据
SocketAsyncEventArgs sendEventArgs = socketAsyncEventArgsPool.Pop();
((AsyncUserToken)sendEventArgs.UserToken).Socket = e.AcceptSocket;
sendAsyncEventArgs.Add(sendEventArgs);
StartAccept(e);
//bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(recvEventArgs);
//if (!willRaiseEvent)
//{
// ProcessReceive(recvEventArgs);
//}
StartRecv(recvEventArgs);
}
/// <summary>
/// tcp服务器开始接受tcp客户端发送的数据
/// </summary>
private void StartRecv(SocketAsyncEventArgs e)
{
try
{
//if (e.Buffer == null) {

// return;
//}
//Socket socket = ((AsyncUserToken)e.UserToken).Socket;
//string s = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
bool willRaiseEvent = ((AsyncUserToken)e.UserToken).Socket.ReceiveAsync(e);
//bool willRaiseEvent = ((AsyncUserToken)e.UserToken).Socket.ReceiveAsync(e);
//bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(e);
//bool willRaiseEvent = false;
if (!willRaiseEvent)
{
ProcessReceive(e);
}
}
catch(Exception ex)
{
throw new ArgumentException(ex.Message + ex.StackTrace);
}

}
/// <summary>
/// socket.sendAsync和socket.recvAsync的完成回调函数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void IO_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
default:
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
}
/// <summary>
/// 处理接受到的tcp客户端数据
/// </summary>
/// <param name="e"></param>
private void ProcessReceive(SocketAsyncEventArgs e)
{
AsyncUserToken token = (AsyncUserToken)e.UserToken;
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
{
if (recvMessageEvent != null)
//一定要指定GetString的长度
recvMessageEvent(Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred));
StartRecv(e);
}
else
{
CloseClientSocket(e);
}
}
/// <summary>
/// 处理tcp服务器发送的结果
/// </summary>
/// <param name="e"></param>
private void ProcessSend(SocketAsyncEventArgs e)
{
AsyncUserToken token = (AsyncUserToken)e.UserToken;
if (e.SocketError == SocketError.Success)
{
if (sendResultEvent != null)
sendResultEvent(e.BytesTransferred);
}
else
{
if (sendResultEvent != null)
sendResultEvent(e.BytesTransferred);
CloseClientSocket(e);
}
}
/// <summary>
/// 关闭一个与tcp客户端连接的socket
/// </summary>
/// <param name="e"></param>
private void CloseClientSocket(SocketAsyncEventArgs e)
{
AsyncUserToken token = e.UserToken as AsyncUserToken;
try
{
//关闭socket时,单独使用socket.close()通常会造成资源提前被释放,应该在关闭socket之前,先使用shutdown进行接受或者发送的禁用,再使用socket进行释放
token.Socket.Shutdown(SocketShutdown.Both);
}
catch { }
token.Socket.Close();
Interlocked.Decrement(ref numberAcceptedClients);
socketAsyncEventArgsPool.Push(e);
maxNumberAcceptedClients.Release();
if (e.LastOperation == SocketAsyncOperation.Send)
sendAsyncEventArgs.Remove(e);
}
/// <summary>
/// 给全部tcp客户端发送数据
/// </summary>
/// <param name="message"></param>
public void SendMessageToAllClients(string message)
{
if (string.IsNullOrEmpty(message))
throw new ArgumentNullException("message cannot be null");
foreach (var e in sendAsyncEventArgs)
{
byte[] buff = Encoding.UTF8.GetBytes(message);
if (buff.Length > bufferSize)
throw new ArgumentOutOfRangeException("message is out off range");
e.SetBuffer(buff,0,buff.Length);
bool willRaiseEvent = ((AsyncUserToken)e.UserToken).Socket.SendAsync(e);
if (!willRaiseEvent)
{
ProcessSend(e);
}
}
}
}

/// <summary>
/// 客户端
/// </summary>
public class TcpClientSocketAsync :ISocketTool
{
//接收数据事件
public Action<string> recvMessageEvent = null;
//发送结果事件
public Action<int> sendResultEvent = null;
//接受缓存数组
private byte[] recvBuff = null;
//发送缓存数组
private byte[] sendBuff = null;
//连接socket
private Socket connectSocket = null;
//用于发送数据的SocketAsyncEventArgs
private SocketAsyncEventArgs sendEventArg = null;
//用于接收数据的SocketAsyncEventArgs
private SocketAsyncEventArgs recvEventArg = null;
//tcp服务器ip
private string ip = "";
//tcp服务器端口
private int port = 0;
private int bufferSize = 1024 * 1024 * 8;
private Thread dataParseThread = null;

/// <summary>
/// 构造函数
/// </summary>
/// <param name="bufferSize">用于socket发送和接受的缓存区大小</param>
public TcpClientSocketAsync(int bufferSize = 0)
{
try
{
//设置用于发送数据的SocketAsyncEventArgs
sendBuff = new byte[bufferSize];
sendEventArg = new SocketAsyncEventArgs();
sendEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
sendEventArg.SetBuffer(sendBuff, 0, bufferSize);
//设置用于接受数据的SocketAsyncEventArgs
recvBuff = new byte[bufferSize];
recvEventArg = new SocketAsyncEventArgs();
recvEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
recvEventArg.SetBuffer(recvBuff, 0, bufferSize);
}
catch (Exception ex)
{
Log.Error("TcpClientSocketAsync error:" + ex.Message + ex.StackTrace);
}
}

/// <summary>
/// 开启tcp客户端,连接tcp服务器
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
public void Start(string ip, int port)
{
if (string.IsNullOrEmpty(ip))
throw new ArgumentNullException("ip cannot be null");
if (port < 1 || port > 65535)
throw new ArgumentOutOfRangeException("port is out of range");
this.ip = ip;
this.port = port;
try
{
connectSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress address = IPAddress.Parse(ip);
IPEndPoint endpoint = new IPEndPoint(address, port);
//连接tcp服务器
SocketAsyncEventArgs connectEventArg = new SocketAsyncEventArgs();
connectEventArg.Completed += ConnectEventArgs_Completed;
connectEventArg.RemoteEndPoint = endpoint;//设置要连接的tcp服务器地址
bool willRaiseEvent = connectSocket.ConnectAsync(connectEventArg);
if (!willRaiseEvent)
ProcessConnect(connectEventArg);
}
catch (Exception ex)
{
throw ex;
}
}

/// <summary>
/// 发送数据到tcp服务器
/// </summary>
/// <param name="message">要发送的数据</param>
public void Send(string data)
{
Send(Encoding.UTF8.GetBytes(data));
}

public void Send(byte[] data)
{
if (data == null)
throw new ArgumentNullException("message cannot be null");
if (connectSocket == null)
throw new Exception("socket cannot be null");
data.CopyTo(sendBuff, 0);
sendEventArg.SetBuffer(0, data.Length);
bool willRaiseEvent = connectSocket.SendAsync(sendEventArg);
if (!willRaiseEvent)
{
ProcessSend(sendEventArg);
}
}

/// <summary>
/// 关闭tcp客户端
/// </summary>
public void CloseSocket()
{
if (connectSocket == null)
return;
try
{
//关闭socket时,单独使用socket.close()通常会造成资源提前被释放,应该在关闭socket之前,先使用shutdown进行接受或者发送的禁用,再使用socket进行释放
connectSocket.Shutdown(SocketShutdown.Both);
}
catch { }
try
{
connectSocket.Close();
}
catch { }
}

/// <summary>
/// 重启tcp客户端,重新连接tcp服务器
/// </summary>
public void Restart()
{
CloseSocket();
Start(this.ip, this.port);
}

private void ConnectEventArgs_Completed(object sender, SocketAsyncEventArgs e)
{
ProcessConnect(e);
}

private void ProcessConnect(SocketAsyncEventArgs e)
{
StartRecv();
}

/// <summary>
/// tcp客户端开始接受tcp服务器发送的数据
/// </summary>
public void StartRecv()
{
bool willRaiseEvent = connectSocket.ReceiveAsync(recvEventArg);
if (!willRaiseEvent)
{
ProcessReceive(recvEventArg);
}
}

/// <summary>
/// socket.sendAsync和socket.recvAsync的完成回调函数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void IO_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
default:
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
}

/// <summary>
/// 处理接受到的tcp服务器数据
/// </summary>
/// <param name="e"></param>
private void ProcessReceive(SocketAsyncEventArgs e)
{
AsyncUserToken token = (AsyncUserToken)e.UserToken;
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
{
byte[] buff = new byte[e.BytesTransferred];
Array.Copy(e.Buffer,buff,buff.Length);
Commom.receiveQueue.Enqueue(new ReceiveDataModel(buff,buff.Length));
string s = Encoding.UTF8.GetString(buff);
if (recvMessageEvent != null)
//一定要指定GetString的长度,否则整个bugger都要转成string
recvMessageEvent(Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred));
StartRecv();
}
else
{
Restart();
}
}
/// <summary>
/// 处理tcp客户端发送的结果
/// </summary>
/// <param name="e"></param>
private void ProcessSend(SocketAsyncEventArgs e)
{
AsyncUserToken token = (AsyncUserToken)e.UserToken;
if (e.SocketError == SocketError.Success)
{
if (sendResultEvent != null)
sendResultEvent(e.BytesTransferred);
}
else
{
if (sendResultEvent != null)
sendResultEvent(-1);
Restart();
}
}

public void DataParseStart()
{
//如果线程已退出,再次启动流程时重新创建
if (dataParseThread == null)
{
dataParseThread = new Thread(new ThreadStart(DataParse));
dataParseThread.Start();
dataParseThread.IsBackground = true;
}
}

private void DataParse()
{
try
{
while (true)
{
ReceiveDataModel data;
Commom.receiveQueue.TryDequeue(out data);
if (data == null) continue;
}

}
catch (Exception ex)
{
Log.Error("Tcp_Socket DataParse error:" + ex.Message + ex.StackTrace);
}
}

}

标签:ReceiveAsync,SocketAsyncEventArgs,C#,private,tcp,new,null,public
From: https://www.cnblogs.com/xhztech/p/17631046.html

相关文章

  • Combobox后台绑定
    本文主要介绍WPF中Combobox的后台绑定,我在这里主要讲解数据驱动1、对于前台绑定,我们首先写出想要绑定的对象新建一个Models文件夹,将Student类写入publicclassStudent{publicintId{get;set;}publicstringName{get;set;}publicintAge{get......
  • 4收4发ARINC429模块
    USB2.0Hi-Speed(480Mbits/s)* 发送通道:每路发送通道FIFO大小为511x32bit(CHR32904) 缓存256条发送消息(CHR32904-EX)发送FIFO可设置复位可设置消息间隔,字间隔和发送帧的预定数星发送波特率100Kbps、50Kbps、48Kbps、12.5Kbps、10Kbps可设置标准USB429字格式转换与否......
  • 非2的幂次的ASTC纹理格式尺寸对带宽的影响
    1)非2的幂次的ASTC纹理格式尺寸对带宽的影响2)​C#端如何处理xLua在执行DoString时候死循环3)如何制定美术规范或者各个模块的指标4)如何处理Lua的io.open出现中文路径这是第348篇UWA技术知识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答、社区帖子等技术知识点,助力大家更全面地......
  • 第一:Docker的安装
    小白开始学Docker: 从一个全新的centos7开始操作:第一步:下载安装docker需要的依赖包        yum-yinstallyum-utilsdevice-mapper-persistent-datalvm2 第二步:设置一下下载Docker的镜像源,如果不设置,会默认去Docker的官方下载,但是官方的服务器在国外,下载......
  • Ceph RBD的使用笔记
    ceph的内容太多了,单独写一篇文章记录自己的一些RBD的学习笔记,其实简书和其他博客上已经记录的非常全面了,但是因为出处都比较分散,所以还是自己把自己的实验记录一下便于以后学习的查阅。也感谢各位大佬的无私分享。 1.RBD池的创建和enable[cephadmin@ceph-node~]$cephosdp......
  • 软件测试|Chrome 115之后的版本,如何更新driver?
    2023年8月,chrome自动更新到115版本了,而从https://registry.npmmirror.com/binary.html?path=chromedriver/处只能下载114版本的driver,无法工作。参考:https://blog.csdn.net/Tester_muller/article/details/132086996  找到https://googlechromelabs.github.io/chrome-for......
  • 如何管理敏捷迭代,Sprint Backlog?
    ​什么是Sprint Backlog? Sprint Backlog是Scrum的主要工件之一。在Scrum中,团队按照迭代的方式工作,每个迭代称为一个Sprint。在Sprint开始之前,PO会准备好产品Backlog,准备好的产品Backlog应该是经过梳理、估算和优先级排列的。在Sprint开始时,第一件事情是Sprint计划会议,在Sprin......
  • python 解决Could not import the lzma module. Your installed Python is incomplete
    python安装好pandas后import报错如下图:原因分析在执行./configure和makeinstall的时候出现错误提示,被忽略了,如下图:解决方法安装yuminstall-yxz-devel然后重新安装python,执行./configure和makeinstall......
  • linux环境下监控docker进程运行情况,使用钉钉群机器人报警异常服务
    背景:在linux环境下,很多服务我们都使用docker来跑,很是方便,容器服务独立,配置独立,数据独立等等,但是有个问题,就是如果某个服务异常了,暂停了,停止了,一直重启中,我们要怎么及时的知道是哪个服务,并进行处理,保证业务正常运行。本文主要介绍使用docker服务自带的一些命令来实现一个基本的监......
  • javascript 高级编程系列 - 定型数组
    定型数组是一种用于处理数值类型数据的专用数组,最早是在webGL中使用的,WebGL是OpenGLES2.0的移植版,在web页面中通过<canvas>元素来呈现它。定型数组也被一同移植而来,其可为javascript提供快速的按位运算。在javascript中,数字是以64位浮点格式储存的,并按需转换为32位整数,所以算术......