1. import
2. import
3. import
4. import
5. import
6. import
7. import
8. import
9. import
10. /**
11. * 服务端
12. */
13. public class
14.   
15. /**
16.      * 服务器默认绑定端口
17.      */
18. public static final int DEFAULT_PORT = 9999;  
19.   
20. /**
21.      * 选择器
22.      */
23. public
24.   
25. public SocketServer(String ip, int
26. null;  
27. try
28. int
29. if (port > 0)  
30.                 _port = port;  
31. /* 获取通道 */
32.             ssc = ServerSocketChannel.open();  
33. /* 配置非阻塞 */
34. false);  
35. /**
36.              * 配置绑定端口 ServerSocketChannel没有bind()方法,
37.              * 因此有必要取出对等的ServerSocket并使用它来绑定到一
38.              * 个端口以开始监听连接
39.              */
40. new
41. /* 获取选择器 */
42. this.selector = Selector.open();  
43. /* 将通道注册到选择器 */
44. this.selector, SelectionKey.OP_ACCEPT);  
45. catch(ClosedChannelException e1){  
46. "关闭的通道,无法注册到选择器");  
47.             e1.printStackTrace();  
48. catch
49. try
50. if(ssc != null) ssc.close();  
51. catch
52.                 e.printStackTrace();  
53.             }  
54. "服务器绑定端口冲突");  
55.             e2.printStackTrace();  
56.         }  
57.     }  
58.       
59. /**
60.      * 轮询选择器
61.      * @throws Exception
62.      */
63. public void pollSelect() throws
64. /* (阻塞)轮询选择器,直到有事件 */
65. while (this.selector.select()>0) {  
66. /* 获取事件通知列表 */
67. this.selector.selectedKeys().iterator();  
68. while
69.                 SelectionKey selectKey = it.next();  
70.                 it.remove();  
71. try
72.                     process(selectKey);  
73. catch
74.                     e.printStackTrace();  
75. continue;  
76.                 }  
77.             }  
78.         }  
79.     }  
80.   
81. /**
82.      * 事件处理
83.      * @param selectKey
84.      */
85. public void process(SelectionKey selectKey) throws
86. if (selectKey.isAcceptable()) { /* 客户端连接事件 */
87.             accept(selectKey);  
88. else if (selectKey.isReadable()) { /* 可读事件 */
89.             read(selectKey);  
90.         }  
91.     }  
92.       
93. /**
94.      * 连接事件
95.      * @param selectKey
96.      */
97. public void accept(SelectionKey selectKey) throws
98. null;  
99. try
100.             ssc = (ServerSocketChannel) selectKey  
101.                     .channel();  
102.             SocketChannel sc = ssc.accept();  
103. false);  
104. /* 发送信息 */
105. new String("Hello World!")  
106.                     .getBytes()));  
107. /* 注册读事件 */
108. this.selector, SelectionKey.OP_READ);  
109. catch
110. if(ssc!=null)   
111.                 ssc.close();  
112. throw new IOException("关闭的通道,无法注册到选择器");  
113. catch
114. if(ssc!=null)   
115.                 ssc.close();  
116. throw new IOException("连接服务或配置失败!");  
117.         }  
118.     }  
119.       
120. /**
121.      * 可读事件
122.      * @param selectKey
123.      */
124. public void read(SelectionKey selectKey) throws
125. null;  
126. try
127. // 服务器可读取消息:得到事件发生的Socket通道
128.             channel = (SocketChannel) selectKey.channel();  
129. // 创建读取的缓冲区
130. 100);  
131.             channel.read(buffer);  
132. byte[] data = buffer.array();  
133. new
134. "客户端:"
135. // ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
136. // 将消息回送给客户端
137. // channel.write(outBuffer);
138. catch
139. if(channel != null)   
140.                 channel.close();  
141. throw new Exception("客户端将通道关闭,无法从通道读入缓冲或将缓冲数据写回通道!");  
142.         }  
143.     }  
144.       
145.       
146. public static void
147. null;  
148. try
149. new SocketServer("localhost", 9999);  
150.             ss.pollSelect();  
151. catch
152.             e.printStackTrace();  
153.         }  
154.     }  
155. }




