1、什么是同步与异步,堵塞与非堵塞

首先我们要明确搞明确:同步就等于堵塞?异步就等于非堵塞?这是不正确的,同步不等于阻 塞。而异步也不等于非堵塞。

1)那什么是同步编程?

什么是同步,就是在发出一个功能调用时。在没有得到结果之前,该调用就不返回。依据这个定义。android中绝大多数函数都是同步调用。可是一般而言。我们在谈论同步、异步的时候,特指那些须要其它部件协作或者须要一定时间完毕的任务。在android中,因为主线程(UI线程的不安全性),我们常常会用到handler的SendMessage函数,就是一个同步线程。它将数据传送给某个窗体后,在对方处理完消息后。这个函数是不会返回的,当处理完毕的时候才返回对应的返回值。

2)那什么是异步编程?

异步的概念和同步相反的。

当一个调用者异步发出一个功能调用时,调用者不能立马得到结果。

实际处理这个调用的部件在完毕后,通过状态、通知和回调来通知调用者。

以 android中AsyncTask类为例,顾名思义异步运行任务,在doInBackground 运行完毕后,onPostExecute 方法将被UI 线程调用,后台的计算结果将通过该方法传递到UI 线程,而且在界面上展示给用户.。在android或者java异步编程中须要注意下面几个知识点:回调,监听者模式,观察者模式。这几点在之后另外几篇文章中会提及。

3)什么是堵塞式编程?

堵塞调用是指调用结果返回之前。当前线程会被挂起。函数仅仅有在 得到结果之后才会返回。

由于这点定义跟同步编程的定义非常相像,所以非常多人觉得同步编程就等堵塞式编程。对于同步调用来说。非常多时候当前线程还是激活的,仅仅是从逻辑上当前函数没有返回而已。比如。我们在 socket编程中调用Receive函数,假设缓冲区中没有数据,这个函数就会一直等待,直到有数据才返回。而此时。当前线程还会继续处理各种各样的消
息。假设主窗体和调用函数在同一个线程中,除非你在特殊的界面操作函数中调用。事实上主界面还是应该能够刷新。可是在android中,因为主线程(UI线程)的不安全性。特别到4.0版本号后,系统已经不同意在主线程中进行耗时的同步编程。

所以android才出现了AsyncTask类用于异步编程。

4)什么是非堵塞式编程?

非堵塞和堵塞的概念相相应,指在不能立马得到结果之前,该函数不会堵塞当前线程,而会立马返回。

从这个定义上来说,非堵塞编程能够说是异步编程的一种,可是异步编程并不等于非堵塞式编程。

5)差别大概

我们用买票的案例去理解它,当我们去买票的时候。假设还在排队,一直排着。直到买到票再离开,这个就是同步编程(所谓同步就是当一个进程发起一个函数(任务)调用的时候,一直会到函数(任务)完毕)。

那还有另外一方式。你能够叫一个人(监听者,观察者)帮你看着。直接你买票了,再通知你。你能够先去别的事情(而异步这不会这样。异步情况下是当一个进程发
起一个函数(任务)调用的时候。不会等函数返回)。堵塞是就是等排队,非堵塞就是直接走开。

2、几个关键知识点

1)java.net.InetSocketAddress

此类实现 IP 套接字地址(IP 地址 + port号)。它还能够是一个对(主机名 + port号),在此情况下,将尝试解析主机名。假设解析失败,则该地址将被视为未解析 地址,可是其在某些情形下仍然能够使用,比方通过代理连接。
需注意接口:
public InetSocketAddress(InetAddress addr,int port)
依据 IP 地址和port号创建套接字地址。

有效port值介于 0 和 65535 之间。port号 zero 同意系统在 bind 操作中挑选临时的port。

2)java.nio.channels.Selector

可通过调用此类的 open 方法创建选择器,该方法将使用系统的默认选择器提供者创建新的选择器。

也可通过调用自己定义选择器提供者的 openSelector 方法来创建选择器。通过选择器的 close 方法关闭选择器之前,它一直保持打开状态。

