http://freshflower.iteye.com/blog/2285272

想着当初到处找不到相关资料来实现.net的Socket通信的痛苦与心酸, 于是将自己写的代码公布给大家, 让大家少走点弯路, 以供参考. 若是觉得文中的思路有哪里不正确的地方, 欢迎大家指正, 共同进步.

说到Socket通信, 必须要有个服务端, 打开一个端口进行监听(废话!) 可能大家都会把socket.Accept方法放在一个while(true)的循环里, 当然也没有错, 但个人认为这个不科学, 极大可能地占用服务资源. 赞成的请举手. 所以我想从另外一个方面解决这个问题. 之后是在MSDN找到SocketAsyncEventArgs的一个实例, 然后拿来改改, 有需要的同学可以看看MSDN的官方实例.https://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs(v=vs.110).aspx

需要了解客户端写法的, 请参考: 客户端实现http://freshflower.iteye.com/blog/2285286

不多说, 接下来贴代码, 这个实例中需要用到几个类:

1. BufferManager类, 管理传输流的大小  原封不动地拷贝过来,

2. SocketEventPool类: 管理SocketAsyncEventArgs的一个应用池. 有效地重复使用.

3. AsyncUserToken类: 这个可以根据自己的实际情况来定义.主要作用就是存储客户端的信息.

4. SocketManager类: 核心,实现Socket监听,收发信息等操作.

BufferManager类

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net.Sockets;
  5. using System.Text;
  6. namespace Plates.Service
  7. {
  8. class BufferManager
  9. {
  10. int m_numBytes;                 // the total number of bytes controlled by the buffer pool
  11. byte[] m_buffer;                // the underlying byte array maintained by the Buffer Manager
  12. Stack<int> m_freeIndexPool;     //
  13. int m_currentIndex;
  14. int m_bufferSize;
  15. public BufferManager(int totalBytes, int bufferSize)
  16. {
  17. m_numBytes = totalBytes;
  18. m_currentIndex = 0;
  19. m_bufferSize = bufferSize;
  20. m_freeIndexPool = new Stack<int>();
  21. }
  22. // Allocates buffer space used by the buffer pool
  23. public void InitBuffer()
  24. {
  25. // create one big large buffer and divide that
  26. // out to each SocketAsyncEventArg object
  27. m_buffer = new byte[m_numBytes];
  28. }
  29. // Assigns a buffer from the buffer pool to the
  30. // specified SocketAsyncEventArgs object
  31. //
  32. // <returns>true if the buffer was successfully set, else false</returns>
  33. public bool SetBuffer(SocketAsyncEventArgs args)
  34. {
  35. if (m_freeIndexPool.Count > 0)
  36. {
  37. args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);
  38. }
  39. else
  40. {
  41. if ((m_numBytes - m_bufferSize) < m_currentIndex)
  42. {
  43. return false;
  44. }
  45. args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);
  46. m_currentIndex += m_bufferSize;
  47. }
  48. return true;
  49. }
  50. // Removes the buffer from a SocketAsyncEventArg object.
  51. // This frees the buffer back to the buffer pool
  52. public void FreeBuffer(SocketAsyncEventArgs args)
  53. {
  54. m_freeIndexPool.Push(args.Offset);
  55. args.SetBuffer(null, 0, 0);
  56. }
  57. }
  58. }

SocketEventPool类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net.Sockets;
  5. using System.Text;
  6. namespace Plates.Service
  7. {
  8. class SocketEventPool
  9. {
  10. Stack<SocketAsyncEventArgs> m_pool;
  11. public SocketEventPool(int capacity)
  12. {
  13. m_pool = new Stack<SocketAsyncEventArgs>(capacity);
  14. }
  15. public void Push(SocketAsyncEventArgs item)
  16. {
  17. if (item == null) { throw new ArgumentNullException("Items added to a SocketAsyncEventArgsPool cannot be null"); }
  18. lock (m_pool)
  19. {
  20. m_pool.Push(item);
  21. }
  22. }
  23. // Removes a SocketAsyncEventArgs instance from the pool
  24. // and returns the object removed from the pool
  25. public SocketAsyncEventArgs Pop()
  26. {
  27. lock (m_pool)
  28. {
  29. return m_pool.Pop();
  30. }
  31. }
  32. // The number of SocketAsyncEventArgs instances in the pool
  33. public int Count
  34. {
  35. get { return m_pool.Count; }
  36. }
  37. public void Clear()
  38. {
  39. m_pool.Clear();
  40. }
  41. }
  42. }

