Java Socket实战之六 使用NIO包实现Socket通信

Java Socket实战之六 使用NIO包实现Socket通信

前面几篇文章介绍了使用java.io和java.net类库实现的Socket通信,下面介绍一下使用java.nio类库实现的Socket。

java.nio包是Java在1.4之后增加的,用来提高I/O操作的效率。在nio包中主要包括以下几个类或接口:

* Buffer:缓冲区,用来临时存放输入或输出数据。

* Charset:用来把Unicode字符编码和其它字符编码互转。

* Channel:数据传输通道,用来把Buffer中的数据写入到数据源,或者把数据源中的数据读入到Buffer。

* Selector:用来支持异步I/O操作,也叫非阻塞I/O操作。


nio包中主要通过下面两个方面来提高I/O操作效率:

* 通过Buffer和Channel来提高I/O操作的速度。

* 通过Selector来支持非阻塞I/O操作。


下面来看一下程序中是怎么通过这些类库实现Socket功能。

首先介绍一下几个辅助类

辅助类SerializableUtil,这个类用来把java对象序列化成字节数组,或者把字节数组反序列化成java对象。

  1. packagecom.googlecode.garbagecan.test.socket;
  2. importjava.io.ByteArrayInputStream;
  3. importjava.io.ByteArrayOutputStream;
  4. importjava.io.IOException;
  5. importjava.io.ObjectInputStream;
  6. importjava.io.ObjectOutputStream;
  7. publicclassSerializableUtil{
  8. publicstaticbyte[]toBytes(Objectobject){
  9. ByteArrayOutputStreambaos=newByteArrayOutputStream();
  10. ObjectOutputStreamoos=null;
  11. try{
  12. oos=newObjectOutputStream(baos);
  13. oos.writeObject(object);
  14. byte[]bytes=baos.toByteArray();
  15. returnbytes;
  16. }catch(IOExceptionex){
  17. thrownewRuntimeException(ex.getMessage(),ex);
  18. }finally{
  19. try{
  20. oos.close();
  21. }catch(Exceptione){}
  22. }
  23. }
  24. publicstaticObjecttoObject(byte[]bytes){
  25. ByteArrayInputStreambais=newByteArrayInputStream(bytes);
  26. ObjectInputStreamois=null;
  27. try{
  28. ois=newObjectInputStream(bais);
  29. Objectobject=ois.readObject();
  30. returnobject;
  31. }catch(IOExceptionex){
  32. thrownewRuntimeException(ex.getMessage(),ex);
  33. }catch(ClassNotFoundExceptionex){
  34. thrownewRuntimeException(ex.getMessage(),ex);
  35. }finally{
  36. try{
  37. ois.close();
  38. }catch(Exceptione){}
  39. }
  40. }
  41. }

辅助类MyRequestObject和MyResponseObject,这两个类是普通的java对象,实现了Serializable接口。MyRequestObject类是Client发出的请求,MyResponseObject是Server端作出的响应。
  1. packagecom.googlecode.garbagecan.test.socket.nio;
  2. importjava.io.Serializable;
  3. publicclassMyRequestObjectimplementsSerializable{
  4. privatestaticfinallongserialVersionUID=1L;
  5. privateStringname;
  6. privateStringvalue;
  7. privatebyte[]bytes;
  8. publicMyRequestObject(Stringname,Stringvalue){
  9. this.name=name;
  10. this.value=value;
  11. this.bytes=newbyte[1024];
  12. }
  13. publicStringgetName(){
  14. returnname;
  15. }
  16. publicvoidsetName(Stringname){
  17. this.name=name;
  18. }
  19. publicStringgetValue(){
  20. returnvalue;
  21. }
  22. publicvoidsetValue(Stringvalue){
  23. this.value=value;
  24. }
  25. @Override
  26. publicStringtoString(){
  27. StringBuffersb=newStringBuffer();
  28. sb.append("Request[name:"+name+",value:"+value+",bytes:"+bytes.length+"]");
  29. returnsb.toString();
  30. }
  31. }
  32. packagecom.googlecode.garbagecan.test.socket.nio;
  33. importjava.io.Serializable;
  34. publicclassMyResponseObjectimplementsSerializable{
  35. privatestaticfinallongserialVersionUID=1L;
  36. privateStringname;
  37. privateStringvalue;
  38. privatebyte[]bytes;
  39. publicMyResponseObject(Stringname,Stringvalue){
  40. this.name=name;
  41. this.value=value;
  42. this.bytes=newbyte[1024];
  43. }
  44. publicStringgetName(){
  45. returnname;
  46. }
  47. publicvoidsetName(Stringname){
  48. this.name=name;
  49. }
  50. publicStringgetValue(){
  51. returnvalue;
  52. }
  53. publicvoidsetValue(Stringvalue){
  54. this.value=value;
  55. }
  56. @Override
  57. publicStringtoString(){
  58. StringBuffersb=newStringBuffer();
  59. sb.append("Response[name:"+name+",value:"+value+",bytes:"+bytes.length+"]");
  60. returnsb.toString();
  61. }
  62. }