需注意接口:
public static Selector open()throws IOException

打开一个选择器。

public abstract void close()throws IOException

关闭此选择器。

假设某个线程眼下正堵塞在此选择器的某个选择方法中,则中断该线程,如同调用该选择器的 wakeup 方法那样。

全部仍与此选择器关联的未取消键已无效、其通道已注销,而且与此选择器关联的全部其它资源已释放。

假设此选择器已经关闭。则调用此方法无效。

关闭选择器后,除了调用此方法或 wakeup 方法外,以不论什么其它方式继续使用它都将导致抛出 ClosedSelectorException。

注:选择器的关闭是关键点,特别须要注意上述第二条

3)java.nio.channels.SocketChannel

针对面向流的连接套接字的可选择通道。

套接字通道不是连接网络套接字的完整抽象。必须通过调用 socket 方法所获得的关联 Socket 对象来完毕对套接字选项的绑定、关闭和操作。

不可能为随意的已有套接字创建通道,也不可能指定与套接字通道关联的套接字所使用的 SocketImpl 对象。



通过调用此类的某个 open 方法创建套接字通道。新创建的套接字通道已打开,但尚未连接。试图在未连接的通道上调用 I/O 操作将导致抛出 NotYetConnectedException。可通过调用套接字通道的 connect 方法连接该通道;一旦连接后,关闭套接字通道之前它会一直保持已连接状态。可通过调用套接字通道的 isConnected 方法来确定套接字通道是否已连接。


套接字通道支持非堵塞连接:可创建一个套接字通道。而且通过 connect 方法能够发起到远程套接字的连接,之后通过 finishConnect 方法完毕该连接。

可通过调用 isConnectionPending 方法来确定是否正在进行连接操作。



可单独地关闭 套接字通道的输入端和输出端,而无需实际关闭该通道。调用关联套接字对象的 shutdownInput 方法来关闭某个通道的输入端将导致该通道上的兴许读取操作返回 -1(指示流的末尾)。调用关联套接字对象的 shutdownOutput 方法来关闭通道的输出端将导致该通道上的兴许写入操作抛出 ClosedChannelException。


套接字通道支持异步关闭,这与 Channel 类中所指定的异步 close 操作类似。假设一个线程关闭了某个套接字的输入端。而同一时候还有一个线程被堵塞在该套接字通道上的读取操作中,那么处于堵塞线程中的读取操作将完毕,而不读取不论什么字节且返回 -1。I假设一个线程关闭了某个套接字的输出端,而同一时候还有一个线程被堵塞在该套接字通道上的写入操作中,那么堵塞线程将收到 AsynchronousCloseException。


多个并发线程可安全地使用套接字通道。虽然在随意给定时刻最多仅仅能有一个线程进行读取和写入操作,但数据报通道支持并发的读写。

connect 和 finishConnect 方法是相互同步的,假设正在调用当中某个方法的同一时候试图发起读取或写入操作,则在该调用完毕之前该操作被堵塞。

3、实例代码演示

连接核心代码:

  1. Selector mSelector = null;
  2. ByteBuffer sendBuffer = null;
  3. SocketChannel client = null;
  4. InetSocketAddress isa = null;
  5. SocketEventListener mSocketEventListener = null;
  6. private boolean Connect(String site, int port)
  7. {
  8. if (mSocketEventListener != null)
  9. {
  10. mSocketEventListener.OnSocketPause();
  11. }
  12. boolean ret = false;
  13. try
  14. {
  15. mSelector = Selector.open();
  16. client = SocketChannel.open();
  17. client.socket().setSoTimeout(5000);
  18. isa = new InetSocketAddress(site, port);
  19. boolean isconnect = client.connect(isa);
  20. // 将客户端设定为异步
  21. client.configureBlocking(false);
  22. // 在轮讯对象中注冊此客户端的读取事件(就是当server向此客户端发送数据的时候)
  23. client.register(mSelector, SelectionKey.OP_READ);
  24.  
  25. long waittimes = 0;
  26.  
  27. if(!isconnect)
  28. {
  29. while (!client.finishConnect())
  30. {
  31. EngineLog.redLog(TAG, "等待非堵塞连接建立....");
  32. Thread.sleep(50);
  33. if(waittimes < 100)
  34. {
  35. waittimes++;
  36. }
  37. else
  38. {
  39. break;
  40. }
  41. }
  42. }
  43. Thread.sleep(500);
  44. haverepaired();
  45. startListener();
  46. ret = true;
  47. }
  48. catch (Exception e)
  49. {
  50. EngineLog.redLog(TAG + " - Connect error", e != null ? e.toString() : "null");
  51. try
  52. {
  53. Thread.sleep(1000 * 10);
  54. }
  55. catch (Exception e1)
  56. {
  57. EngineLog.redLog(TAG + " - Connect error", e1 != null ? e1.toString() : "null");
  58. }
  59. ret = false;
  60. }
  61. return ret;
  62. }