AsyncUserToken类

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Text;
  8. namespace Plates.Service
  9. {
  10. class AsyncUserToken
  11. {
  12. /// <summary>
  13. /// 客户端IP地址
  14. /// </summary>
  15. public IPAddress IPAddress { get; set; }
  16. /// <summary>
  17. /// 远程地址
  18. /// </summary>
  19. public EndPoint Remote { get; set; }
  20. /// <summary>
  21. /// 通信SOKET
  22. /// </summary>
  23. public Socket Socket { get; set; }
  24. /// <summary>
  25. /// 连接时间
  26. /// </summary>
  27. public DateTime ConnectTime { get; set; }
  28. /// <summary>
  29. /// 所属用户信息
  30. /// </summary>
  31. public UserInfoModel UserInfo { get; set; }
  32. /// <summary>
  33. /// 数据缓存区
  34. /// </summary>
  35. public List<byte> Buffer { get; set; }
  36. public AsyncUserToken()
  37. {
  38. this.Buffer = new List<byte>();
  39. }
  40. }
  41. }

SocketManager类

  1. using Plates.Common;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Net.Sockets;
  8. using System.Text;
  9. using System.Threading;
  10. namespace Plates.Service
  11. {
  12. class SocketManager
  13. {
  14. private int m_maxConnectNum;    //最大连接数
  15. private int m_revBufferSize;    //最大接收字节数
  16. BufferManager m_bufferManager;
  17. const int opsToAlloc = 2;
  18. Socket listenSocket;            //监听Socket
  19. SocketEventPool m_pool;
  20. int m_clientCount;              //连接的客户端数量
  21. Semaphore m_maxNumberAcceptedClients;
  22. List<AsyncUserToken> m_clients; //客户端列表
  23. #region 定义委托
  24. /// <summary>
  25. /// 客户端连接数量变化时触发
  26. /// </summary>
  27. /// <param name="num">当前增加客户的个数(用户退出时为负数,增加时为正数,一般为1)</param>
  28. /// <param name="token">增加用户的信息</param>
  29. public delegate void OnClientNumberChange(int num, AsyncUserToken token);
  30. /// <summary>
  31. /// 接收到客户端的数据
  32. /// </summary>
  33. /// <param name="token">客户端</param>
  34. /// <param name="buff">客户端数据</param>
  35. public delegate void OnReceiveData(AsyncUserToken token, byte[] buff);
  36. #endregion
  37. #region 定义事件
  38. /// <summary>
  39. /// 客户端连接数量变化事件
  40. /// </summary>
  41. public event OnClientNumberChange ClientNumberChange;
  42. /// <summary>
  43. /// 接收到客户端的数据事件
  44. /// </summary>
  45. public event OnReceiveData ReceiveClientData;
  46. #endregion
  47. #region 定义属性
  48. /// <summary>
  49. /// 获取客户端列表
  50. /// </summary>
  51. public List<AsyncUserToken> ClientList { get { return m_clients; } }
  52. #endregion
  53. /// <summary>
  54. /// 构造函数
  55. /// </summary>
  56. /// <param name="numConnections">最大连接数</param>
  57. /// <param name="receiveBufferSize">缓存区大小</param>
  58. public SocketManager(int numConnections, int receiveBufferSize)
  59. {
  60. m_clientCount = 0;
  61. m_maxConnectNum = numConnections;
  62. m_revBufferSize = receiveBufferSize;
  63. // allocate buffers such that the maximum number of sockets can have one outstanding read and
  64. //write posted to the socket simultaneously
  65. m_bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToAlloc, receiveBufferSize);
  66. m_pool = new SocketEventPool(numConnections);
  67. m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections);
  68. }
  69. /// <summary>
  70. /// 初始化
  71. /// </summary>
  72. public void Init()
  73. {
  74. // Allocates one large byte buffer which all I/O operations use a piece of.  This gaurds
  75. // against memory fragmentation
  76. m_bufferManager.InitBuffer();
  77. m_clients = new List<AsyncUserToken>();
  78. // preallocate pool of SocketAsyncEventArgs objects
  79. SocketAsyncEventArgs readWriteEventArg;
  80. for (int i = 0; i < m_maxConnectNum; i++)
  81. {
  82. readWriteEventArg = new SocketAsyncEventArgs();
  83. readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
  84. readWriteEventArg.UserToken = new AsyncUserToken();
  85. // assign a byte buffer from the buffer pool to the SocketAsyncEventArg object
  86. m_bufferManager.SetBuffer(readWriteEventArg);
  87. // add SocketAsyncEventArg to the pool
  88. m_pool.Push(readWriteEventArg);
  89. }
  90. }
  91. /// <summary>
  92. /// 启动服务
  93. /// </summary>
  94. /// <param name="localEndPoint"></param>
  95. public bool Start(IPEndPoint localEndPoint)
  96. {
  97. try
  98. {
  99. m_clients.Clear();
  100. listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  101. listenSocket.Bind(localEndPoint);
  102. // start the server with a listen backlog of 100 connections
  103. listenSocket.Listen(m_maxConnectNum);
  104. // post accepts on the listening socket
  105. StartAccept(null);
  106. return true;
  107. }
  108. catch (Exception)
  109. {
  110. return false;
  111. }
  112. }
  113. /// <summary>
  114. /// 停止服务
  115. /// </summary>
  116. public void Stop()
  117. {
  118. foreach (AsyncUserToken token in m_clients)
  119. {
  120. try
  121. {
  122. token.Socket.Shutdown(SocketShutdown.Both);
  123. }
  124. catch (Exception) { }
  125. }
  126. try
  127. {
  128. listenSocket.Shutdown(SocketShutdown.Both);
  129. }
  130. catch (Exception) { }
  131. listenSocket.Close();
  132. int c_count = m_clients.Count;
  133. lock (m_clients) { m_clients.Clear(); }
  134. if (ClientNumberChange != null)
  135. ClientNumberChange(-c_count, null);
  136. }
  137. public void CloseClient(AsyncUserToken token)
  138. {
  139. try
  140. {
  141. token.Socket.Shutdown(SocketShutdown.Both);
  142. }
  143. catch (Exception) { }
  144. }
  145. // Begins an operation to accept a connection request from the client
  146. //
  147. // <param name="acceptEventArg">The context object to use when issuing
  148. // the accept operation on the server's listening socket</param>
  149. public void StartAccept(SocketAsyncEventArgs acceptEventArg)
  150. {
  151. if (acceptEventArg == null)
  152. {
  153. acceptEventArg = new SocketAsyncEventArgs();
  154. acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
  155. }
  156. else
  157. {
  158. // socket must be cleared since the context object is being reused
  159. acceptEventArg.AcceptSocket = null;
  160. }
  161. m_maxNumberAcceptedClients.WaitOne();
  162. if (!listenSocket.AcceptAsync(acceptEventArg))
  163. {
  164. ProcessAccept(acceptEventArg);
  165. }
  166. }
  167. // This method is the callback method associated with Socket.AcceptAsync
  168. // operations and is invoked when an accept operation is complete
  169. //
  170. void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
  171. {
  172. ProcessAccept(e);
  173. }
  174. private void ProcessAccept(SocketAsyncEventArgs e)
  175. {
  176. try
  177. {
  178. Interlocked.Increment(ref m_clientCount);
  179. // Get the socket for the accepted client connection and put it into the
  180. //ReadEventArg object user token
  181. SocketAsyncEventArgs readEventArgs = m_pool.Pop();
  182. AsyncUserToken userToken = (AsyncUserToken)readEventArgs.UserToken;
  183. userToken.Socket = e.AcceptSocket;
  184. userToken.ConnectTime = DateTime.Now;
  185. userToken.Remote = e.AcceptSocket.RemoteEndPoint;
  186. userToken.IPAddress = ((IPEndPoint)(e.AcceptSocket.RemoteEndPoint)).Address;
  187. lock (m_clients) { m_clients.Add(userToken); }
  188. if (ClientNumberChange != null)
  189. ClientNumberChange(1, userToken);
  190. if (!e.AcceptSocket.ReceiveAsync(readEventArgs))
  191. {
  192. ProcessReceive(readEventArgs);
  193. }
  194. }
  195. catch (Exception me)
  196. {
  197. RuncomLib.Log.LogUtils.Info(me.Message + "\r\n" + me.StackTrace);
  198. }
  199. // Accept the next connection request
  200. if (e.SocketError == SocketError.OperationAborted) return;
  201. StartAccept(e);
  202. }
  203. void IO_Completed(object sender, SocketAsyncEventArgs e)
  204. {
  205. // determine which type of operation just completed and call the associated handler
  206. switch (e.LastOperation)
  207. {
  208. case SocketAsyncOperation.Receive:
  209. ProcessReceive(e);
  210. break;
  211. case SocketAsyncOperation.Send:
  212. ProcessSend(e);
  213. break;
  214. default:
  215. throw new ArgumentException("The last operation completed on the socket was not a receive or send");
  216. }
  217. }
  218. // This method is invoked when an asynchronous receive operation completes.
  219. // If the remote host closed the connection, then the socket is closed.
  220. // If data was received then the data is echoed back to the client.
  221. //
  222. private void ProcessReceive(SocketAsyncEventArgs e)
  223. {
  224. try
  225. {
  226. // check if the remote host closed the connection
  227. AsyncUserToken token = (AsyncUserToken)e.UserToken;
  228. if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
  229. {
  230. //读取数据
  231. byte[] data = new byte[e.BytesTransferred];
  232. Array.Copy(e.Buffer, e.Offset, data, 0, e.BytesTransferred);
  233. lock (token.Buffer)
  234. {
  235. token.Buffer.AddRange(data);
  236. }
  237. //注意:你一定会问,这里为什么要用do-while循环?
  238. //如果当客户发送大数据流的时候,e.BytesTransferred的大小就会比客户端发送过来的要小,
  239. //需要分多次接收.所以收到包的时候,先判断包头的大小.够一个完整的包再处理.
  240. //如果客户短时间内发送多个小数据包时, 服务器可能会一次性把他们全收了.
  241. //这样如果没有一个循环来控制,那么只会处理第一个包,
  242. //剩下的包全部留在token.Buffer中了,只有等下一个数据包过来后,才会放出一个来.
  243. do
  244. {
  245. //判断包的长度
  246. byte[] lenBytes = token.Buffer.GetRange(0, 4).ToArray();
  247. int packageLen = BitConverter.ToInt32(lenBytes, 0);
  248. if (packageLen > token.Buffer.Count - 4)
  249. {   //长度不够时,退出循环,让程序继续接收
  250. break;
  251. }
  252. //包够长时,则提取出来,交给后面的程序去处理
  253. byte[] rev = token.Buffer.GetRange(4, packageLen).ToArray();
  254. //从数据池中移除这组数据
  255. lock (token.Buffer)
  256. {
  257. token.Buffer.RemoveRange(0, packageLen + 4);
  258. }
  259. //将数据包交给后台处理,这里你也可以新开个线程来处理.加快速度.
  260. if(ReceiveClientData != null)
  261. ReceiveClientData(token, rev);
  262. //这里API处理完后,并没有返回结果,当然结果是要返回的,却不是在这里, 这里的代码只管接收.
  263. //若要返回结果,可在API处理中调用此类对象的SendMessage方法,统一打包发送.不要被微软的示例给迷惑了.
  264. } while (token.Buffer.Count > 4);
  265. //继续接收. 为什么要这么写,请看Socket.ReceiveAsync方法的说明
  266. if (!token.Socket.ReceiveAsync(e))
  267. this.ProcessReceive(e);
  268. }
  269. else
  270. {
  271. CloseClientSocket(e);
  272. }
  273. }
  274. catch (Exception xe)
  275. {
  276. RuncomLib.Log.LogUtils.Info(xe.Message + "\r\n" + xe.StackTrace);
  277. }
  278. }
  279. // This method is invoked when an asynchronous send operation completes.
  280. // The method issues another receive on the socket to read any additional
  281. // data sent from the client
  282. //
  283. // <param name="e"></param>
  284. private void ProcessSend(SocketAsyncEventArgs e)
  285. {
  286. if (e.SocketError == SocketError.Success)
  287. {
  288. // done echoing data back to the client
  289. AsyncUserToken token = (AsyncUserToken)e.UserToken;
  290. // read the next block of data send from the client
  291. bool willRaiseEvent = token.Socket.ReceiveAsync(e);
  292. if (!willRaiseEvent)
  293. {
  294. ProcessReceive(e);
  295. }
  296. }
  297. else
  298. {
  299. CloseClientSocket(e);
  300. }
  301. }
  302. //关闭客户端
  303. private void CloseClientSocket(SocketAsyncEventArgs e)
  304. {
  305. AsyncUserToken token = e.UserToken as AsyncUserToken;
  306. lock (m_clients) { m_clients.Remove(token); }
  307. //如果有事件,则调用事件,发送客户端数量变化通知
  308. if (ClientNumberChange != null)
  309. ClientNumberChange(-1, token);
  310. // close the socket associated with the client
  311. try
  312. {
  313. token.Socket.Shutdown(SocketShutdown.Send);
  314. }
  315. catch (Exception) { }
  316. token.Socket.Close();
  317. // decrement the counter keeping track of the total number of clients connected to the server
  318. Interlocked.Decrement(ref m_clientCount);
  319. m_maxNumberAcceptedClients.Release();
  320. // Free the SocketAsyncEventArg so they can be reused by another client
  321. e.UserToken = new AsyncUserToken();
  322. m_pool.Push(e);
  323. }
  324. /// <summary>
  325. /// 对数据进行打包,然后再发送
  326. /// </summary>
  327. /// <param name="token"></param>
  328. /// <param name="message"></param>
  329. /// <returns></returns>
  330. public void SendMessage(AsyncUserToken token, byte[] message)
  331. {
  332. if (token == null || token.Socket == null || !token.Socket.Connected)
  333. return;
  334. try
  335. {
  336. //对要发送的消息,制定简单协议,头4字节指定包的大小,方便客户端接收(协议可以自己定)
  337. byte[] buff = new byte[message.Length + 4];
  338. byte[] len = BitConverter.GetBytes(message.Length);
  339. Array.Copy(len, buff, 4);
  340. Array.Copy(message, 0, buff, 4, message.Length);
  341. //token.Socket.Send(buff);  //这句也可以发送, 可根据自己的需要来选择
  342. //新建异步发送对象, 发送消息
  343. SocketAsyncEventArgs sendArg = new SocketAsyncEventArgs();
  344. sendArg.UserToken = token;
  345. sendArg.SetBuffer(buff, 0, buff.Length);  //将数据放置进去.
  346. token.Socket.SendAsync(sendArg);
  347. }
  348. catch (Exception e){
  349. RuncomLib.Log.LogUtils.Info("SendMessage - Error:" + e.Message);
  350. }
  351. }
  352. }
  353. }

