您的位置:首页 > 编程语言 > Java开发

Mina2.x与Spring2.5.6集成

2012-10-31 16:46 232 查看
This is a Web Project,本文代码支持绑定多个端口....



首先是Web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext*.xml</param-value>
	</context-param>
	
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
</web-app>


下面是用于模拟短信协议的实体类

package com.jadyer.model;

/**
 * 模拟短信协议内容的对象
 * @see M sip:wap.fetion.com.cn SIP-C/2.0 //状态行,一般表示协议的名字、版本号等
 * @see S: 1580101xxxx                    //短信的发送号码
 * @see R: 1880202xxxx                    //短信的接收号码
 * @see L: 21                             //短信的字节数
 * @see 你好!!Hello World!!               //短信的内容
 * @see 上面每行的末尾使用ASCII的10(\n)作为换行符
 */
public class SmsObject {
	private String sender;   //短信发送者
	private String receiver; //短信接收者
	private String message;  //短信内容
	/*三个属性的getter和setter略*/
}


下面是服务端的消息处理器ServerHandler.java

package com.jadyer.handler;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;

import com.jadyer.model.SmsObject;

public class ServerHandler extends IoHandlerAdapter {
	@Override
	public void messageReceived(IoSession session, Object message) throws Exception {
		SmsObject sms = (SmsObject)message;
		System.out.println("The message received from Client is [" + sms.getMessage() + "]");
		SmsObject respSms = new SmsObject();
		respSms.setSender("13716700602");
		respSms.setReceiver("15025302990");
		respSms.setMessage("ok,收到消息了小伙儿....");
		session.write(respSms);
	}
	@Override
	public void sessionOpened(IoSession session) throws Exception{
		System.out.println("InComing Client:" + session.getRemoteAddress());
	}
}


下面是我们自定义的编码器CmccSipcEncoder.java

package com.jadyer.codec;

import java.nio.charset.Charset;
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.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;

import com.jadyer.model.SmsObject;

public class CmccSipcEncoder extends ProtocolEncoderAdapter {
	private final String charset;
	private final CharsetEncoder charsetEncoder;
	public CmccSipcEncoder(String charset){
		this.charset = charset;
		this.charsetEncoder = Charset.forName(charset).newEncoder();
	}
	@Override
	public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
		SmsObject sms = (SmsObject)message;
		IoBuffer buffer = IoBuffer.allocate(100).setAutoExpand(true);
		String statusLine = "M sip:wap.fetion.com.cn SIP-C/2.0";
		String sender = sms.getSender();
		String receiver = sms.getReceiver();
		String smsContent = sms.getMessage();
		buffer.putString(statusLine+'\n', charsetEncoder);
		buffer.putString("S: "+sender+'\n', charsetEncoder);
		buffer.putString("R: "+receiver+'\n', charsetEncoder);
		//使用String类与Byte[]类型之间的转换方法获得转为字节流后的字节数
		buffer.putString("L: "+smsContent.getBytes(charset).length+'\n', charsetEncoder);
		buffer.putString(smsContent, charsetEncoder);
		buffer.flip();
		out.write(buffer);
	}
}


下面是我们自定义的解码器CmccSipcDecoder.java

package com.jadyer.codec;

import java.nio.charset.Charset;
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.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;

import com.jadyer.model.SmsObject;

public class CmccSipcDecoder extends CumulativeProtocolDecoder {
	private final CharsetDecoder charsetDecoder;
	public CmccSipcDecoder(String charset){
		this.charsetDecoder = Charset.forName(charset).newDecoder();
	}
	@Override
	protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
		IoBuffer buffer = IoBuffer.allocate(100).setAutoExpand(true);
		int i = 1;          //记录解析到了短信协议中的哪一行(\n)
		int matchCount = 0; //记录在当前行中读取到了哪一个字节
		String statusLine="", sender="", receiver="", length="", sms="";
		while(in.hasRemaining()){
			byte b = in.get();
			buffer.put(b);
			if(10==b && 5>i){ //10==b表示换行:该短信协议解码器使用\n(ASCII的10字符)作为分解点
				matchCount++;
				if(1 == i){
					buffer.flip(); //limit=position,position=0
					statusLine = buffer.getString(matchCount, charsetDecoder);
					statusLine = statusLine.substring(0, statusLine.length()-1); //移除本行的最后一个换行符
					matchCount = 0; //本行读取完毕,所以让matchCount=0
					buffer.clear();
				}
				if(2 == i){
					buffer.flip();
					sender = buffer.getString(matchCount, charsetDecoder);
					sender = sender.substring(0, sender.length()-1);
					matchCount = 0;
					buffer.clear();
				}
				if(3 == i){
					buffer.flip();
					receiver = buffer.getString(matchCount, charsetDecoder);
					receiver = receiver.substring(0, receiver.length()-1);
					matchCount = 0;
					buffer.clear();
				}
				if(4 == i){
					buffer.flip();
					length = buffer.getString(matchCount, charsetDecoder);
					length = length.substring(0, length.length()-1);
					matchCount = 0;
					buffer.clear();
				}
				i++;
			}else if(5 == i){
				matchCount++;
				if(Long.parseLong(length.split(": ")[1]) == matchCount){
					buffer.flip();
					sms = buffer.getString(matchCount, charsetDecoder);
					i++;
					break;
				}
			}else{
				matchCount++;
			}
		}
		SmsObject smsObject = new SmsObject();
		smsObject.setSender(sender.split(": ")[1]);
		smsObject.setReceiver(receiver.split(": ")[1]);
		smsObject.setMessage(sms);
		out.write(smsObject);
		return false; //告诉Mina:本次数据已全部读取完毕,故返回false
	}
}