在上述代码中,我们能够看到有一个SocketEventListener监听接口,这个接口用于监听socket事件,将其回调给调用者

SocketEventListener接口:

  1. public interface SocketEventListener
  2. {
  3. /**
  4. * Socket正在接收数据
  5. * */
  6. public void OnStreamRecive();
  7. /**
  8. * Socket接收数据完毕
  9. * */
  10. public void OnStreamReciveFinish();
  11. /**
  12. * Socket有新的消息返回
  13. * */
  14. public void OnStreamComing(byte[] aStreamData);
  15. /**
  16. * Socket出现异常
  17. * */
  18. public void OnSocketPause();
  19. /**
  20. * Socket已修复,可用
  21. * */
  22. public void OnSocketAvaliable();
  23. }

监听接口的使用:

  1. rivate void startListener()
  2. {
  3. if (mReadThread == null || mReadThread.isInterrupted())
  4. {
  5. mReadThread = null;
  6. mReadThread = new Thread()
  7. {
  8. @Override
  9. public void run()
  10. {
  11. while (!this.isInterrupted() && mRunRead)
  12. {
  13. MyLineLog.redLog(TAG,"startListener:" + mSendMsgTime);
  14. try
  15. {
  16. // 假设client连接没有打开就退出循环
  17. if (!client.isOpen())
  18. break;
  19. // 此方法为查询是否有事件发生假设没有就堵塞,有的话返回事件数量
  20. int eventcount = mSelector.select();
  21. // 假设没有事件返回循环
  22. if (eventcount > 0)
  23. {
  24. starttime = CommonClass.getCurrentTime();
  25. // 遍例全部的事件
  26. for (SelectionKey key : mSelector.selectedKeys())
  27. {
  28. // 删除本次事件
  29. mSelector.selectedKeys().remove(key);
  30. // 假设本事件的类型为read时,表示server向本client发送了数据
  31. if (key.isValid() && key.isReadable())
  32. {
  33. if (mSocketEventListener != null)
  34. {
  35. mSocketEventListener.OnStreamRecive();
  36. }
  37. boolean readresult = ReceiveDataBuffer((SocketChannel) key.channel());
  38.  
  39. if (mSocketEventListener != null)
  40. {
  41. mSocketEventListener.OnStreamReciveFinish();
  42. }
  43.  
  44. if(readresult)
  45. {
  46. key.interestOps(SelectionKey.OP_READ);
  47. sleep(200);
  48. }
  49. else
  50. {
  51. throw new Exception();
  52. }
  53. }
  54. key = null;
  55. }
  56. mSelector.selectedKeys().clear();
  57. }
  58. }
  59. catch (Exception e)
  60. {
  61. mRunRead = false;
  62. mReadThread = null;
  63. if(e instanceof InterruptedException)
  64. {
  65. MyLineLog.redLog(TAG, "startListener:" + e.toString());
  66. }
  67. else
  68. {
  69. break;
  70. }
  71. }
  72. }
  73. }
  74. };
  75. mReadThread.setName(TAG + " Listener, " + CommonClass.getCurrentTime());
  76. mRunRead = true;
  77. mReadThread.start();
  78. }
  79. }

