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. }