1. import
2. import
3. import
4. import
5. import
6. import
7. import
8. import
9. /**
10. * 客户端
11. */
12. public class
13.   
14. public
15.       
16. public SocketClient(String ip, int
17. null;  
18. try
19. //channel = SocketChannel.open(new InetSocketAddress(ip,port));
20.             channel = SocketChannel.open();  
21. // 设置通道为非阻塞  
22. false);  
23. // 获得一个通道管理器  
24. this.selector = Selector.open();  
25. // 客户端连接服务器,其实方法执行并没有实现连接 
26. new
27. /**while(!channel.finishConnect()){
28.                 System.out.println("尝试连接....");
29.             }*/
30. // 注册连接事件。  
31. this.selector, SelectionKey.OP_CONNECT);    
32. catch(ClosedChannelException e1){  
33. "关闭的通道,无法注册到选择器");  
34.             e1.printStackTrace();  
35. catch
36. "连接异常!");  
37. try
38. if(channel != null) channel.close();  
39. catch
40.                 e.printStackTrace();  
41.             }  
42.             e2.printStackTrace();  
43.         }  
44.     }  
45.       
46. /**
47.      * 轮询选择器
48.      * @throws IOException
49.      */
50. public void  pollSelect() throws
51. /* (阻塞)轮询选择器,直到有事件 */
52. while ( this.selector.select() > 0
53. /* 获取事件通知列表 */
54. this.selector.selectedKeys().iterator();    
55. while
56.                 SelectionKey selectKey = (SelectionKey) ite.next();    
57. // 删除已选的key,以防重复处理  
58.                 ite.remove();    
59.                 process(selectKey);  
60.             }    
61.         }    
62.     }  
63.       
64. /**
65.      * 处理事件
66.      * @param selectKey
67.      */
68. public void process(SelectionKey selectKey) throws
69. if
70.             connect(selectKey);  
71. else if
72.             read(selectKey);  
73.         }    
74.     }  
75.       
76. /**
77.      * 连接事件
78.      * @param selectKey
79.      * @throws Exception
80.      */
81. public void connect(SelectionKey selectKey) throws
82. try
83.             SocketChannel channel = (SocketChannel) selectKey    
84.                     .channel();    
85. /* 如果正在连接,则完成连接 */
86. if(channel.isConnectionPending()){  
87. /**
88.                  * connect()方法尚未被调用,调用finishConnect()方法,
89.                  * 那么将产生NoConnectionPendingException
90.                  */
91.                 channel.finishConnect();    
92.             }  
93. /**
94.              * 在非阻塞模式下调用connect()方法之后,SocketChannel又被切换回了阻塞模式;那么如果
95.              * 有必要的话,调用线程会阻塞直到连接建立完成,finishConnect()方法接着就会返回true
96.              * 值。
97.              */
98. /* 设置成非阻塞 */
99. false);    
100. /* 给服务端发送信息 */
101. new String("编号001客户端连接成功!").getBytes()));    
102. /* 注册读事件 */
103. this.selector, SelectionKey.OP_READ);  
104. catch
105. throw new IOException("关闭的通道,无法注册到选择器");  
106. catch
107. throw new IOException("连接服务或配置失败!");  
108.         }  
109.     }  
110.       
111. /**
112.      * 读事件
113.      * @param selectKey
114.      * @throws Exception
115.      */
116. public void read(SelectionKey selectKey) throws
117. try
118. // 服务器可读通道
119.             SocketChannel channel = (SocketChannel) selectKey.channel();   
120. // 创建读取的缓冲区  
121. 100);    
122.             channel.read(buffer);    
123. byte[] data = buffer.array();    
124. new
125.             System.out.println(msg);    
126.             ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());  
127. // 将消息回送给服务端  
128. //channel.write(outBuffer);
129. catch
130. throw new IOException("服务端将通道关闭,无法从通道读入缓冲或将缓冲数据写回通道!");  
131.         }  
132.     }  
133.       
134. public static void
135. null;  
136. try
137. new SocketClient("localhost", 9999);  
138.             sc. pollSelect();  
139. catch
140.             e.printStackTrace();  
141.         }  
142.     }  
143. }