调用方法:

  1. SocketManager m_socket = new SocketManager(200, 1024);
  2. m_socket.Init();
  3. m_socket.Start(new IPEndPoint(IPAddress.Any, 13909));

好了,大功告成, 当初自己在写这些代码的时候, 一个地方就卡上很久, 烧香拜菩萨都没有用, 只能凭网上零星的一点代码给点提示. 现在算是做个总结吧. 让大家一看就明白, Socket通信就是这样, 可简单可复杂.

上面说的是服务器,那客户端的请参考

C#如何利用SocketAsyncEventArgs实现高效能TCPSocket通信 (客户端实现)

C#SocketAsyncEventArgs实现高效能多并发TCPSocket通信 (服务器实现)的更多相关文章

  1. (转)C#SocketAsyncEventArgs实现高效能多并发TCPSocket通信

    原文地址:http://freshflower.iteye.com/blog/2285272.http://freshflower.iteye.com/blog/2285286 一)服务器端 说到So ...

  2. 高效能人士必知铁律--note

    偶然看到了<高效能人士 必知铁律>这本书,我比较少看成功学,但是这本书把很多著名的成功学书籍整理出来,有时会让你耳目一新,有些观点尽管是常识,但是却加深了你对它们的理解,比如: 只要在积极 ...

  3. 高效能团队协作的JIRA实践

    http://www.csdn.net/article/2015-05-21/2824739?utm_source=tuicool 高效能团队是企业生存和发展的基石.任何企业面对当下的激烈竞争,要想脱 ...

  4. [转]如何写出高效能TSQL -深入浅出SQL Server Relational Engine (含 SQL 2014 in-memory Engine)

    [转]如何写出高效能TSQL -深入浅出SQL Server Relational Engine (含 SQL 2014 in-memory Engine) http://blogs.technet. ...

  5. 《高效能程序员的修炼》读后感 By Yong Zhang

    想不到我工作中经常GOOGLE搜寻技术问题的stack overflow网站的创办人竟然是<高效能程序员的修炼>一书的作者!看了一遍全书,果然名不虚传. 本书更多的从人文角度而非技术角度去 ...

  6. 高效能Windows人士的N个习惯之一:启动篇

    接触电脑十多年,经历了各种折腾阶段,这几年开始沉静下来,不再追求花哨的界面与应用,只注重工作的效率,逐渐养成了一套自己的操作习惯,感觉不错,特撰文分享.标题借用了一下<高效能人士的七个习惯> ...

  7. DevOps|高效能敏捷交付组织:特性团队(FeatureTeam)+Scrum

    这是<研发效能组织能力建设>的第三篇.特性团队和Scrum,这两个定义我们在之前的文章中都详细介绍了.这两个组织模式或者说管理实践,我都用过所以有些时候特别有感触.书本上纯粹的模式很容易理 ...

  8. 高扩展的基于NIO的服务器架构

    当你考虑写一个扩展性良好的基于Java的服务器时,相信你会毫不犹豫地使用Java的NIO包.为了确保你的服务器能够健壮.稳定地运行,你可能会花大量的时间阅读博客和教程来了解线程同步的NIO selec ...

  9. 高扩展的基于NIO的服务器架构(二)

    接上文高扩展的基于NIO的服务器架构 Reactor模式 如下图所示,将不同事件的检测分离开,当一种事件发生时一个事件处理器EventHandler将通知与该事件处理相对应的专用工作线程 采用这种架构 ...

