利用NIO建立Socket服務(wù)器
傳統(tǒng)的Java 的IO,利用Socket建立服務(wù)器,接收客戶端連接,一般都是為每一個連接建立一個線程,如果連接數(shù)巨大,那么服務(wù)器開銷也將巨大。。NIO的原理,可以參照圖:
Socket的Channel在Selector上注冊某一種動作,Selector通過select操作,監(jiān)視所有在該Selector注冊過的Channel的對應(yīng)的動作,如果監(jiān)測到某一對應(yīng)的動作,則返回selectedKeys,自己手動取到各個SelectionKey進行相應(yīng)的處理。當(dāng)然NIO不僅可以接受Socket的Channel,還有文件操作等其他IO操作。
作業(yè)的要求:
使用socket編程實現(xiàn)一個簡單的文件服務(wù)器??蛻舳顺绦?qū)崿F(xiàn)put功能(將一個文件從本地傳到文件服務(wù)器)和get功能(從文件服務(wù)器取一遠程文件存為本地文件)。客戶端和文件服務(wù)器不在同一臺機器上。
put [-h hostname] [-p portname] local_filename remote_filename get [-h hostname] [-p portname] remote_filename local_filename
服務(wù)器端不使用nio,直接使用io的socket代碼如下:
- import java.io.*;
- import java.net.ServerSocket;
- import java.net.Socket;
- public class ServerMain {
- public static void main(String[] args) {
- class SocketThread extends Thread{
- private Socket socket;
- private byte[] buf;
- private int len = 0;
- public SocketThread(Socket socket) {
- this.socket = socket;
- buf = new byte[1024];
- }
- @Override
- public void run() {
- try {
- DataInputStream dis = new DataInputStream(socket.getInputStream());
- DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
- //String command = dis.readUTF();
- len = dis.read(buf);
- String command = new String(buf,0,len);
- System.out.println("command=="+command);
- String[] temp =command.split(" ");
- command = temp[0]; //命令 是put還是get
- String filename = temp[1]; //文件名
- File file = new File("C:\\",filename);//假設(shè)放在C盤
- if(command.equals("get")){
- if(!file.exists()){
- //dos.writeUTF("notexists");
- dos.write("notexists".getBytes());
- dos.flush();
- System.out.println("沒有這個文件,無法提供下載!");
- dis.close();
- dos.close();
- socket.close();
- return;
- }
- //dos.writeUTF("DownloadReady "+file.length());
- dos.write("準(zhǔn)備下載".getBytes());
- dos.flush();
- System.out.println("正在接受文件下載...");
- DataInputStream fis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
- while ((len = fis.read(buf))!= -1) {
- dos.write(buf, 0, len);
- }
- dos.flush();
- fis.close();
- System.out.println("文件傳輸完成");
- }
- else {
- //dos.writeUTF("UploadReady");
- dos.write("UploadReady".getBytes());
- dos.flush();
- System.out.println("正在接受文件上傳...");
- DataOutputStream fileOut =
- new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
- while ((len = dis.read(buf))!=-1) {
- fileOut.write(buf, 0, len);
- }
- System.out.println("上傳完畢!");
- fileOut.close();
- }
- dis.close();
- dos.close();
- socket.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- System.out.println("等待客戶端連接....");
- int index = 0;
- try {
- ServerSocket server = new ServerSocket(9527,300); //端口號9527 允許***連接數(shù)300
- while (true) {
- Socket socket = server.accept();
- System.out.println("收到第"+(++index)+"個連接");
- new SocketThread(socket).start(); //對每個連接創(chuàng)建一個線程
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
使用NIO建立的Socket服務(wù)器,代碼如下:
- import java.io.BufferedInputStream;
- import java.io.BufferedOutputStream;
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.nio.ByteBuffer;
- import java.nio.CharBuffer;
- import java.nio.channels.SelectionKey;
- import java.nio.channels.Selector;
- import java.nio.channels.ServerSocketChannel;
- import java.nio.channels.SocketChannel;
- import java.nio.charset.Charset;
- import java.nio.charset.CharsetDecoder;
- import java.nio.charset.CharsetEncoder;
- import java.util.Iterator;
- public class NewSocketServer {
- private static final int port = 9527;
- private Selector selector;
- private ByteBuffer clientBuffer = ByteBuffer.allocate(1024);
- private CharsetDecoder decoder = Charset.forName("GB2312").newDecoder();
- private CharsetEncoder encoder = Charset.forName("GB2312").newEncoder();
- //編碼解碼格式設(shè)置成GBK也行.UTF-8不行,中文亂碼 (前提都是客戶端沒有設(shè)置任何編碼解碼格式)
- public void setListener() throws Exception{
- selector = Selector.open(); //打開選擇器
- ServerSocketChannel server = ServerSocketChannel.open(); //定義一個 ServerSocketChannel通道
- server.socket().bind(new InetSocketAddress(port)); //ServerSocketChannel綁定端口
- server.configureBlocking(false); //配置通道使用非阻塞模式
- server.register(selector, SelectionKey.OP_ACCEPT); //該通道在selector上注冊 接受連接的動作
- while(true)
- {
- selector.select(); //select() 會阻塞,直到在該selector上注冊的channel有對應(yīng)的消息讀入
- Iterator iter = selector.selectedKeys().iterator();
- while (iter.hasNext()) {
- SelectionKey key = (SelectionKey) iter.next();
- iter.remove(); // 刪除此消息
- process(key); // 當(dāng)前線程內(nèi)處理。(為了高效,一般會在另一個線程中處理此消息)
- }
- }
- }
- private void process(SelectionKey key) throws IOException {
- if (key.isAcceptable()) { // 接收請求
- ServerSocketChannel server = (ServerSocketChannel) key.channel();
- SocketChannel channel = server.accept();//類似于io的socket,ServerSocketChannel的accept函數(shù)返回 SocketChannel
- channel.configureBlocking(false); //設(shè)置非阻塞模式
- SelectionKey sKey = channel.register(selector, SelectionKey.OP_READ);
- sKey.attach("read_command"); //這兒接收到連接請求之后可以為每個連接設(shè)置一個ID
- }
- else if (key.isReadable()) { // 讀信息
- SocketChannel channel = (SocketChannel) key.channel();
- String name = (String) key.attachment();
- if(name.equals("read_command")){
- int count = channel.read(clientBuffer);
- if (count > 0) {
- clientBuffer.flip();
- CharBuffer charBuffer = decoder.decode(clientBuffer);
- String command = charBuffer.toString();
- //command形如:get abc.png 或者 put aaa.png
- System.out.println("command===="+command); //得到客戶端傳來的命令
- String[] temp =command.split(" ");
- command = temp[0]; //命令 是put還是get
- String filename = temp[1]; //文件名
- SelectionKey sKey = channel.register(selector,SelectionKey.OP_WRITE);
- if(command.equals("put"))sKey.attach("UploadReady#"+filename); //要保護該通道的文件名
- else if(command.equals("get")){
- if(!new File("C:\\",filename).exists()){ //假設(shè)文件都是在C盤根目錄
- System.out.println("沒有這個文件,無法提供下載!");
- sKey.attach("notexists");
- }
- else sKey.attach("DownloadReady#"+filename); //要保護該通道的文件名
- }
- } else {
- channel.close();
- }
- }
- else if(name.startsWith("read_file")){//這兒可以新開一個線程 文件操作也可以用NIO
- DataOutputStream fileOut =
- new DataOutputStream(
- new BufferedOutputStream(
- new FileOutputStream(
- new File("C:\\",name.split("#")[1]))));
- int passlen = channel.read(clientBuffer);
- while (passlen>=0) {
- clientBuffer.flip();
- fileOut.write(clientBuffer.array(), 0, passlen);
- passlen = channel.read(clientBuffer);
- }
- System.out.println("上傳完畢!");
- fileOut.close();
- channel.close();
- }
- clientBuffer.clear();
- }
- else if (key.isWritable()) { // 寫事件
- SocketChannel channel = (SocketChannel) key.channel();
- String flag = (String) key.attachment();
- if(flag.startsWith("downloading")){//這兒可以新開一個線程 文件操作也可以用NIO
- DataInputStream fis = new DataInputStream(
- new BufferedInputStream(
- new FileInputStream(
- new File("C:\\",flag.split("#")[1]))));
- byte[] buf = new byte[1024];
- int len =0;
- while ((len = fis.read(buf))!= -1) {
- channel.write(ByteBuffer.wrap(buf, 0, len));
- }
- fis.close();
- System.out.println("文件傳輸完成");
- channel.close();
- }
- else if(flag.equals("notexists")){
- //channel.write(encoder.encode(CharBuffer.wrap(flag)));
- channel.write(ByteBuffer.wrap(flag.getBytes())); //不用編碼也行 客戶端直接接收 中文也不是亂碼
- channel.close();
- }
- else if(flag.startsWith("UploadReady")){
- channel.write(encoder.encode(CharBuffer.wrap("UploadReady")));
- //這兒如果不重新注冊該通道的讀操作 selector選擇到該通道的將繼續(xù)永遠是寫操作,也就無法跳轉(zhuǎn)到上面的接受上傳的處理
- SelectionKey sKey =channel.register(selector, SelectionKey.OP_READ);//register是覆蓋的????!!!
- sKey.attach("read_file#"+flag.split("#")[1]);
- //key.attach("read_file#"+flag.split("#")[1]); //select不到讀操作
- }
- else if(flag.startsWith("DownloadReady")){
- channel.write(ByteBuffer.wrap("準(zhǔn)備下載".getBytes()));
- //channel.write(encoder.encode(CharBuffer.wrap("準(zhǔn)備下載")));
- key.attach("downloading#"+flag.split("#")[1]);
- }
- }
- }
- public static void main(String[] args) {
- try {
- System.out.println("等待來至" + port + "端口的客戶端連接.....");
- new NewSocketServer().setListener();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
客戶端代碼如下:
- import java.io.*;
- import java.net.InetAddress;
- import java.net.Socket;
- import java.util.Scanner;
- public class ClientMain {
- private int ServerPort = 9527;
- private String ServerAddress = "192.168.1.154";
- private String GetOrPut = "get";
- private String local_filename = "";
- private String remote_filename = "";
- private byte[] buf;
- private int len;
- class SocketThread extends Thread{
- @Override
- public void run() {
- try {
- File file = new File("C:\\",local_filename); //假設(shè)文件放在C盤
- if(!file.exists()&&GetOrPut.equals("put")){
- System.out.println("本地沒有這個文件,無法上傳!");
- return;
- }
- InetAddress loalhost = InetAddress.getLocalHost();
- Socket socket = new Socket(ServerAddress,ServerPort,loalhost,44);
- //服務(wù)器IP地址 端口號 本機IP 本機端口號
- DataInputStream dis = new DataInputStream(socket.getInputStream());
- DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
- //dos.writeUTF(GetOrPut+" "+remote_filename);//服務(wù)器端如果是io的socket,writeUTF和writeUTF對接
- dos.write((GetOrPut+" "+remote_filename).getBytes());
- dos.flush();
- //String tempString = dis.writeUTF();
- buf = new byte[1024];
- len = dis.read(buf);
- String tempString = new String(buf,0,len);//服務(wù)器反饋的信息
- //System.out.println(tempString);
- if(tempString.equals("notexists")){
- System.out.println("服務(wù)器沒有這個文件,無法下載!");
- dos.close();
- dis.close();
- socket.close();
- return;
- }
- if(tempString.startsWith("準(zhǔn)備下載")){
- DataOutputStream fileOut =
- new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
- while ((len = dis.read(buf))!=-1) {
- fileOut.write(buf, 0, len);
- }
- System.out.println("下載完畢!");
- fileOut.close();
- dos.close();
- dis.close();
- socket.close();
- }
- else if(tempString.equals("UploadReady")){
- System.out.println("正在上傳文件.......");
- DataInputStream fis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
- while ((len = fis.read(buf))!= -1) {
- dos.write(buf, 0, len);
- }
- dos.flush();
- System.out.println("上傳完畢!");
- fis.close();
- dis.close();
- dos.close();
- socket.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- public boolean checkCommand(String command)
- {
- if(!command.startsWith("put")&&!command.startsWith("get")){
- System.out.println("輸入命令錯誤");
- return false;
- }
- int index = -1;
- String temp = "";
- String[] tempStrings = null;
- if((index=command.indexOf("-h"))>0){
- temp = command.substring(index+3);
- temp = temp.substring(0, temp.indexOf(' '));
- ServerAddress = temp;
- }
- if((index=command.indexOf("-p"))>0){
- temp = command.substring(index+3);
- temp = temp.substring(0, temp.indexOf(' '));
- ServerPort = Integer.valueOf(temp);
- }
- tempStrings = command.split(" ");
- if(command.startsWith("put")){
- GetOrPut = "put";
- local_filename = tempStrings[tempStrings.length-2];
- remote_filename = tempStrings[tempStrings.length-1];
- }
- else if(command.startsWith("get")){
- GetOrPut = "get";
- local_filename = tempStrings[tempStrings.length-1];
- remote_filename = tempStrings[tempStrings.length-2];
- }
- return true;
- }
- public static void main(String[] args) {
- ClientMain thisC= new ClientMain();
- Scanner sc = new Scanner(System.in);
- String commandString = "";
- do {
- System.out.println("請輸入命令:");
- commandString = sc.nextLine();
- } while (!thisC.checkCommand(commandString));
- ClientMain.SocketThread a = thisC.new SocketThread();
- a.start();
- }
- }
原文鏈接:http://tonl.iteye.com/blog/1202817
【編輯推薦】