之二:



引用




继上节利用JAVA NIO实现简单数据传,本节实现自定义对象传输,为了实现接收方构建完整对象,自定义对象实现Serializable接口,以便完成对象序列化与反序化。 
下一节我们将采用线程池来管理读写,期待... 



1. import
2.   
3. /**
4.  * 传输对象,利用ObjectOutputStream、ObjectInputStream
5.  * 传输,所以对象需要可序列化
6.  * @author oy
7.  *
8.  */
9. public class User implements
10.       
11. private static final long
12.   
13. private int
14.       
15. private
16.       
17. /* Ignore this attribution */
18. private transient
19.   
20. public User(String name, int
21. this.name = name;  
22. this.age = age;  
23. this.school = school;  
24.     }  
25.   
26. public int
27. return
28.     }  
29. public void setAge(int
30. this.age = age;  
31.     }  
32. public
33. return
34.     }  
35.   
36. public void
37. this.name = name;  
38.     }  
39.   
40. public
41. return
42.     }  
43.   
44. public void
45. this.school = school;  
46.     }  
47.   
48. @Override
49. public
50. return "User [age=" + age + ", name=" + name + ", school="
51. "]";  
52.     }  
53. }



1. /* 发送对象信息 */
2. ByteArrayOutputStream baos = new
3. ObjectOutputStream oos = new
4. oos.writeObject(new User("Test", 30, "xx大学"));  
5. channel.write(ByteBuffer.wrap(baos.toByteArray()));



1. /* 接收对象信息 */
2. ByteBuffer buffer = ByteBuffer.allocate(1024);  
3. channel.read(buffer);  
4. byte[] data = buffer.array();  
5. ByteArrayInputStream bais = new
6. ObjectInputStream ois = new
7. User user = (User)ois.readObject();








之三:






本节采用JDK1.5之后java.util.concurrent包的API服务端实现线程 

池读取信息,可以接采用Executors工具快速创建线程池,也可以ExecutorService子类自 

定义创建。 
  
   客端连接服务端发送信息后关闭连接SOCKET短连接(HTTP为短连接),若采用SOCKET长 

连接,需要增加"心跳检测",本节暂未实现长连接。 
  
  因Selector轮询可读事件时,存在重读问题,解决办法是在读的代码块中加下述代码 

