上节实现了简单的EchoServer,但客户端还是Telnet,这节就实现TcpClient吧
客户端的Encoder与Decoder 跟服务端的一样,可以直接拿来使用,但还是做了些调整,
解码代码:
package com.skymr.mina.tcptest;
import java.nio.ByteBuffer;
import java.nio.charset.CharsetDecoder;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
public class MyLineCodecDecoder implements ProtocolDecoder{
private String key = "textKey1234";
protected CharsetDecoder decoder;
public MyLineCodecDecoder(CharsetDecoder decoder){
this.decoder = decoder;
}
@Override
public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)
throws Exception {
ByteBuffer buffer = null;
Object o = session.getAttribute(key);
if(o == null){
buffer = ByteBuffer.allocate(1024);
session.setAttribute(key, buffer);
}
else{
buffer = (ByteBuffer)o;
}
byte last = -1;
while(in.remaining()> 0){
byte cur = in.get();
if(cur==10 && last==13){
writeToDecodeOutput(buffer, out);
}
else{
if(cur != 13){
buffer.put(cur);
}
}
last = cur;
}
}
private void writeToDecodeOutput(ByteBuffer buffer, ProtocolDecoderOutput out){
String text = null;
try{
text = new String(readBuffer(buffer),decoder.charset());
}catch(Exception e){
e.printStackTrace();
text = new String(readBuffer(buffer));
}
LineText lineText = new LineText();
lineText.setText(text);
out.write(lineText);
buffer.clear();
}
public static byte[] readBuffer(ByteBuffer buffer){
buffer.flip();
int len = buffer.limit() - buffer.position();
byte[] ret = new byte[len];
System.arraycopy(buffer.array(), 0, ret, 0, len);
return ret;
}
@Override
public void finishDecode(IoSession session, ProtocolDecoderOutput out)
throws Exception {
session.removeAttribute(key);
}
@Override
public void dispose(IoSession session) throws Exception {
session.removeAttribute(key);
}
}
解码部分的改动较大, 特别是decode方法,改得比较灵活: 才发现telnet客户端与TcpClient的差异:
Telnet发送时是一个字节一个字节地发,而TcpClient是一次性发送整行,为了配合这两个客户端的差异才会这么改动
编码代码:
package com.skymr.mina.tcptest;
import java.nio.charset.CharsetEncoder;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
public class MyLineCodecEncoder implements ProtocolEncoder{
protected CharsetEncoder charset;
public MyLineCodecEncoder(CharsetEncoder charset){
this.charset = charset;
}
@Override
public void encode(IoSession session, Object message,
ProtocolEncoderOutput out) throws Exception {
LineText text = (LineText)message;
IoBuffer buffer = IoBuffer.allocate(1024, false);
buffer.putString(text.getText(), charset);
buffer.put((byte)13);
buffer.put((byte)10);
buffer.flip();
out.write(buffer);
}
@Override
public void dispose(IoSession session) throws Exception {
}
}
IoHandler实现:
package com.skymr.mina.tcptest;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
public class ClientHandler extends IoHandlerAdapter{
@Override
public void sessionOpened(IoSession session) throws Exception {
super.sessionOpened(session);
System.out.println("连接服务器成功");
TcpClient.isConnected = true;
}
@Override
public void sessionClosed(IoSession session) throws Exception {
}
@Override
public void messageReceived(IoSession session, Object message)
throws Exception {
System.out.println("收到消息:"+message);
}
@Override
public void messageSent(IoSession session, Object message) throws Exception {
System.out.println("我发送了:"+message);
}
}
IoHandler的主要工作:
1.当连接服务器成功时,通知主线程
2.打印接收的消息和发送的消息
主线程TcpClient实现:
package com.skymr.mina.tcptest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.SocketConnector;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
public class TcpClient {
public static boolean isConnected = false;
private IoSession session;
public void startConnect() throws Exception{
SocketConnector connector = new NioSocketConnector();
connector.setConnectTimeoutMillis(30 * 1000);
connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MyLineCodecFactory(Charset.forName("UTF-8"))));
// connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("GBK"))));
connector.setHandler(new ClientHandler());
connector.connect(new InetSocketAddress("127.0.0.1",8000));
while(!isConnected){
Thread.sleep(50);
}
session = connector.getManagedSessions().values().iterator().next();
work();
}
public void work() throws Exception{
java.io.BufferedReader is = new BufferedReader(new InputStreamReader(System.in,"GBK"));
while(true){
String line = is.readLine().trim();
line = new String(line.getBytes("UTF-8"),"UTF-8");
System.out.println("将要发送:"+line);
LineText lt = new LineText();
lt.setText(line);
session.write(lt);
}
}
public static void main(String[] args) throws Exception{
new TcpClient().startConnect();
}
}
编写思路:
1.连接服务器
2.再没有接收到连接成功的通知时一直等待
3.接到通知后取得IoSession,不解的是IoSession是个Map,客户端应该就是多个IoSession,可以连接多个服务器或一个服务器的多的端口,但怎么得到想要的Session呢,稍后再说吧,这里就一个
4.循环读取System.in输入,取得一行字符串后使用session发送出去