下面主要看一下Server端的代码,其中有一些英文注释对理解代码很有帮助,注释主要是来源jdk的文档和例子,这里就没有再翻译
  1. packagecom.googlecode.garbagecan.test.socket.nio;
  2. importjava.io.ByteArrayOutputStream;
  3. importjava.io.IOException;
  4. importjava.net.InetSocketAddress;
  5. importjava.nio.ByteBuffer;
  6. importjava.nio.channels.ClosedChannelException;
  7. importjava.nio.channels.SelectionKey;
  8. importjava.nio.channels.Selector;
  9. importjava.nio.channels.ServerSocketChannel;
  10. importjava.nio.channels.SocketChannel;
  11. importjava.util.Iterator;
  12. importjava.util.logging.Level;
  13. importjava.util.logging.Logger;
  14. importcom.googlecode.garbagecan.test.socket.SerializableUtil;
  15. publicclassMyServer3{
  16. privatefinalstaticLoggerlogger=Logger.getLogger(MyServer3.class.getName());
  17. publicstaticvoidmain(String[]args){
  18. Selectorselector=null;
  19. ServerSocketChannelserverSocketChannel=null;
  20. try{
  21. //Selectorforincomingtimerequests
  22. selector=Selector.open();
  23. //Createanewserversocketandsettononblockingmode
  24. serverSocketChannel=ServerSocketChannel.open();
  25. serverSocketChannel.configureBlocking(false);
  26. //Bindtheserversockettothelocalhostandport
  27. serverSocketChannel.socket().setReuseAddress(true);
  28. serverSocketChannel.socket().bind(newInetSocketAddress(10000));
  29. //Registeracceptsontheserversocketwiththeselector.This
  30. //steptellstheselectorthatthesocketwantstobeputonthe
  31. //readylistwhenacceptoperationsoccur,soallowingmultiplexed
  32. //non-blockingI/Ototakeplace.
  33. serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
  34. //Here'swhereeverythinghappens.Theselectmethodwill
  35. //returnwhenanyoperationsregisteredabovehaveoccurred,the
  36. //threadhasbeeninterrupted,etc.
  37. while(selector.select()>0){
  38. //SomeoneisreadyforI/O,getthereadykeys
  39. Iterator<SelectionKey>it=selector.selectedKeys().iterator();
  40. //Walkthroughthereadykeyscollectionandprocessdaterequests.
  41. while(it.hasNext()){
  42. SelectionKeyreadyKey=it.next();
  43. it.remove();
  44. //Thekeyindexesintotheselectorsoyou
  45. //canretrievethesocketthat'sreadyforI/O
  46. execute((ServerSocketChannel)readyKey.channel());
  47. }
  48. }
  49. }catch(ClosedChannelExceptionex){
  50. logger.log(Level.SEVERE,null,ex);
  51. }catch(IOExceptionex){
  52. logger.log(Level.SEVERE,null,ex);
  53. }finally{
  54. try{
  55. selector.close();
  56. }catch(Exceptionex){}
  57. try{
  58. serverSocketChannel.close();
  59. }catch(Exceptionex){}
  60. }
  61. }
  62. privatestaticvoidexecute(ServerSocketChannelserverSocketChannel)throwsIOException{
  63. SocketChannelsocketChannel=null;
  64. try{
  65. socketChannel=serverSocketChannel.accept();
  66. MyRequestObjectmyRequestObject=receiveData(socketChannel);
  67. logger.log(Level.INFO,myRequestObject.toString());
  68. MyResponseObjectmyResponseObject=newMyResponseObject(
  69. "responsefor"+myRequestObject.getName(),
  70. "responsefor"+myRequestObject.getValue());
  71. sendData(socketChannel,myResponseObject);
  72. logger.log(Level.INFO,myResponseObject.toString());
  73. }finally{
  74. try{
  75. socketChannel.close();
  76. }catch(Exceptionex){}
  77. }
  78. }
  79. privatestaticMyRequestObjectreceiveData(SocketChannelsocketChannel)throwsIOException{
  80. MyRequestObjectmyRequestObject=null;
  81. ByteArrayOutputStreambaos=newByteArrayOutputStream();
  82. ByteBufferbuffer=ByteBuffer.allocate(1024);
  83. try{
  84. byte[]bytes;
  85. intsize=0;
  86. while((size=socketChannel.read(buffer))>=0){
  87. buffer.flip();
  88. bytes=newbyte[size];
  89. buffer.get(bytes);
  90. baos.write(bytes);
  91. buffer.clear();
  92. }
  93. bytes=baos.toByteArray();
  94. Objectobj=SerializableUtil.toObject(bytes);
  95. myRequestObject=(MyRequestObject)obj;
  96. }finally{
  97. try{
  98. baos.close();
  99. }catch(Exceptionex){}
  100. }
  101. returnmyRequestObject;
  102. }
  103. privatestaticvoidsendData(SocketChannelsocketChannel,MyResponseObjectmyResponseObject)throwsIOException{
  104. byte[]bytes=SerializableUtil.toBytes(myResponseObject);
  105. ByteBufferbuffer=ByteBuffer.wrap(bytes);
  106. socketChannel.write(buffer);
  107. }
  108. }