连接完之后就是发送数据和接收数据。以下是发送数据的核心代码:

  1. public boolean SendSocketMsg(byte[] aMessage) throws IOException
  2. {
  3. boolean ret = false;
  4. try
  5. {
  6. sendBuffer.clear();
  7. sendBuffer = ByteBuffer.wrap(aMessage);
  8. int sendsize = client.write(sendBuffer);
  9. sendBuffer.flip();
  10. sendBuffer.clear();
  11. mSendMsgTime = CommonClass.getCurrentTime();
  12. MyLineLog.redLog(TAG, "SendSocketMsg:" + mSendMsgTime + ", sendsize:" + sendsize);
  13. ret = true;
  14. }
  15. catch (Exception e)
  16. {
  17. MyLineLog.redLog(TAG, "发送数据失败。");
  18.  
  19. if (mSocketEventListener != null)
  20. {
  21. mSocketEventListener.OnSocketPause();
  22. }
  23. // crash();
  24. }
  25. return ret;
  26. }

由于实际工作须要,我们须要常常会碰到两个问题。无效数据和大数据,怎样去解决问题呢,无效数据用过滤,大数据用分块接收。以下是接收数据的方法:

  1. private boolean ReceiveDataBuffer(SocketChannel aSocketChannel)
  2. {
  3. // n 有数据的时候返回读取到的字节数。
  4. // 0 没有数据而且没有达到流的末端时返回0。
  5. // -1 当达到流末端的时候返回-1。
  6.  
  7. boolean ret = false;
  8.  
  9. ByteArrayBuffer bab = new ByteArrayBuffer(8*1024);
  10. while(true)
  11. {
  12. try
  13. {
  14. ByteBuffer readBuffer = ByteBuffer.allocate(1024 * 1);
  15. readBuffer.clear();
  16. int readsize = aSocketChannel.read(readBuffer);
  17.  
  18. if(readsize > 0)
  19. {
  20. MyLineLog.redLog(TAG, "aSocketChannel.read=>" + readsize);
  21. byte[] readbytes = readBuffer.array();
  22. bab.append(readbytes, 0, readsize);
  23. readBuffer.clear();
  24. readBuffer.flip();
  25. ret = true;
  26. }
  27. else if(readsize == 0)
  28. {
  29. int buffersize = bab.length();
  30. byte[] readdata = bab.buffer();
  31. int readdataoffset = 0;
  32. boolean parsedata = true;
  33.  
  34. while(readdataoffset < buffersize && parsedata)
  35. {
  36. byte datatype = readdata[readdataoffset];
  37. if (datatype == PushUtils.PACKAGETYPE_HEARTBEAT || datatype == PushUtils.PACKAGETYPE_HEARTBEAR_NODATA)
  38. {
  39. byte[] blockdata = new byte[] { datatype };
  40. ReceiveData(blockdata);
  41. readdataoffset += 1;
  42. blockdata = null;
  43. }
  44. else
  45. {
  46. byte[] blocklength = new byte[4];
  47. System.arraycopy(readdata, readdataoffset + 5, blocklength, 0, 4);
  48. int blocksize = CommonClass.bytes2int(CommonClass.LitteEndian_BigEndian(blocklength));
  49. blocklength = null;
  50.  
  51. int blockdatasize = 5 + blocksize + 4;
  52.  
  53. if(blockdatasize <= buffersize)
  54. {
  55. MyLineLog.redLog(TAG, "块数据大小:" + blockdatasize);
  56. byte[] blockdata = new byte[blockdatasize];
  57. System.arraycopy(readdata, readdataoffset, blockdata, 0, blockdatasize);
  58.  
  59. long starttime = CommonClass.getCurrentTime();
  60. ReceiveData(blockdata);
  61. long endtime = CommonClass.getCurrentTime();
  62. MyLineLog.redLog(TAG, "解析数据用时:" + (endtime - starttime) + "ms");
  63. readdataoffset += blockdatasize;
  64. blockdata = null;
  65. }
  66. else if(blockdatasize < 10240)
  67. {//小于10k,则属于正常包
  68. MyLineLog.redLog(TAG, "块数据大小:" + blockdatasize + ",小于10k,说明数据不完整。继续获取。");
  69. //将未解析数据存到暂时buffer
  70. int IncompleteSize = buffersize - readdataoffset;
  71. if(IncompleteSize > 0)
  72. {
  73. byte[] Incompletedata = new byte[IncompleteSize];
  74. System.arraycopy(readdata, readdataoffset, Incompletedata, 0, IncompleteSize);
  75. bab.clear();
  76. bab.append(Incompletedata, 0, IncompleteSize);
  77. parsedata = false;
  78. Incompletedata = null;
  79. }
  80. }
  81. else
  82. {//异常包
  83. MyLineLog.yellowLog(TAG, "块数据错误大小:" + blockdatasize);
  84. MyLineLog.redLog(TAG,"blockdatasize error:" + blockdatasize);
  85. ret = true;
  86. break;
  87. }
  88. }
  89. }
  90.  
  91. if(parsedata)
  92. {
  93. ret = true;
  94. break;
  95. }
  96. }
  97. else if(readsize == -1)
  98. {
  99. ret = false;
  100. break;
  101. }
  102. else
  103. {
  104. ret = true;
  105. break;
  106. }
  107. }
  108. catch (IOException e)
  109. {
  110. MyLineLog.redLog(TAG, "aSocketChannel IOException=>" + e.toString());
  111. ret = false;
  112. break;
  113. }
  114. }
  115. bab.clear();
  116. bab = null;
  117. return ret;
  118. }

