Socket的Channel在Selector上注册某一种动作,Selector通过select操作,监视所有在该Selector注册过的Channel的对应的动作,如果监测到某一对应的动作,则返回selectedKeys,自己手动取到各个SelectionKey进行相应的处理。当然NIO不仅可以接受Socket的Channel,还有文件操作等其他IO操作。
传统的Java 的IO,利用Socket建立服务器,接收客户端连接,一般都是为每一个连接建立一个线程,如果连接数巨大,那么服务器开销也将巨大。。NIO的原理,可以参照图:
Socket的Channel在Selector上注册某一种动作,Selector通过select操作,监视所有在该Selector注册过的Channel的对应的动作,如果监测到某一对应的动作,则返回selectedKeys,自己手动取到各个SelectionKey进行相应的处理。当然NIO不仅可以接受Socket的Channel,还有文件操作等其他IO操作。
作业的要求:
使用socket编程实现一个简单的文件服务器。客户端程序实现put功能(将一个文件从本地传到文件服务器)和get功能(从文件服务器取一远程文件存为本地文件)。客户端和文件服务器不在同一台机器上。
put [-h hostname] [-p portname] local_filename remote_filename
get [-h hostname] [-p portname] remote_filename local_filename
服务器端不使用nio,直接使用io的socket代码如下:
1. import java.io.*;
2. import .ServerSocket;
3. import .Socket;
4.
5. public class ServerMain {
6.
7. public static void main(String[] args) {
8.
9. class SocketThread extends Thread{
10.
11. private Socket socket;
12. private byte[] buf;
13. private int len = 0;
14. public SocketThread(Socket socket) {
15. this.socket = socket;
16. new byte[1024];
17. }
18.
19. @Override
20. public void run() {
21. try {
22. new DataInputStream(socket.getInputStream());
23. new DataOutputStream(socket.getOutputStream());
24.
25. //String command = dis.readUTF();
26. len = dis.read(buf);
27. new String(buf,0,len);
28.
29. "command=="+command);
30.
31. " ");
32. 0]; //命令 是put还是get
33. 1]; //文件名
34.
35. new File("C:\\",filename);//假设放在C盘
36. if(command.equals("get")){
37. if(!file.exists()){
38. //dos.writeUTF("notexists");
39. "notexists".getBytes());
40. dos.flush();
41. "没有这个文件,无法提供下载!");
42. dis.close();
43. dos.close();
44. socket.close();
45. return;
46. }
47. //dos.writeUTF("DownloadReady "+file.length());
48. "准备下载".getBytes());
49. dos.flush();
50.
51. "正在接受文件下载...");
52. new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
53.
54. while ((len = fis.read(buf))!= -1) {
55. 0, len);
56. }
57. dos.flush();
58.
59. fis.close();
60. "文件传输完成");
61. }
62. else {
63. //dos.writeUTF("UploadReady");
64. "UploadReady".getBytes());
65. dos.flush();
66.
67. "正在接受文件上传...");
68. DataOutputStream fileOut =
69. new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
70.
71. while ((len = dis.read(buf))!=-1) {
72. 0, len);
73. }
74. "上传完毕!");
75. fileOut.close();
76. }
77. dis.close();
78. dos.close();
79. socket.close();
80. catch (Exception e) {
81. e.printStackTrace();
82. }
83. }
84.
85. }
86.
87. "等待客户端连接....");
88. int index = 0;
89. try {
90. new ServerSocket(9527,300); //端口号9527 允许最大连接数300
91. while (true) {
92. Socket socket = server.accept();
93. "收到第"+(++index)+"个连接");
94. new SocketThread(socket).start(); //对每个连接创建一个线程
95. }
96. catch (Exception e) {
97. e.printStackTrace();
98. }
99. }
100. }
使用NIO建立的Socket服务器,代码如下:
1. import java.io.BufferedInputStream;
2. import java.io.BufferedOutputStream;
3. import java.io.DataInputStream;
4. import java.io.DataOutputStream;
5. import java.io.File;
6. import java.io.FileInputStream;
7. import java.io.FileOutputStream;
8. import java.io.IOException;
9. import .InetSocketAddress;
10. import java.nio.ByteBuffer;
11. import java.nio.CharBuffer;
12. import java.nio.channels.SelectionKey;
13. import java.nio.channels.Selector;
14. import java.nio.channels.ServerSocketChannel;
15. import java.nio.channels.SocketChannel;
16. import java.nio.charset.Charset;
17. import java.nio.charset.CharsetDecoder;
18. import java.nio.charset.CharsetEncoder;
19. import java.util.Iterator;
20.
21. public class NewSocketServer {
22.
23. private static final int port = 9527;
24. private Selector selector;
25. private ByteBuffer clientBuffer = ByteBuffer.allocate(1024);
26. private CharsetDecoder decoder = Charset.forName("GB2312").newDecoder();
27. private CharsetEncoder encoder = Charset.forName("GB2312").newEncoder();
28. //编码解码格式设置成GBK也行.UTF-8不行,中文乱码 (前提都是客户端没有设置任何编码解码格式)
29.
30. public void setListener() throws Exception{
31.
32. //打开选择器
33.
34. //定义一个 ServerSocketChannel通道
35. new InetSocketAddress(port)); //ServerSocketChannel绑定端口
36. false); //配置通道使用非阻塞模式
37. //该通道在selector上注册 接受连接的动作
38.
39. while(true)
40. {
41. //select() 会阻塞,直到在该selector上注册的channel有对应的消息读入
42. Iterator iter = selector.selectedKeys().iterator();
43. while (iter.hasNext()) {
44. SelectionKey key = (SelectionKey) iter.next();
45. // 删除此消息
46. // 当前线程内处理。(为了高效,一般会在另一个线程中处理此消息)
47. }
48. }
49. }
50.
51. private void process(SelectionKey key) throws IOException {
52. if (key.isAcceptable()) { // 接收请求
53. ServerSocketChannel server = (ServerSocketChannel) key.channel();
54. //类似于io的socket,ServerSocketChannel的accept函数返回 SocketChannel
55. false); //设置非阻塞模式
56. SelectionKey sKey = channel.register(selector, SelectionKey.OP_READ);
57. "read_command"); //这儿接收到连接请求之后可以为每个连接设置一个ID
58. }
59. else if (key.isReadable()) { // 读信息
60. SocketChannel channel = (SocketChannel) key.channel();
61. String name = (String) key.attachment();
62. if(name.equals("read_command")){
63. int count = channel.read(clientBuffer);
64. if (count > 0) {
65. clientBuffer.flip();
66. CharBuffer charBuffer = decoder.decode(clientBuffer);
67. String command = charBuffer.toString();
68.
69. //command形如:get abc.png 或者 put aaa.png
70. "command===="+command); //得到客户端传来的命令
71.
72. " ");
73. 0]; //命令 是put还是get
74. 1]; //文件名
75.
76. SelectionKey sKey = channel.register(selector,SelectionKey.OP_WRITE);
77. if(command.equals("put"))sKey.attach("UploadReady#"+filename); //要保护该通道的文件名
78. else if(command.equals("get")){
79. if(!new File("C:\\",filename).exists()){ //假设文件都是在C盘根目录
80. "没有这个文件,无法提供下载!");
81. "notexists");
82. }
83. else sKey.attach("DownloadReady#"+filename); //要保护该通道的文件名
84. }
85. else {
86. channel.close();
87. }
88. }
89. else if(name.startsWith("read_file")){//这儿可以新开一个线程 文件操作也可以用NIO
90. DataOutputStream fileOut =
91. new DataOutputStream(
92. new BufferedOutputStream(
93. new FileOutputStream(
94. new File("C:\\",name.split("#")[1]))));
95.
96. int passlen = channel.read(clientBuffer);
97. while (passlen>=0) {
98. clientBuffer.flip();
99. 0, passlen);
100. passlen = channel.read(clientBuffer);
101. }
102. "上传完毕!");
103. fileOut.close();
104. channel.close();
105. }
106. clientBuffer.clear();
107. }
108. else if (key.isWritable()) { // 写事件
109. SocketChannel channel = (SocketChannel) key.channel();
110. String flag = (String) key.attachment();
111. if(flag.startsWith("downloading")){//这儿可以新开一个线程 文件操作也可以用NIO
112. new DataInputStream(
113. new BufferedInputStream(
114. new FileInputStream(
115. new File("C:\\",flag.split("#")[1]))));
116.
117. byte[] buf = new byte[1024];
118. int len =0;
119. while ((len = fis.read(buf))!= -1) {
120. 0, len));
121. }
122. fis.close();
123. "文件传输完成");
124. channel.close();
125. }
126. else if(flag.equals("notexists")){
127. //channel.write(encoder.encode(CharBuffer.wrap(flag)));
128. //不用编码也行 客户端直接接收 中文也不是乱码
129. channel.close();
130. }
131. else if(flag.startsWith("UploadReady")){
132. "UploadReady")));
133.
134. //这儿如果不重新注册该通道的读操作 selector选择到该通道的将继续永远是写操作,也就无法跳转到上面的接受上传的处理
135. //register是覆盖的????!!!
136. "read_file#"+flag.split("#")[1]);
137. //key.attach("read_file#"+flag.split("#")[1]); //select不到读操作
138. }
139. else if(flag.startsWith("DownloadReady")){
140. "准备下载".getBytes()));
141. //channel.write(encoder.encode(CharBuffer.wrap("准备下载")));
142. "downloading#"+flag.split("#")[1]);
143. }
144. }
145. }
146.
147. public static void main(String[] args) {
148.
149. try {
150. "等待来至" + port + "端口的客户端连接.....");
151. new NewSocketServer().setListener();
152. catch (Exception e) {
153. e.printStackTrace();
154. }
155.
156. }
157. }
客户端代码如下:
1. import java.io.*;
2. import .InetAddress;
3. import .Socket;
4. import java.util.Scanner;
5.
6. public class ClientMain {
7.
8. private int ServerPort = 9527;
9. private String ServerAddress = "192.168.1.154";
10. private String GetOrPut = "get";
11. private String local_filename = "";
12. private String remote_filename = "";
13. private byte[] buf;
14. private int len;
15. class SocketThread extends Thread{
16.
17. @Override
18. public void run() {
19. try {
20.
21. new File("C:\\",local_filename); //假设文件放在C盘
22. if(!file.exists()&&GetOrPut.equals("put")){
23. "本地没有这个文件,无法上传!");
24. return;
25. }
26.
27. InetAddress loalhost = InetAddress.getLocalHost();
28. new Socket(ServerAddress,ServerPort,loalhost,44);
29. //服务器IP地址 端口号 本机IP 本机端口号
30. new DataInputStream(socket.getInputStream());
31. new DataOutputStream(socket.getOutputStream());
32.
33. //dos.writeUTF(GetOrPut+" "+remote_filename);//服务器端如果是io的socket,writeUTF和writeUTF对接
34. " "+remote_filename).getBytes());
35. dos.flush();
36.
37. //String tempString = dis.writeUTF();
38. new byte[1024];
39. len = dis.read(buf);
40. new String(buf,0,len);//服务器反馈的信息
41.
42. //System.out.println(tempString);
43. if(tempString.equals("notexists")){
44. "服务器没有这个文件,无法下载!");
45. dos.close();
46. dis.close();
47. socket.close();
48. return;
49. }
50.
51. if(tempString.startsWith("准备下载")){
52. DataOutputStream fileOut =
53. new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
54.
55. while ((len = dis.read(buf))!=-1) {
56. 0, len);
57. }
58. "下载完毕!");
59. fileOut.close();
60. dos.close();
61. dis.close();
62. socket.close();
63. }
64. else if(tempString.equals("UploadReady")){
65. "正在上传文件.......");
66. new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
67.
68. while ((len = fis.read(buf))!= -1) {
69. 0, len);
70. }
71. dos.flush();
72. "上传完毕!");
73. fis.close();
74. dis.close();
75. dos.close();
76. socket.close();
77. }
78.
79. catch (Exception e) {
80. e.printStackTrace();
81. }
82. }
83.
84. }
85.
86. public boolean checkCommand(String command)
87. {
88. if(!command.startsWith("put")&&!command.startsWith("get")){
89. "输入命令错误");
90. return false;
91. }
92.
93. int index = -1;
94. "";
95. null;
96.
97. if((index=command.indexOf("-h"))>0){
98. 3);
99. 0, temp.indexOf(' '));
100. ServerAddress = temp;
101. }
102. if((index=command.indexOf("-p"))>0){
103. 3);
104. 0, temp.indexOf(' '));
105. ServerPort = Integer.valueOf(temp);
106. }
107.
108. " ");
109. if(command.startsWith("put")){
110. "put";
111. 2];
112. 1];
113. }
114. else if(command.startsWith("get")){
115. "get";
116. 1];
117. 2];
118. }
119.
120. return true;
121. }
122.
123. public static void main(String[] args) {
124. new ClientMain();
125. new Scanner(System.in);
126. "";
127. do {
128. "请输入命令:");
129. commandString = sc.nextLine();
130. while (!thisC.checkCommand(commandString));
131.
132. new SocketThread();
133. a.start();
134. }
135. }

