下面是位于src下的log4j.properties

log4j.rootLogger=DEBUG,CONSOLE

#use ConsoleAppender for ConsoleOut
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=DEBUG
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%d{yyyyMMdd HH:mm:ss SSS}][%t][%C{1}.%M]%m%n


下面是核心的配置文件applicationContext-mina.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
					 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> 						
	<!-- 构造过滤器链 -->
	<bean id="ioFilterChainBuilder" class="org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder">
		<property name="filters">
			<map>
				<!-- 日志过滤器 -->
				<entry key="logging">
					<bean class="org.apache.mina.filter.logging.LoggingFilter"/>
				</entry>
				<!-- 协议编解码过滤器 -->
				<entry key="codec">
					<bean class="org.apache.mina.filter.codec.ProtocolCodecFilter">
						<constructor-arg index="0">
							<bean class="com.jadyer.codec.CmccSipcEncoder">
								<constructor-arg value="UTF-8"/>
							</bean>
						</constructor-arg>
						<constructor-arg index="1">
							<bean class="com.jadyer.codec.CmccSipcDecoder">
								<constructor-arg value="UTF-8"/>
							</bean>
						</constructor-arg>
					</bean>
				</entry>
			</map>
		</property>
	</bean>
	
	<!-- 构造Server端 -->
	<bean id="myServer" class="com.jadyer.MinaStartup" init-method="bind">
		<property name="reuseAddress" value="true"/>
		<property name="writeTimeout" value="10000"/>
		<property name="bothIdleTime" value="20"/>
		<property name="filterChainBuilder" ref="ioFilterChainBuilder"/>
		<property name="handler">
			<bean class="com.jadyer.handler.ServerHandler"/>
		</property>
		<property name="socketAddresses">
			<list>
				<bean class="java.net.InetSocketAddress">
					<constructor-arg index="0" value="192.168.1.2"/>
					<constructor-arg index="1" value="15566"/>
				</bean>
				<bean class="java.net.InetSocketAddress">
					<constructor-arg index="0" value="192.168.1.2"/>
					<constructor-arg index="1" value="15569"/>
				</bean>
			</list>
		</property>
	</bean>
	
	
	<!-- 构造Server端 -->
	<!-- reuseAddress:该属性用于指定端口是否可重用,详细介绍请参考java.net.ServerSocket.setReuseAddress()方法 -->
	<!--              说白了就是,如果有Client正在此端口上wait,若这时重启Server,可能导致重启失败or无法bing此port -->
	<!-- defaultLocalAddress:该属性可以传入java.net.InetSocketAddress(就好像我现在写的这样),也可以直接传字符串 -->
	<!--                     如果传入字符串,可以写成"192.168.1.2:15566"或者冒号端口":15566"或者直接端口"15566" -->
	<!--                     当写入字符串时,则需明确指定<bean class="org.springframework......CustomEditorConfigurer"> -->
	<!-- 这是因为NioSocketAcceptor需要的是InetSocketAddress对象,于是就需要一个编辑器将注入的字符串构造为InetSocketAddress对象 -->
	<!-- Mina2.x自带的org.apache.mina.integration.beans包中提供了很多的属性编辑器,它用来实现与所有的DI框架整合而不仅限于Spring -->
	<!-- 
	<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
		<property name="customEditors">
			<map>
				<entry key="java.net.SocketAddress">
					<bean class="org.apache.mina.integration.beans.InetSocketAddressEditor"/>
				</entry>
			</map>
		</property>
	</bean>
	 -->
	<!-- 下面这种写法,只能绑定一个端口 -->
	<!-- 如果想自由的,控制Mina2.x服务的启停,包括绑定多个端口等,那么使用上面的com.jadyer.MinaStartup显然是一种好办法 -->
	<!-- 用坦克<captmjc@gmail.com>的话说就是:刚才那招虽然很傻,但是必杀 -->
	<!-- 
	<bean id="myServer" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor" init-method="bind" destroy-method="unbind">
		<property name="reuseAddress" value="true"/>
		<property name="filterChainBuilder" ref="ioFilterChainBuilder"/>
		<property name="handler">
			<bean class="com.jadyer.handler.ServerHandler"/>
		</property>
		<property name="defaultLocalAddress">
			<bean class="java.net.InetSocketAddress">
				<constructor-arg index="0" value="192.168.1.2"/>
				<constructor-arg index="1" value="15566"/>
			</bean>
		</property>
	</bean>
	 -->