假设数据量过大的话。还会使用压缩方法进行传输。那应该怎样接收呢,以下是一段接收压缩数据的方法:

  1. private void ReceiveData(byte[] aDataBlock)
  2. {
  3. try
  4. {
  5. MyLineLog.redLog(TAG, "ReceiveData:" + mSendMsgTime);
  6. if (mSendMsgTime != 0)
  7. {
  8. mSendMsgTime = 0;
  9. }
  10.  
  11. byte[] ret = null;
  12.  
  13. int offset = 0;
  14.  
  15. byte datatype = aDataBlock[offset];
  16. offset += 1;
  17.  
  18. if (datatype != -1)
  19. {
  20. if (datatype == PushUtils.PACKAGETYPE_HEARTBEAT)
  21. {
  22. ret = new byte[] { datatype };
  23. }
  24. else if (datatype == PushUtils.PACKAGETYPE_HEARTBEAR_NODATA)
  25. {
  26. ret = new byte[] { datatype };
  27. }
  28. else if (datatype == PushUtils.PACKAGETYPE_NORMAL || datatype == PushUtils.PACKAGETYPE_HEARTBEAR_HAVEDATA)
  29. {
  30. byte[] databytelength = new byte[4];
  31. System.arraycopy(aDataBlock, offset, databytelength, 0, 4);
  32. offset += 4;
  33. int header = CommonClass.bytes2int(CommonClass.LitteEndian_BigEndian(databytelength));
  34. databytelength = null;
  35.  
  36. if (header == PushUtils.PACKAGEHEADER)
  37. {
  38. byte[] datalengthbyte = new byte[4];
  39. System.arraycopy(aDataBlock, offset, datalengthbyte, 0, 4);
  40. offset += 4;
  41.  
  42. int datalength = CommonClass.bytes2int(CommonClass.LitteEndian_BigEndian(datalengthbyte));
  43. datalengthbyte = null;
  44.  
  45. if (datalength > 4)
  46. {
  47. // compressed bit 临时不压缩
  48. byte compressed = aDataBlock[offset];
  49. offset += 1;
  50.  
  51. if (compressed == 1)
  52. {//解压缩
  53. //跳过头4个字节,此处用于解压缩后的数据大小,临时不须要
  54. offset += 4;
  55. int contentlength = datalength - 1 - 4;
  56. byte[] datacontentbyte = new byte[contentlength];
  57. System.arraycopy(aDataBlock, offset, datacontentbyte, 0, contentlength);
  58. offset += contentlength;
  59.  
  60. byte[] compressdata = new byte[contentlength - 4];
  61. System.arraycopy(datacontentbyte, 0, compressdata, 0, contentlength - 4);
  62.  
  63. long starttime = CommonClass.getCurrentTime();
  64. byte[] decompressdatacontentbyte = CommonClass.decompress(compressdata);
  65. long endtime = CommonClass.getCurrentTime();
  66. MyLineLog.redLog(TAG, "解压缩数据用时:" + (endtime - starttime) + "ms");
  67. int decompressdatacontentbytelength = decompressdatacontentbyte.length;
  68. compressdata = null;
  69. int footer = PushUtils.getInt(datacontentbyte, contentlength - 4);
  70.  
  71. if (footer == PushUtils.PACKAGEFOOTER)
  72. {
  73. ret = new byte[decompressdatacontentbytelength + 1];
  74. ret[0] = datatype;
  75. System.arraycopy(decompressdatacontentbyte, 0, ret, 1, decompressdatacontentbytelength);
  76. datacontentbyte = null;
  77. decompressdatacontentbyte = null;
  78. }
  79. }
  80. else
  81. {//数据未压缩
  82. int contentlength = datalength - 1;
  83. byte[] datacontentbyte = new byte[contentlength];
  84. System.arraycopy(aDataBlock, offset, datacontentbyte, 0, contentlength);
  85. offset += contentlength;
  86.  
  87. int footer = PushUtils.getInt(datacontentbyte, contentlength - 4);
  88.  
  89. if (footer == PushUtils.PACKAGEFOOTER)
  90. {
  91. ret = new byte[contentlength + 1 - 4];
  92. ret[0] = datatype;
  93. System.arraycopy(datacontentbyte, 0, ret, 1, contentlength - 4);
  94. datacontentbyte = null;
  95. }
  96. }
  97. }
  98. }
  99. }
  100.  
  101. if (mSocketEventListener != null)
  102. {
  103. mSocketEventListener.OnStreamComing(ret);
  104. }
  105. }
  106. }
  107. catch (Exception e)
  108. {
  109. MyLineLog.redLog(TAG + " - ReceiveData error", e.toString());
  110. }
  111. }