selectKey.cancel()或selectKey.interestOps(selectKey.interestOps() & 

(~SelectionKey.OP_READ)) 




    1. import
    2. import
    3. import
    4. import
    5. import
    6. import
    7. import
    8. import
    9. import
    10. import
    11. import
    12. /**
    13. * 服务端
    14. */
    15. public class
    16.   
    17. /**
    18.      * 服务器默认绑定端口
    19.      */
    20. public static final int DEFAULT_PORT = 9999;  
    21.   
    22. /**
    23.      * 选择器
    24.      */
    25. private
    26. /**
    27.      * 读取线程池
    28.      */
    29. private
    30.       
    31. public SocketServer(String ip, int
    32. null;  
    33. try
    34. int
    35. if (port > 0)  
    36.                 _port = port;  
    37. /* 获取通道 */
    38.             ssc = ServerSocketChannel.open();  
    39. /* 配置非阻塞 */
    40. false);  
    41. /**
    42.              * 配置绑定端口 ServerSocketChannel没有bind()方法,
    43.              * 因此有必要取出对等的ServerSocket并使用它来绑定到一
    44.              * 个端口以开始监听连接
    45.              */
    46. new
    47. /* 获取选择器 */
    48. this.selector = Selector.open();  
    49. /* 将通道注册到选择器 */
    50. this.selector, SelectionKey.OP_ACCEPT);  
    51.               
    52. /**
    53.              * 可以使用Executors,快速创建线程池,但是如果综合考虑
    54.              * CPU、内存等资及并发情况,可以采用自定方式创建池(
    55.              *  拒绝处理等问题
    56.              * ),此处简单自定义创建读取池
    57.              * */
    58. new ThreadPoolExecutor(1, 2, 2000L,  
    59. new LinkedBlockingQueue<Runnable>(5));  
    60. catch
    61. "服务器启动失败!");  
    62.             e2.printStackTrace();  
    63. try
    64. if(ssc != null) ssc.close();  
    65. catch
    66.                 e.printStackTrace();  
    67.             }  
    68.         }  
    69.     }  
    70.       
    71. /**
    72.      * 轮询选择器
    73.      * @throws Exception
    74.      */
    75. public void pollSelect() throws
    76. /* (阻塞)轮询选择器,直到有事件 */
    77. while (true) {  
    78. int readyChannels = 0;  
    79. /*选择器是否关闭*/
    80. if(this.selector.isOpen()){  
    81. this.selector.select();  
    82.             }  
    83. if(readyChannels == 0) continue;  
    84. /* 获取事件通知列表 */
    85. this.selector.selectedKeys().iterator();  
    86. while
    87.                 SelectionKey selectKey = it.next();  
    88.                 it.remove();  
    89. try
    90.                     process(selectKey);  
    91. catch
    92.                     e.printStackTrace();  
    93.                 }  
    94.             }  
    95.         }  
    96.     }  
    97.   
    98. /**
    99.      * 事件处理
    100.      * @param selectKey
    101.      */
    102. public void process(SelectionKey selectKey) throws
    103. /* 客户端连接事件 */
    104. if
    105.             accept(selectKey);  
    106. else if (selectKey.isReadable()) { /* 可读事件 */
    107.             read(selectKey);  
    108. /**
    109.              * 解决重复读
    110.              * 或设置不关注读事件来解决重复
    111.              * selectKey.interestOps(selectKey.interestOps() & (~SelectionKey.OP_READ));
    112.              * */
    113.             selectKey.cancel();  
    114.         }  
    115.     }  
    116.       
    117. /**
    118.      * 连接事件
    119.      * @param selectKey
    120.      */
    121. public void accept(SelectionKey selectKey) throws
    122. null;  
    123. try
    124.             ServerSocketChannel ssc = (ServerSocketChannel) selectKey  
    125.                     .channel();  
    126.             sc = ssc.accept();  
    127. false);  
    128. "客户端:"
    129. "端口"+sc.socket().getPort()  
    130. " 已连接");  
    131. this.selector, SelectionKey.OP_READ);  
    132. catch
    133. if(sc!=null)   
    134.                 sc.close();  
    135. throw new IOException("客户端已断开连接!");  
    136.         }  
    137.     }  
    138.       
    139. /**
    140.      * 可读事件
    141.      * @param selectKey
    142.      */
    143. public void read(SelectionKey selectKey) throws
    144.         SocketChannel sc = (SocketChannel) selectKey  
    145.                 .channel();  
    146. /*提交线程池处理*/
    147. new
    148.     }  
    149.       
    150.       
    151. public static void
    152. null;  
    153. try
    154. new SocketServer("localhost", 9999);  
    155.             ss.pollSelect();  
    156. catch
    157.             e.printStackTrace();  
    158.         }  
    159.     }  
    160. }




      1. import
      2. import
      3. import
      4. import
      5. import
      6.   
      7. /**
      8.  * 从线程中读信息
      9.  * @author oy
      10.  *
      11.  */
      12. public class SocketServerReadThread implements
      13.       
      14. private
      15.       
      16. public
      17. this.channel = channel;  
      18.     }  
      19.       
      20. @Override
      21. public void
      22. try
      23. // 创建读取的缓冲区
      24. 1024);  
      25.             channel.read(buffer);  
      26. byte[] data = buffer.array();  
      27. new
      28. new
      29.             User user = (User)ois.readObject();  
      30. "客户端:"
      31. "端口"+channel.socket().getPort() + " 消息: "
      32. catch
      33. "客户端已断开连接!");  
      34. try
      35. if(channel != null)   
      36.                     channel.close();  
      37. catch
      38.                 e1.printStackTrace();  
      39.             }  
      40.         }  
      41.     }  
      42. }