</beans>


下面是我们自定义的用于启动Mina服务的MinaStartup.java

package com.jadyer;

import java.io.IOException;
import java.net.SocketAddress;
import java.util.List;

import org.apache.mina.core.filterchain.IoFilterChainBuilder;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

/**
 * 用于启动Mina2.x服务
 * @create Oct 31, 2012 4:38:54 PM
 * @author 玄玉<http://blog.csdn/net/jadyer>
 */
public class MinaStartup {
	private IoHandler handler;                       //处理器
	private List<SocketAddress> socketAddresses;     //监听地址
	private IoFilterChainBuilder filterChainBuilder; //过滤器链
	private int bothIdleTime;                        //双向发呆时间
	private int writeTimeout;                        //写操作超时时间
	private boolean reuseAddress;                    //监听的端口是否可重用
	
	public final void bind() throws IOException {
		NioSocketAcceptor acceptor = new NioSocketAcceptor();
		acceptor.setHandler(this.handler);
		acceptor.setReuseAddress(this.reuseAddress);
		acceptor.setFilterChainBuilder(this.filterChainBuilder);
		acceptor.getSessionConfig().setWriteTimeout(this.writeTimeout);
		acceptor.getSessionConfig().setBothIdleTime(this.bothIdleTime);
		if(this.socketAddresses==null || this.socketAddresses.size()<1){
			throw new RuntimeException("监听SocketAddress数不得小于1");
		}
		acceptor.bind(this.socketAddresses);
		if(acceptor.isActive()){
			System.out.println("写 超 时: " + this.writeTimeout + "ms");
			System.out.println("发呆配置: Both Idle " + this.bothIdleTime + "s");
			System.out.println("端口重用: " + this.reuseAddress);
			System.out.println("服务端初始化完成......");
			System.out.println("服务已启动....开始监听...." + acceptor.getLocalAddresses());
		}else{
			System.out.println("服务端初始化失败......");
		}
	}
	
	public void setHandler(IoHandler handler) {
		this.handler = handler;
	}
	public void setSocketAddresses(List<SocketAddress> socketAddresses) {
		this.socketAddresses = socketAddresses;
	}
	public void setFilterChainBuilder(IoFilterChainBuilder filterChainBuilder) {
		this.filterChainBuilder = filterChainBuilder;
	}
	public void setBothIdleTime(int bothIdleTime) {
		this.bothIdleTime = bothIdleTime;
	}
	public void setWriteTimeout(int writeTimeout) {
		this.writeTimeout = writeTimeout;
	}
	public void setReuseAddress(boolean reuseAddress) {
		this.reuseAddress = reuseAddress;
	}
}


至此为止,Mina2.x就与Spring2.5.6集成完了

下面,就可以把该Web应用发布到Tomcat上了

启动Tomcat时,Mina2.x服务也就会被自动启动

如果想手工启动Mina2.x服务,可以使用下面这个MainApp.java

package com.jadyer;

import org.springframework.context.support.FileSystemXmlApplicationContext;

/**
 * 读取Spring的xml文件并启动应用
 * @create Oct 30, 2012 11:41:27 AM
 * @author 玄玉<http://blog.csdn/net/jadyer>
 */
public class MainApp {
	public static void main(String[] args) {
		new FileSystemXmlApplicationContext("classpath:applicationContext*.xml");
	}
}


最后再附上一个测试类MyClient.java

package com.jadyer.client;

import java.net.InetSocketAddress;

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.transport.socket.nio.NioSocketConnector;

import com.jadyer.codec.CmccSipcDecoder;
import com.jadyer.codec.CmccSipcEncoder;
import com.jadyer.model.SmsObject;

public class MyClient {
	public static void main(String[] args) {
		IoConnector connector = new NioSocketConnector();
		connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new CmccSipcEncoder("UTF-8"), new CmccSipcDecoder("UTF-8")));
		connector.setHandler(new ClientHandler());
		//connector.connect(new InetSocketAddress("192.168.1.2", 15566));
		connector.connect(new InetSocketAddress("192.168.1.2", 15569));
	}
	
	private static class ClientHandler extends IoHandlerAdapter {
		@Override
		public void sessionOpened(IoSession session) throws Exception {
			SmsObject sms = new SmsObject();
			sms.setSender("15025302990");
			sms.setReceiver("13716700602");
			sms.setMessage("Hi Jadyer,这是我用Mina2.x发给你的消息....");
			session.write(sms);
		}
		@Override
		public void messageReceived(IoSession session, Object message) throws Exception {
			System.out.println("The message received from Server is [" + ((SmsObject)message).getMessage() + "]");
			session.close(false);
			session.getService().dispose();
		}
	}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: