NIO Socket實現檔案伺服器
一、背景
很多時候我們都會用FTP工具來實現檔案的上傳下載功能,於是琢磨著也用java的相關知識來自己實現一個簡單的檔案伺服器
二、NIO Socket
考慮到檔案的上傳下載其實就是客戶端和伺服器進行通訊,然後進行資料交換。此時就可以採用Socket來實現。從JDK1.4版本以來java提供了更加高效的非阻塞形式的Socket,也就是NIO方式的Socket,通過通道Channel和緩衝器Buffer的方式來進行資料的讀寫。關於NIO的介紹,這裡找了網上的一篇文章,可以參考一下 JAVA NIO簡介
三、實施步驟
1、實現檔案的上傳下載等,需要伺服器和客戶端兩部分。服務端我們取名為FileCenter,客戶端我們取名為FileClient
2、首先實現FileCenter,具體程式碼如下,理解可參照註釋
package org.filecenter;
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.net.ServerSocket;
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;
/**
* 檔案下中心,命令模式如下
* 顯示檔案:list
* 列出所有可下載的檔案
* 下載檔案:get server_file [local_file]
* server_file伺服器中的檔名,local_file本地儲存路徑
* 上傳檔案:put local_file [server_file]
* local_file本地檔案路徑,server_file伺服器儲存檔名
*
* @author cyxl
*
*/
public class FileCenter {
private static Selector selector; // 選擇器
private static final int server_port = 12345; // 伺服器埠
private static CharsetDecoder decoder = Charset.forName("GB2312")
.newDecoder(); // 位元組轉字元
private static CharsetEncoder encoder = Charset.forName("GB2312")
.newEncoder(); // 字元轉位元組
private static ByteBuffer buffer = ByteBuffer.allocate(1024);
private static final String server_path = "C:\\file_center\\"; // 伺服器檔案路徑
public static void main(String[] args) {
try {
selector = Selector.open();// 開啟選擇器
ServerSocketChannel serverChannel = ServerSocketChannel.open();
ServerSocket server = serverChannel.socket();
server.bind(new InetSocketAddress(server_port));
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("等待客戶端連線……");
while (true) {
selector.select();
Iterator<SelectionKey> itr = selector.selectedKeys().iterator();
while (itr.hasNext()) {
SelectionKey key = itr.next();
itr.remove();
process(key);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void process(SelectionKey key) throws IOException {
if (key.isAcceptable()) {
// 連線
ServerSocketChannel serverChannel = (ServerSocketChannel) key
.channel();
SocketChannel client = serverChannel.accept();
client.configureBlocking(false);
SelectionKey sKey = client.register(selector, SelectionKey.OP_READ);
sKey.attach("[r_cmd]"); // 連線好後讀取客戶端發來的命令
} else if (key.isReadable()) {
// 讀取
SocketChannel channel = (SocketChannel) key.channel();
String attach = key.attachment().toString();
if (attach.equals("[r_cmd]")) {
// 獲取命令
int len = channel.read(buffer);
if (len > 0) {
buffer.flip();
String cmd = "";
CharBuffer charBuffer = decoder.decode(buffer);
cmd = charBuffer.toString();
SelectionKey sKey = channel.register(selector,
SelectionKey.OP_WRITE);
System.out.println("cmd:" + cmd);
if (cmd.trim().equals("list")) {
sKey.attach("[list_file]");
} else {
String[] temp = cmd.split(" ");
if (temp.length >= 2) {
cmd = temp[0];
String filename = temp[1];
if (cmd.equals("get")) {
// 下載
File file = new File(server_path, filename);
if (file.exists()) {
sKey.attach("[get]:" + filename);
} else {
sKey.attach("[no_file]");
}
} else if (cmd.equals("put")) {
// 上傳
sKey.attach("[put]:" + filename);
} else {
// 錯誤命令格式
sKey.attach("[error_command]");
}
}
}
} else {
channel.close();
}
}
buffer.clear();
} else if (key.isWritable()) {
// 寫入
SocketChannel channel = (SocketChannel) key.channel();
String attach = key.attachment().toString();
if (attach.startsWith("[list_file]")) {
channel.write(encoder.encode(CharBuffer.wrap("list files")));
File file = new File(server_path);
String[] filenames = file.list();
String temp = "";
for (String filename : filenames) {
temp += filename + ";";
}
temp = temp.substring(0, temp.length() - 1);
// 寫入所有可下載的檔案
channel.write(ByteBuffer.wrap(temp.getBytes()));
channel.close();
} else if (attach.equals("[no_file]")) {
channel.write(ByteBuffer.wrap("no such file".getBytes()));
channel.close();
} else if (attach.equals("[error_command]")) {
channel.write(ByteBuffer.wrap("error command".getBytes()));
channel.close();
} else if (attach.startsWith("[get]")) {
channel.write(encoder.encode(CharBuffer.wrap("開始下載")));
File file = new File(server_path, attach.split(":")[1]);
DataInputStream dis = new DataInputStream(
new BufferedInputStream(new FileInputStream(file)));
int len = 0;
byte[] buf = new byte[1024];
while ((len = dis.read(buf)) != -1) {
channel.write(ByteBuffer.wrap(buf, 0, len));
}
dis.close();
System.out.println("下載完成");
channel.close();
} else if (attach.startsWith("[put]")) {
channel.write(encoder.encode(CharBuffer.wrap("開始上傳")));
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream(new File(
server_path, attach.split(":")[1]))));
int len = channel.read(buffer);
while (len >= 0) {
if (len != 0) {
buffer.flip();
}
dos.write(buffer.array(), 0, len);
len = channel.read(buffer);
}
dos.close();
channel.close();
System.out.println("上傳完畢");
}
}
}
}
作為伺服器,我們首先在C盤根目錄下新建一個file_center的目錄,該目錄下的的所有檔案可提供下載,上傳的檔案也是儲存在這個目錄下
3、客戶端FileClient的實現程式碼如下
package org.filecenter;
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.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
public class FileClient {
private int ServerPort = 12345;
private String ServerAddress = "127.0.0.1";
private String CMD = "list";
private String local_file = "";
private String server_file = "";
class SocketThread extends Thread {
@Override
public void run() {
try {
File file = new File(local_file);
if (!file.exists() && CMD.equals("put")) {
System.out.println("本地沒有這個檔案,無法上傳!");
return;
}
InetAddress loalhost = InetAddress.getLocalHost();
Socket socket = new Socket(ServerAddress, ServerPort, loalhost,
44);
// 伺服器IP地址 埠號 本機IP 本機埠號
DataInputStream dis = new DataInputStream(socket
.getInputStream());
DataOutputStream dos = new DataOutputStream(socket
.getOutputStream());
// dos.writeUTF(GetOrPut+" "+server_file);//伺服器端如果是do的socket,writeUTF和writeUTF對接
dos.write((CMD + " " + server_file).getBytes());
dos.flush();
// String tempString = dis.writeUTF();
byte[] buf = new byte[1024];
int len = dis.read(buf);
String tempString = new String(buf, 0, len);// 伺服器反饋的資訊
// System.out.println(tempString);
if (tempString.equals("no such file")) {
System.out.println("伺服器沒有這個檔案,無法下載!");
dos.close();
dis.close();
socket.close();
return;
}
if (tempString.startsWith("開始下載")) {
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("開始上傳")) {
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();
}
else if(tempString.equals("list files"))
{
len=dis.read(buf);
String temp=new String(buf,0,len);
String[] strs=temp.split(";");
System.out.println("檔案列表");
for(String str:strs)
{
System.out.println(str);
}
dis.close();
dos.close();
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public boolean checkCommand(String command) {
if (!command.startsWith("put") && !command.startsWith("get") && !command.equals("list")) {
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")) {
CMD = "put";
local_file = tempStrings[tempStrings.length - 2];
server_file = tempStrings[tempStrings.length - 1];
} else if (command.startsWith("get")) {
CMD = "get";
local_file = tempStrings[tempStrings.length - 1];
server_file = tempStrings[tempStrings.length - 2];
}
else
{
CMD = "list";
}
return true;
}
public static void main(String[] args) {
FileClient client = new FileClient();
Scanner sc = new Scanner(System.in);
String commandString = "";
do {
System.out.println("請輸入命令:");
commandString = sc.nextLine();
} while (!client.checkCommand(commandString));
FileClient.SocketThread a = client.new SocketThread();
a.start();
}
}
這裡我們提供了三種命令方式
1)list命令,列出伺服器中所有可供下載的檔案列表
2)get命令,下載伺服器中的檔案。示例:get test.txt d:\test2.txt
3)put命令,上傳檔案至伺服器。示例:put e:\hello.rar hello.rar
4、將上述兩個java檔案進行編譯,然後開啟兩個命令視窗進行測試
四、總結
1、此檔案伺服器相對簡單,有很多可以擴充套件和進行改善的地方。比如可以根據需要對檔案的上傳下載等功能可以開啟獨立的執行緒進行操作
2、在檔案的讀寫方面也可以考慮nio的方式進行
版權宣告:本文為博主原創文章,未經博主允許不得轉載。
相關文章
- 使用Java NIO 和 NIO2實現檔案輸入/輸出Java
- Thinking in Java--使用NIO實現非阻塞Socket通訊ThinkingJava
- Python使用socket_TCP實現小檔案下載PythonTCP
- socket實現簡單傳檔案ftp/scp服務FTP
- Java Socket 之 NIOJava
- epoll+socket實現 socket併發 linux伺服器Linux伺服器
- nginx實現檔案伺服器Nginx伺服器
- Python使用socket的UDP協議實現FTP檔案服務PythonUDP協議FTP
- Java實現FTP跨伺服器檔案操作JavaFTP伺服器
- xftp實現linux伺服器傳輸檔案FTPLinux伺服器
- Java NIO 檔案通道 FileChannel 用法Java
- Java NIO Path介面操作檔案Java
- C# 實現socket通訊程式(伺服器端)C#伺服器
- MySQL socket檔案被刪除MySql
- Java基礎(Socket通訊和NIO)Java
- 在linux下搭建NFS伺服器實現檔案共享LinuxNFS伺服器
- SpringBoot利用Nginx作為檔案伺服器實現檔案上傳和訪問Spring BootNginx伺服器
- socket程式設計實現tcp伺服器_C/C++程式設計TCP伺服器C++
- Netty實現Web SocketNettyWeb
- Tomcat實現Web SocketTomcatWeb
- python3 socket檔案傳輸Python
- Zsh 開發指南(第十七篇 使用 socket 檔案和 TCP 實現程式間通訊)TCP
- C# Socket 檔案傳送傳輸C#
- NIO、BIO、AIO 與 PHP 實現AIPHP
- PHP實現單檔案、多檔案上傳 封裝 物件導向實現檔案上傳PHP封裝物件
- Spring Boot實現Web SocketSpring BootWeb
- socket實現聊天功能(二)
- js實現使用檔案流下載csv檔案JS
- 使用 Azure Blob Stoage 實現一個靜態檔案伺服器伺服器
- 雲伺服器配置php.ini實現PHP檔案上傳伺服器PHP
- php原生socket之IO多路複用以及實現web伺服器PHPWeb伺服器
- 通過配置檔案(.htaccess)實現檔案上傳
- 點選按鈕,實現檔案下載,通過按鈕傳送url,spring後臺實現伺服器端檔案下載。Spring伺服器
- Java中使用新NIO.2讀寫檔案Java
- 《初識TCP》iOS、macOS實現socket client與socket serverTCPiOSMacclientServer
- mac下用scp命令實現本地檔案與伺服器Linux檔案之間的相互傳輸Mac伺服器Linux
- socket 實現的 web 伺服器在 Windows 下的讀寫問題Web伺服器Windows
- ajax實現檔案上傳
- Java實現檔案切割拼接Java