在介绍SocketChannel的时候。api提到关闭须要注意事项。以下一段关闭SocketChannel的演示样例代码:

  1. public void closeSocket()
  2. {
  3. mRunRead = false;
  4. if (mReadThread != null)
  5. {
  6. if (!mReadThread.isInterrupted())
  7. {
  8. mReadThread.interrupt();
  9. mReadThread = null;
  10. }
  11. }
  12.  
  13. if (mSelector != null && mSelector.isOpen())
  14. {
  15. try
  16. {
  17. mSelector.close();
  18. }
  19. catch (IOException e)
  20. {
  21. MyLineLog.redLog(TAG + " - closeSocket error", e.toString());
  22. }
  23. mSelector = null;
  24. }
  25.  
  26. if (client != null)
  27. {
  28. try
  29. {
  30. client.close();
  31. client = null;
  32. }
  33. catch (IOException e)
  34. {
  35. MyLineLog.redLog(TAG + " - closeSocket2 error", e.toString());
  36. }
  37. }
  38.  
  39. System.gc();
  40. }

这篇文章解说部分大量參照JavaApi,事实上非常多问题的答案就在Api里面,当你不知道怎样去做的时候。回头看一下Api。细致思考一下,就能解决大部分问题。

Ps:感谢我大学舍友阿钢为我提供的代码


