## 創建阻塞的 EchoClient 客戶程式一般不需要同時建立與伺服器的多個連接,因此用一個線程,按照阻塞模式運行就能滿足需求 ```java public class EchoClient { private SocketChannel socketChannel = null; public ...
創建阻塞的 EchoClient
客戶程式一般不需要同時建立與伺服器的多個連接,因此用一個線程,按照阻塞模式運行就能滿足需求
public class EchoClient {
private SocketChannel socketChannel = null;
public EchoClient() throws IOException {
socketChannel = SocketChannel.open();
InetAddress ia = InetAddress,getLocalHost();
InetSocketAddress isa = new InetSocketAddress(ia,8000);
socketChannel.connect(isa); //連接伺服器
}
public static void main(String args[])throws IOException {
new EchoClient().talk();
}
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(socketOut,true);
}
private BufferedReader getReader(Socket socket) throws IOException {
InputStream socketIn = socket.getInputStream();
return new BufferedReader(new InputStreamReader(socketIn));
}
public void talk() throws IOException {
try {
BufferedReader br = getReader(socketChannel.socket());
PrintWriter pw = getWriter(socketChannel.socket());
BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
String msq = null;
while((msg = localReader.readLine()) != null) {
pw.println(msg);
System.out.println(br.readLine());
if(msq.equals("bye")) {
break;
}
}
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
socketChannel.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
}
創建非阻塞的 EchoClient
對於客戶與伺服器之間的通信,按照它們收發數據的協調程度來區分,可分為同步通信和非同步通信
同步通信指甲方向乙方發送了一批數據後,必須等接收到了乙方的響應數據後,再發送下一批數據。同步通信要求一個 IO 操作完成之後,才能完成下一個 IO 操作,用阻塞模式更容易實現
非同步通信指發送數據和接收數據的操作互不幹擾,各自獨立進行。非同步通信允許發送數據和接收數據的操作各自獨立進行,用非阻塞模式更容易實現
值得註意的是,通信的兩端並不要求都採用同樣的通信方式,當一方採用同步通信時,另一方可以採用非同步通信
public class EchoClient {
private SocketChannel socketChannel = null;
private ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
private ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
private Charset charset = Charset.forName("GBK");
private Selector selector;
public EchoClient() throws IOException {
socketChannel = SocketChannel.open();
InetAddress ia = InetAddress.getLocalHost();
InetSocketAddress isa = new InetSocketAddress(ia, 8000);
socketChannel.connect(isa); //採用阻塞模式連接伺服器
socketChannel.configureBlocking(false); //設置為非阻塞模式
selector = Selector.open();
}
public static void main(String args[]) throws IOException {
final EchoClient client = new EchoClient();
Thread receiver=new Thread() {
public void run() {
client.receiveFromUser(); //接收用戶向控制台輸入的數據
}
};
receiver.start();
client.talk();
}
/** 接收用戶從控制台輸入的數據,放到sendBuffer中 */
public void receiveFromUser() {
try {
BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
String msg = null;
while((msg = localReader.readLine()) != null) {
synchronized(sendBuffer) {
sendBuffer.put(encode(msg + "\r\n"));
}
if (msg.equals("bye")) {
break;
}
}
} catch(IOException e) {
e.printStackTrace();
}
}
//接收和發送數據
public void talk() throws IOException {
socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
while (selector.select() > 0 ) {
Set readyKeys = selector.selectedKeys();
Iterator it = readyKeys.iterator();
while (it.hasNext()) {
SelectionKey key = null;
try {
key = (SelectionKey) it.next();
it.remove();
if (key.isReadable()) {
receive(key);
}
if (key.isWritable()) {
send(key);
}
} catch(IOException e) {
e.printStackTrace();
try {
if(key != null) {
key.cancel();
key.channel().close() ;
}
} catch(Exception ex) {
e.printStackTrace();
}
}
}
}
}
public void send(SelectionKey key) throws IOException {
//發送sendBuffer的數據
SocketChannel socketChannel = (SocketChannel)key.channel();
synchronized(sendBuffer) {
sendBuffer.flip(); //把極限設為位置,把位置設為0
socketChannel.write(sendBuffer); //發送數據
sendBuffer.compact(); //刪除已經發送的數據
}
}
public void receive(SelectionKey key) throws IOException {
//接收EchoServer發送的數據,把它放到receiveBuffer
//如果receiveBuffer有一行數據,就列印這行數據,然後把它從receiveBuffer刪除
SocketChannel socketChannel = (SocketChannel) key.channel();
socketChannel.read(receiveBuffer):
receiveBuffer.flip();
String receiveData = decode (receiveBuffer);
if(receiveData.indexOf("\n") == -1) return;
String outputData = receiveData.substring(0, receiveData.indexOf("\n") + 1):
System.out.print(outputData);
if(outputData.equals("echo:bye\r\n")) {
key.cancel():
socketChannel.close();
selector.close();
System.exit(0);
}
ByteBuffer temp = encode(outputData);
receiveBuffer.position(temp.limit());
receiveBuffer.compact(): //刪除已經列印的數據
}
//解碼
public String decode(ByteBuffer buffer) {
CharBuffer charBuffer= charset.decode(buffer);
return charBuffer.toString();
}
//編碼
public ByteBuffer encode(String str) {
return charset.encode(str);
}
}