Mina实现点对点通信

文章目录


最近在和老师做一个项目,要用到mina,今天做了个简单的多客户端点对点通信来熟悉一下这个框架,也遇到了几个问题,后面会说。
关于原理部分,我觉得这篇可以看看: https://www.cnblogs.com/duanxz/p/5143227.html。

开始吧。

服务器端

主要就是一个Handler和一个主类,这里放在一起了,注释挺详细的:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class Server {
	private static final int PORT = 9123;  
	public static Map<String, IoSession> usersMap;
    public static void main(String[] args){  
        // 监听连接的对象  
        IoAcceptor acceptor = new NioSocketAcceptor();          
        // 配置过滤器  
        // logger过滤器会输出所有的信息,例如新创建的会话、消息的接收、消息的发送、会话的关闭  
        // codec过滤器会转换二进制活协议规定的数据为消息对象,这里是处理基于文本的消息  
        acceptor.getFilterChain().addLast("logger", new LoggingFilter());  
        acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(  
                new TextLineCodecFactory(Charset.forName("UTF-8"))));  
          
        //   
        acceptor.setHandler(new ServerHandler());          
        // 设置输入缓冲区的大小和会话的IDLE熟悉  
        acceptor.getSessionConfig().setReadBufferSize(2048);  
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);          
        try {  
            acceptor.bind(new InetSocketAddress(PORT));//绑定端口,启动监听  
            usersMap=new HashMap<String, IoSession>();
            System.out.println("HelloServer started on port " + PORT);  
        } catch (IOException e) {  
            e.printStackTrace();  
        }      
    }       
}

class ServerHandler extends IoHandlerAdapter {  
    //当有异常发生时触发  
    @Override  
    public void exceptionCaught(IoSession session, Throwable cause)  
            throws Exception {  
        cause.printStackTrace();  
        session.close();  
    }  
    //收到来自客户端的消息  
    @Override  
    public void messageReceived(IoSession session, Object message) throws Exception {  
        //来自客户端信息 from--to--message
        String str = message.toString();  
        String info[] = str.split("--"); 
        
        /*System.out.println("Message From " + info[0]);      
        System.out.println("Message To " + info[1]);*/
        	
        if(info[1].equals("0")){
        	Server.usersMap.put(info[0], session);
        	System.out.println("用户【"+info[0]+"】已登录");
        }
        else if(Server.usersMap.get(info[1])==null){
        	session.write("server : 对方未上线!");
        }
        else {
        	Server.usersMap.get(info[1]).write(info[0]+" : "+info[2]);
		}
        //接受客户端字符串"quit"关闭当前会话连接  
        if(str.trim().equalsIgnoreCase("quit")){  
            session.close();
        }  
    }
    //连接被关闭时触发  
    @Override  
	public void sessionClosed(IoSession session) throws Exception {  
	    System.out.println("session closed from " + session.getRemoteAddress().toString());  
	    Server.usersMap.remove((String) session.getAttribute("clientName"));
	}  
    //有新连接时触发  
	@Override  
	public void sessionOpened(IoSession session) throws Exception {  
		System.out.println("Server sessionOpened "+System.currentTimeMillis());
		System.out.println("session open for " + session.getRemoteAddress()); 
	}   
    
	@Override
    public void sessionCreated(IoSession session) throws Exception {
        System.out.println("Server sessionCreated "+System.currentTimeMillis());
    }
	
    @Override
    public void messageSent(IoSession session, Object message) throws Exception{
	    System.out.println("转发消息成功");
    }
}  

客户端

这里为了简便起见,规定了发送格式为“发送者–接受者–信息”


import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.Scanner;

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.LineDelimiter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

public class Client {  
    public static void main(String[] args){  
    	IoConnector connector = new NioSocketConnector();
        connector.setConnectTimeoutMillis(30000);
        connector.getFilterChain().addLast("logger", new LoggingFilter());  
        connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(  
                new TextLineCodecFactory(Charset.forName("UTF-8"))));  
        
        String name = "Mike";
        connector.setHandler(new ClientHandler(name));    
           
        ConnectFuture future = connector.connect(new InetSocketAddress("localhost", 9123));
        
        // 等待是否连接成功,相当于是转异步执行为同步执行。    
        future.awaitUninterruptibly();
        
        // 连接成功后获取会话对象。如果没有上面的等待,由于connect()方法是异步的,session可能会无法获取。    
        IoSession session = future.getSession(); 
        
        session.write(name+"--0--"+"login");//用于登录
        System.out.println("Client session in main"+System.currentTimeMillis());
        while(true){
        	Scanner scanner = new Scanner(System.in);
        	session.write(scanner.nextLine());
        }
    }
}
class ClientHandler extends IoHandlerAdapter {  
    private final String name;//用户名,用于标识这个用户,当然这里很粗略,并没有验证唯一性。

    public ClientHandler(String name) {
        this.name = name;
    }

    @Override
    public void sessionCreated(IoSession session) throws Exception {
        System.out.println("Client "+name+" sessionCreated "+System.currentTimeMillis());
    }
    
    @Override  
    public void messageReceived(IoSession session, Object message) throws Exception {  
    	String str = message.toString();        
        System.out.println(str);            
    }
    
    @Override
    public void sessionOpened(IoSession session) throws Exception {  
		System.out.println("Client "+name+" sessionOpened "+System.currentTimeMillis());
	}   
}

关于客户端这里,我有几个坑踩得挺久的:

  1. 过滤链中的编解码器要和服务器端的编解码一致,不然有可能会出现客户端收不到服务器端信息的情况。
  2. 我原本的思路是:客户端sessionCreated中放入属性name,然后服务器端sessionOpened中取出session中的name和session放到map中来表示用户已经登录。然后发现服务器端总是取不到这个name属性,后来通过看各个方法的执行时间戳,发现是服务器端的created和opened先执行,再执行客户端的created和opened,所以才换成在建立连接后先发个特殊的消息来表示登录。
    这里写图片描述

聊天界面:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值