随机推荐

  1. 【深入JAVA EE】Spring配置文件解析

    在阅读的过程中有不论什么问题,欢迎一起交流 邮箱:1494713801@qq.com    QQ:1494713801 一.Spring头信息 Spring配置文件的头部信息通常是固定不变的.但每个标 ...

  2. 27:简单错误记录SimpleErrorLog

    题目描述 开发一个简单错误记录功能小模块,能够记录出错的代码所在的文件名称和行号. 处理: 1. 记录最多8条错误记录,循环记录,对相同的错误记录(净文件名称和行号完全匹配)只记录一条,错误计数增加: ...

  3. java.lang.UnsupportedClassVersionError: Unsupported major.minor version 49.0的错误 [转]

    一:要解决的问题 我们在尝鲜 JDK1.5 的时候,相信不少人遇到过 Unsupported major.minor version 49.0 错误,当时定会茫然不知所措.因为刚开始那会儿,网上与此相 ...

  4. git stash 保存当前工作状态

    1. git stash   暂存当前工作状态 2. git stash list 查看暂存列表 3. git stash save 'title' 暂存工作状态并添加说明 4. git stash ...

  5. javascript的slice(),splice(),split(),substring(),substr()

    例子摘抄于http://www.w3school.com.cn/jsref/jsref_obj_array.asp 1.slice(): Array和String对象都有 在Array中  slice ...

  6. android中使用百度定位sdk实时的计算移动距离

    ;   //5秒刷新一次 private Handler refreshHandler = new Handler(){ //刷新界面的Handler public void handleMessag ...

  7. 线性表的链式实现(C++)

    相关内容: 线性表的顺序实现 链式实现(C语言) (推荐在GitHub上查看,下面代码只是最初实现,后续如果有修改,由于太麻烦,不会再更改下文,仅仅更新Github上的代码文件) 结点以及链表类的定义 ...

  8. eclipse adt开发android ndk没有NDK选项问题的解决方案

    原创 2015年01月28日 09:38:40 标签: android ndk / eclipse / adt 15989 今天是2015年1月28号,整理一下昨天使用eclipse adt搭建的an ...

  9. linux uart驱动——相关数据结构以及API(二)

    一.核心数据结构      串口驱动有3个核心数据结构,它们都定义在<#include linux/serial_core.h>1.uart_driver     uart_driver包 ...

  10. sharepoint 2013 资源管理器copy大文件到本地失败解决方法

    Error 0x800700DF: The file size exceeds the limit allowed and cannot be saved 中文错误信息是:文件大小超出同意范围.不能被 ...