採用Java nio 實現的一個簡單的伺服器
阿新 • • 發佈:2019-02-20
伺服器程式碼:
package server.nio;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Server {
private static Logger log = LoggerFactory.getLogger(Server.class);
private static final Integer Default_Port = 9527;
private static final Integer Default_Timeout = 2000;
private static ConcurrentLinkedQueue<SocketChannel> workeQueue = new ConcurrentLinkedQueue<>();
private static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private Integer port;
private Integer timeout;
public Server() {
this(Default_Port,Default_Timeout);
}
public Server(Integer port){
this(port,Default_Timeout);
}
public Server(Integer port,Integer timeout) throws IllegalArgumentException{
if(port == null) port = Default_Port;
if(timeout == null) timeout = Default_Timeout;
this.port = port;
this.timeout = timeout;
long st = System.currentTimeMillis();
try {
log.debug("伺服器開始啟動-->>");
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(Default_Port));
//啟動監聽workQueue佇列的執行緒
log.debug("任務佇列監聽執行緒啟動開始--->>");
startListener();
log.debug("任務佇列監聽執行緒啟動完成---<<");
//啟動伺服器
long et = System.currentTimeMillis();
log.debug("伺服器啟動完成:--<<");
log.debug("啟動耗時:"+(et-st));
start(serverSocketChannel);
} catch (IOException e) {
e.printStackTrace();
}
}
private void startListener() {
Thread t = new Thread(new Runnable() {
public void run() {
while(true){
final SocketChannel sc = workeQueue.poll();
try {
if(sc != null){
if(log.isDebugEnabled()){
log.debug("開始處理客戶端請求:"+sc);
}
executorService.submit(new Callable<Response>() {
@Override
public Response call() throws Exception {
//建立位元組緩衝區
ByteBuffer bytes = ByteBuffer.allocate(64);
ArrayList<Byte> list = new ArrayList<>();
try {
//從通道中讀取位元組到緩衝區
while(sc.read(bytes) != -1){
bytes.flip();
//判斷位元組緩衝區是否已滿
while(bytes.remaining() > 0){
//將緩衝區中的位元組陣列放到list中
//位元組緩衝區滿,但通道中的資料可能還沒有讀取完,因此需要將位元組先儲存起來
//最後統一處理(如果不這樣做,可能會出現意外的結果:如最後轉化成字串,如不這樣做,會因位元組不完整而亂碼)
list.add(bytes.get());
//清空位元組緩衝區,以接收通道中剩餘的資料
}
bytes.clear();
}
byte[] temp = new byte[list.size()];
//將list中的資料放到temp位元組陣列中
for(int i=0;i<list.size();i++){
temp[i] = list.get(i);
}
//反序列化開始
try{
ByteArrayInputStream bin = new ByteArrayInputStream(temp);
ObjectInputStream oin = new ObjectInputStream(bin);
Object obj = oin.readObject();
// Object obj = ObjectUtil.deserializeJdk(temp);
System.out.println(obj);
}catch(Exception e){
log.debug("反序列化物件失敗:請檢查是否實現Serializable介面:");
e.printStackTrace();
}
//反序列化開始結束
} catch (Exception e) {
e.printStackTrace();
}
//處理響應物件
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// ObjectOutputStream oos = new ObjectOutputStream(baos);
// //將返回的物件序列化
// oos.writeObject(response);
// //將byte陣列轉換成通道可以接受的ByteBuffer型別
// ByteBuffer writeBytes = ByteBuffer.wrap(baos.toByteArray());
// sc.write(writeBytes);
// sc.close();
return null;
}
});
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(sc != null){
log.debug("客戶端連結:"+sc+":關閉");
}else{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
log.debug("等待連線....");
}
}
}
}
});
t.start();
}
private void start(ServerSocketChannel serverSocketChannel) {
while(true){
try {
SocketChannel socketChannel = serverSocketChannel.accept();
if(socketChannel != null){
if(log.isDebugEnabled()){
log.debug("客戶端連線放進任務佇列中:"+serverSocketChannel);
}
workeQueue.add(socketChannel);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 響應物件
* @author Administrator
*
*/
public class Response implements Serializable {
// private Integer status;
//
// private Integer code;
//
// private String desc;
//
// private Object data;
//
//
// public String getDesc() {
// return desc;
// }
//
// public void setDesc(String desc) {
// this.desc = desc;
// }
//
// public Integer getStatus() {
// return status;
// }
//
// public void setStatus(Integer status) {
// this.status = status;
// }
//
// public Integer getCode() {
// return code;
// }
//
// public void setCode(Integer code) {
// this.code = code;
// }
//
// public Object getData() {
// return data;
// }
//
// public void setData(Object data) {
// this.data = data;
// }
//
}
}
客戶端類:
package client.nio;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class Client {
public static void main(String[] args) throws Exception {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress(InetAddress.getLocalHost(), 9527));
// String test1 = "hello world";
String test = "Java IO的各種流是阻塞的。這意味著,當一個執行緒呼叫read() 或 write()時,該執行緒被阻塞,直到有一些資料被讀取,或資料完全寫入。該執行緒在此期間不能再幹任何事情了。 Java NIO的非阻塞模式,使一個執行緒從某通道傳送請求讀取資料,但是它僅能得到目前可用的資料,如果目前沒有資料可用時,就什麼都不會獲取。而不是保持執行緒阻塞,所以直至資料變的可以讀取之前,該執行緒可以繼續做其他的事情。 非阻塞寫也是如此。一個執行緒請求寫入一些資料到某通道,但不需要等待它完全寫入,這個執行緒同時可以去做別的事情。 執行緒通常將非阻塞IO的空閒時間用於在其它通道上執行IO操作,所以一個單獨的執行緒現在可以管理多個輸入和輸出通道(channel)。";
//序列化
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(bout);
oout.writeObject(test);
//建立位元組緩衝區併發送給服務端
ByteBuffer buffer = ByteBuffer.wrap(bout.toByteArray());
socketChannel.write(buffer);
socketChannel.close();
}
}
測試:
啟動伺服器:
package server.nio;
public class Main {
public static void main(String[] args) {
Server server = new Server();
}
}
![這裡寫圖片描述](https://img-blog.csdn.net/20161208180036256?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjUwMTQ1OTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
啟動客戶端併發送訊息:
![這裡寫圖片描述](https://img-blog.csdn.net/20161208175459426?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjUwMTQ1OTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)