android中非堵塞socket通信的更多相关文章

  1. Android简单实现Socket通信,client连接server后,server向client发送文字数据

    案例实现的是简单的Socket通信,当client(Androidclient)连接到指定server以后,server向client发送一句话文字信息(你能够拓展其他的了) 先看一下服务端程序的实现 ...

  2. 从零开始学android -- 简易的socket通信

    先来介绍下socket,网上摘抄点资料,免得自己打字了 网络中进程之间如何通信? 本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 1.消息传递(管道.FIFO.消息队列) 2.同步(互 ...

  3. Android进程间通信之socket通信

    用Java中的socket编程. 通过socket实现两个应用之间的通信,可以接收和发送数据,同时将接收到的数据显示在activity界面上. Server端: ServerLastly.java p ...

  4. android wifi热点 socket通信

    1.首先建立wifi热点服务器  wifi客户端连接 2.开启一个子线程循环监听某个端口,进行数据流输入输出 /* 服务器 接收数据 */ class Receiver extends Thread ...

  5. Android 网络编程 Socket

    1.服务端开发 创建一个Java程序 public class MyServer { // 定义保存所有的Socket,与客户端建立连接得到一个Socket public static List< ...

  6. Android开发--Socket通信

    一.Socket通信简介 Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是"请求-响应方式",即在请求时 ...

  7. Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)

    转载地址:http://blog.csdn.net/mad1989/article/details/9147661 ZERO.前言 有关通信原理内容是在网上或百科整理得到,代码部分为本人所写,如果不当 ...

  8. 非堵塞socket实现android手机与PC的文件传输

    项目须要是通过WIFI建立手机和PC的通信,然后自己定义一个简单的协议对要传输的文件进行校验,传输的文件是2张3M的图片,要求考虑网络中断情况处理. 我这里採用的是非堵塞socket来实现的,之前查过 ...

  9. 基于android的Socket通信

    一.Socket通信简介 Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户 ...

随机推荐

  1. Nodejs express框架 浅析

    http://www.expressjs.com.cn/ 1. 中间件 ①挂载中间件的函数:app.use var http = require('http'); var express = requ ...

  2. 汕头市队赛 SRM1X T2 ——扫描线

    绵津见-终 SRM 13 背景 “西瓜也是可以种在海上的!”——绵津见 然而种在海上的西瓜最需要防范的,是时不时会涌向瓜田的阵阵海浪. 幸好,身为海神的绵津见可以释放魔法“水平如镜”来阻止海浪拍打西瓜 ...

  3. 读入输出优化_C++

    当我们考试时遇到大量的读入或者输出时,这些代码会耗费许多运行程序的时间,导致TL 本来 log2n 的算法因为读入被卡成线性的就太不划算了,所以我们这里要采用读入输出优化 getchar 和 putc ...

  4. gdb 脚本

    不满足于一条一条执行命令,我们可以将命令写进脚本里面,连续执行, 1: gdb启动会在当前目录寻找.gdbinit文件,并读取里面的命令列表 2: 我们可以启动gdb 时,加-x cmd.gdb 来指 ...

  5. OPEN SUSE LINUX

    1. 把中文界面变成英文界面 yast2->system->language: 主要语言: 美式英语US 2. 使用root用户默认登录 Ubuntu使用root登录        Ubu ...

  6. (十)Linux查看系统信息的一些命令及查看已安装软件包的命令

    转自:http://cheneyph.iteye.com/blog/824746 系统 # uname -a # 查看内核/操作系统/CPU信息 # head -n 1 /etc/issue # 查看 ...

  7. python的位运算

    # &: 都是1,才为1,否则为零 # |: 都是0,才为0,否则为1 # ^: 相同为0,相异为1 a = bin(20) b = bin(16) print(a) # 0b10100 pr ...

  8. OutputDebugString方便格式化WIN32封装

    void TRACE(LPCTSTR lpszFmt, ...) { va_list args; va_start(args, lpszFmt); ; TCHAR *lpszBuf = (TCHAR* ...

  9. shell 文件夹总大小 du -sh 文件夹

    du -sh 文件夹 du [-abcDhHklmsSx] [-L <符号连接>][-X <文件>][--block-size][--exclude=<目录或文件> ...

  10. Number of Connected Components in an Undirected Graph -- LeetCode

    Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), ...