下面是Client的代码,代码比较简单就是启动了100个线程来访问Server
  1. packagecom.googlecode.garbagecan.test.socket.nio;
  2. importjava.io.ByteArrayOutputStream;
  3. importjava.io.IOException;
  4. importjava.net.InetSocketAddress;
  5. importjava.net.SocketAddress;
  6. importjava.nio.ByteBuffer;
  7. importjava.nio.channels.SocketChannel;
  8. importjava.util.logging.Level;
  9. importjava.util.logging.Logger;
  10. importcom.googlecode.garbagecan.test.socket.SerializableUtil;
  11. publicclassMyClient3{
  12. privatefinalstaticLoggerlogger=Logger.getLogger(MyClient3.class.getName());
  13. publicstaticvoidmain(String[]args)throwsException{
  14. for(inti=0;i<100;i++){
  15. finalintidx=i;
  16. newThread(newMyRunnable(idx)).start();
  17. }
  18. }
  19. privatestaticfinalclassMyRunnableimplementsRunnable{
  20. privatefinalintidx;
  21. privateMyRunnable(intidx){
  22. this.idx=idx;
  23. }
  24. publicvoidrun(){
  25. SocketChannelsocketChannel=null;
  26. try{
  27. socketChannel=SocketChannel.open();
  28. SocketAddresssocketAddress=newInetSocketAddress("localhost",10000);
  29. socketChannel.connect(socketAddress);
  30. MyRequestObjectmyRequestObject=newMyRequestObject("request_"+idx,"request_"+idx);
  31. logger.log(Level.INFO,myRequestObject.toString());
  32. sendData(socketChannel,myRequestObject);
  33. MyResponseObjectmyResponseObject=receiveData(socketChannel);
  34. logger.log(Level.INFO,myResponseObject.toString());
  35. }catch(Exceptionex){
  36. logger.log(Level.SEVERE,null,ex);
  37. }finally{
  38. try{
  39. socketChannel.close();
  40. }catch(Exceptionex){}
  41. }
  42. }
  43. privatevoidsendData(SocketChannelsocketChannel,MyRequestObjectmyRequestObject)throwsIOException{
  44. byte[]bytes=SerializableUtil.toBytes(myRequestObject);
  45. ByteBufferbuffer=ByteBuffer.wrap(bytes);
  46. socketChannel.write(buffer);
  47. socketChannel.socket().shutdownOutput();
  48. }
  49. privateMyResponseObjectreceiveData(SocketChannelsocketChannel)throwsIOException{
  50. MyResponseObjectmyResponseObject=null;
  51. ByteArrayOutputStreambaos=newByteArrayOutputStream();
  52. try{
  53. ByteBufferbuffer=ByteBuffer.allocateDirect(1024);
  54. byte[]bytes;
  55. intcount=0;
  56. while((count=socketChannel.read(buffer))>=0){
  57. buffer.flip();
  58. bytes=newbyte[count];
  59. buffer.get(bytes);
  60. baos.write(bytes);
  61. buffer.clear();
  62. }
  63. bytes=baos.toByteArray();
  64. Objectobj=SerializableUtil.toObject(bytes);
  65. myResponseObject=(MyResponseObject)obj;
  66. socketChannel.socket().shutdownInput();
  67. }finally{
  68. try{
  69. baos.close();
  70. }catch(Exceptionex){}
  71. }
  72. returnmyResponseObject;
  73. }
  74. }
  75. }

最后测试上面的代码,首先运行Server类,然后运行Client类,就可以分别在Server端和Client端控制台看到发送或接收到的MyRequestObject或MyResponseObject对象了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值