SpringMVC4.x+Mina+WebSocket+Hibernate4.x实现高并发网页及时通讯框架!(H5+BaiduMap-JSApi)

本文介绍了一种将WebSocket与旧项目的Socket通讯方式整合的方法,实现了地图应用与现有系统的无缝对接。利用Mina处理Socket通讯,Spring管理依赖,以及WebSocket进行前端消息传递。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

应用环境:

最近研究一个项目,一个地图应用,基于百度地图JS-API开发的。

需要和现有的旧项目对接,旧项目也是公司的,但是是十年左右的技术架构,原来受到技术和硬件的限制,采用Socket为主要的通讯方式。并且由于公司人力资源有限不可能对这个旧项目大刀阔斧的改动。所以博主建议的采用webservice为主要的通讯方式啥的,全是扯淡,不能实际实现。

然后博主想过用MQ,一则,没怎么用到过项目里。二则,还是上面说的,没人给你对接,跨不过Socket这个鸿沟。后来博主心生一计,如果我把后台Socket嵌入到项目里,并且能和公司的项目无缝融合,把Socket发过来的协议内容处理后转化成JSON,交给前台用websocket做消息处理不就行了。

这里可能有同学觉得信息量好大。又是Socket又是WebSocket,然后肯定还要考虑持久化,IOC就不用说了。所以在这里博主选了几个需要用的东西:

Spring

SpringMVC

Hibernate(因为不牵扯到复杂查询,所以Mybatis没优势,所以没用。H在封装完BaseDao和BaseServiceImpl后,基本上是不用写任何CRUD的实现的)

Mina(Apache的高并发Socekt通讯框架。)具体:http://mina.apache.org

前端环境是WebSocket+H5、reconnecting-websocket.js(完成websocket的断线重连)

应用环境:Chrome、FF、IE11,可内迁到IOS、安卓,博主已实测。

不多废话,上POM。这也是一个Beta版,可能在运行的时候缺包。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>map</groupId>
    <artifactId>map</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>map</name>
    <description/>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <scope.project>compile</scope.project>
        <scope.servlet>provided</scope.servlet>
        <scope.test>test</scope.test>
        <spring.version>4.3.5.RELEASE</spring.version>
        <hibernate.version>4.3.11.Final</hibernate.version>
        <mina.version>2.0.16</mina.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>${scope.servlet}</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
            <scope>${scope.servlet}</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>${scope.test}</scope>
        </dependency>
        <!-- 		org\springframework -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-instrument</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc-portlet</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>${spring.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.3</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
            <scope>${scope.project}</scope>
        </dependency>

        <dependency>
            <groupId>com.belerweb</groupId>
            <artifactId>pinyin4j</artifactId>
            <version>2.5.0</version>
        </dependency>

        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.9.7</version>
        </dependency>

        <dependency>
            <groupId>com.metaparadigm</groupId>
            <artifactId>json-rpc</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.3.1</version>
        </dependency>

        <dependency>
            <groupId>com.hynnet</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.4</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.8.6</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.8.6</version>
        </dependency>

        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.9.13</version>
        </dependency>

        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

        <dependency>
            <groupId>ognl</groupId>
            <artifactId>ognl</artifactId>
            <version>3.1.11</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
            <exclusions>
                <exclusion>
                    <groupId>com.sun.jmx</groupId>
                    <artifactId>jmxri</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jdmk</groupId>
                    <artifactId>jmxtools</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.jms</groupId>
                    <artifactId>jms</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.2</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.2</version>
        </dependency>

        <dependency>
            <groupId>org.apache.mina</groupId>
            <artifactId>mina-core</artifactId>
            <version>2.0.16</version>
        </dependency>

        <dependency>
            <groupId>org.apache.mina</groupId>
            <artifactId>mina-integration-beans</artifactId>
            <version>2.0.16</version>
        </dependency>

        <dependency>
            <groupId>org.apache.xbean</groupId>
            <artifactId>xbean-spring</artifactId>
            <version>3.18</version>
        </dependency>

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib-nodep</artifactId>
            <version>2.1_3</version>
        </dependency>

        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.20.0-GA</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
            </plugin>

        </plugins>
    </build>
</project>


Spring-mvc.xml配置

<!-- 支持AOP -->
	<aop:aspectj-autoproxy />
	<!-- 注解扫描 -->
	<context:component-scan base-package="com.zxit"  />
	<!-- 全局拦截器 -->
	<mvc:interceptors> 
		<bean class="com.zxit.interceptor.MVCInterceptor"></bean>
 	</mvc:interceptors> 
	<bean
		class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
		<property name="cacheSeconds" value="0" />
		<property name="messageConverters">
			<list>
				<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
			 	<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
			 	<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
			 	<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
			    <bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
            </list>
		</property>
	</bean>
	<!-- 启动mvc注解模式 -->
	<mvc:annotation-driven>
		<mvc:message-converters>
			<!-- 加入ajax乱码过滤,使其支持utf-8的有效返回值 -->
			<bean class="org.springframework.http.converter.StringHttpMessageConverter">
				<property name="supportedMediaTypes">
					<list>
						<value>text/html;charset=UTF-8</value>
					</list>
				</property>
			</bean>
		</mvc:message-converters>
	</mvc:annotation-driven>
	<!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 -->
	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver"
		p:suffix=".jsp">
	</bean>
	<!-- 时间格式转换 -->
	<bean id="conversionService"
		class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
	<!-- 处理文件上传 批量导入的时候能用用 其他估计也用不到 -->
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<property name="defaultEncoding" value="utf-8" /> <!-- 默认编码 (ISO-8859-1) -->
		<property name="maxInMemorySize" value="10240" /> <!-- 最大内存大小 (10240) -->
<!-- 		<property name="uploadTempDir" value="/upload/" /> 上传后的目录名 (WebUtils#TEMP_DIR_CONTEXT_ATTRIBUTE) -->
		<property name="maxUploadSize" value="-1" /> <!-- 最大文件大小,-1为无限止(-1) -->
	</bean>

Spring-config.xml数据、事物、切面

<!-- 数据源 -->
	<!-- 不需要安装任何数据库的客户端程序,只需要按照格式配置即可。当然程序提供用客户端的连接方式,详见下面的注释 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="oracle.jdbc.driver.OracleDriver" />
     	<property name="jdbcUrl" value="jdbc:oracle:thin:@${db.jdbcUrl}" />  
		<property name="user" value="${db.user}" />
		<property name="password" value="${db.password}" /> 	
	</bean>

	<!-- SessionFactory -->
	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<!-- ibernate的相关属性配置 -->
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
				<prop key="hibernate.hbm2ddl.auto">none</prop>
				<prop key="hibernate.show_sql">${db.show_sql}</prop>
				<prop key="hibernate.format_sql">${db.format_sql}</prop>
				<prop key="hibernate.jdbc.use_streams_for_binary">true</prop>
				<prop key="hibernate.jdbc.fetch_size">1</prop>
				<prop key="hibernate.jdbc.batch_size">0</prop>
				<prop key="current_session_context_class">thread</prop>
				<prop key="javax.persistence.validation.mode">none</prop>
				<!-- 二级缓存,3年内这个肯定用不到 -->
				<prop key="hibernate.cache.use_second_level_cache">false</prop>
				<prop key="hibernate.cache.use_query_cache">false</prop>
                <prop key="hibernate.connection.url">jdbc:oracle:thin:@//10.58.7.166:1521/orcl</prop>
                <prop key="hibernate.connection.driver_class">oracle.jdbc.OracleDriver</prop>
			</props>
		</property>
		<!-- 实体类扫描器 -->
		<property name="packagesToScan">
			<value>com.zxit.model</value>
		</property>
	</bean>
	
	<bean id="hibernateTemplate" class="org.springframework.orm.hibernate4.HibernateTemplate">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
 
    <!--配置一个JdbcTemplate实例 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>

	<!-- 事务管理器 -->
	<bean id="txManager"
		class="org.springframework.orm.hibernate4.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>
	
	<!-- 事务注解 -->
	<tx:annotation-driven transaction-manager="txManager"	proxy-target-class="false" />
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="find*" read-only="true" />
			<tx:method name="init*" read-only="true" />
			<tx:method name="*" />  <!-- 其他事务在require中运行 -->
		</tx:attributes>
	</tx:advice>

	<!-- aop事务切面 -->
	<aop:config>
		<aop:pointcut expression="execution(public * com.zxit.service.impl.*.*(..))" id="businessService" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="businessService" />
	</aop:config>

Spring-mina.xml 用Spring管理MINA。

<import resource="classpath*:spring-config.xml" />

<!-- 	消息主体 -->
	<bean id="netAndWebMsgService" class="com.zxit.service.impl.NetAndWebMsgServiceImpl">
 		<property name="systemConfig" ref="systemConfig"></property>
	</bean>
	
	
	<!-- 多线程处理过滤器,为后面的操作开启多线程,一般放在编解码过滤器之后,开始业务逻辑处理 -->
	<bean id="executorFilter" class="org.apache.mina.filter.executor.ExecutorFilter">
		<constructor-arg index="0">
			<value>1000</value>
		</constructor-arg>
		<constructor-arg index="1">
			<value>1800</value>
		</constructor-arg>
	</bean>
	<bean id="mdcInjectionFilter" class="org.apache.mina.filter.logging.MdcInjectionFilter">  
        <constructor-arg value="remoteAddress" />  
    </bean>
		
		
	<!-- Mina自带日志过滤器 默认级别为debug -->
	<bean id="loggingFilter" class="org.apache.mina.filter.logging.LoggingFilter">
	   <property name="messageReceivedLogLevel" ref="info"></property>
	   <property name="exceptionCaughtLogLevel" ref="info"></property>
	</bean>
		
	
	<!-- 累加数据包解码器:解断丢包、粘包问题 -->
	<bean id="textCodecFilter" class="org.apache.mina.filter.codec.ProtocolCodecFilter">
 		<constructor-arg> 
 		<!-- 处理对象流时候用ObjectSerializationCodecFactory -->  
        <!-- <bean class="org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory" /> -->  
         <!--构造函数的参数传入自己实现的对象-->
            <bean class="com.zxit.socket.MyCodeFactory"></bean>
<!-- 			<bean class="org.apache.mina.filter.codec.textline.TextLineCodecFactory" /> 默认实现对象-->
 		</constructor-arg> 
	</bean>
	
	
	<!-- 枚举类型 依赖注入  需要先通过此类进行类型转换-->
	<bean id="info" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">    
        <property name="staticField" value="org.apache.mina.filter.logging.LogLevel.INFO" /> 
    </bean>
    
    
	<bean id="filterChainBuilder" 
		class="org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder">
		<property name="filters">
			<map>
				<!--mina自带的线程池filter-->
				<entry key="executor" value-ref="executorFilter" />  
				 <entry key="mdcInjectionFilter" value-ref="mdcInjectionFilter" />
				<!--<entry key="codecFilter" value-ref="codecFilter" />-->
				 <!--自己实现的编解码器filter-->
                <entry key="codecFilter" value-ref="textCodecFilter" />
				<entry key="loggingFilter" value-ref="loggingFilter" />
				<!--心跳filter-->
                <entry key="keepAliveFilter" value-ref="keepAliveFilter" />
			</map>
		</property>
	</bean>
	
	<!-- 设置 I/O 接受器,并指定接收到请求后交给 mainHandler 进行处理PropertyEditor -->
	<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
		<property name="customEditors">
			<map>
				<entry key="java.net.SocketAddress" value="org.apache.mina.integration.beans.InetSocketAddressEditor">
				</entry>
			</map>
		</property>
	</bean>
	
	<!-- session config  UDP+TCP合一 通过工厂方法注入 -->
	<bean id="ioAcceptor" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor" init-method="bind" destroy-method="unbind">
		<property name="defaultLocalAddress" value=":1234" /><!-- TCP监听端口 -->
		<property name="handler" ref="netAndWebMsgService" />
		<property name="reuseAddress" value="true" />
		<property name="filterChainBuilder" ref="filterChainBuilder" />
		<!-- 默认启用的线程个数是CPU 的核数+1, -->
<!-- 	实测: -->
<!-- 	当线程达到15以上时,基本可以忽略10毫秒的连续发包,10万个UDP包粘包只有10个以内 -->
<!-- 	当线程达到15以上时,基本可以忽略100毫秒的连续发包,10万个UDP包粘包为0 -->
		<constructor-arg index="0" value="1"></constructor-arg>
	</bean>
	
	
	<!--心跳检测filter-->
    <bean id="keepAliveFilter" class="org.apache.mina.filter.keepalive.KeepAliveFilter">
        <!--构造函数的第一个参数传入自己实现的工厂-->
        <constructor-arg>
            <bean class="com.zxit.socket.MyKeepAliveMessageFactory"></bean>
        </constructor-arg>
        <!--第二个参数需要的是IdleStatus对象,value值设置为读写空闲-->
        <constructor-arg type = "org.apache.mina.core.session.IdleStatus" value="BOTH_IDLE" >
       </constructor-arg>
        <!--心跳频率,不设置则默认60s  -->
        <property name="requestInterval" value="5" />
        <!--心跳超时时间,不设置则默认30s    -->
        <property name="requestTimeout" value="10" />
        <!--不设置默认false-->
        <property name="forwardEvent" value="true" />
    </bean>
	
		
	<!-- session config  UDP\TCP分开    通过工厂方法注入 -->
<!-- 	<bean id="tcpAcceptor" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor" init-method="bind" destroy-method="unbind">   -->
<!--         <property name="defaultLocalAddress" value=":10000" />   -->
<!--         <property name="handler" ref="tcpHandler" />   -->
<!--         <property name="filterChainBuilder" ref="filterChainBuilder" />   -->
<!--         <property name="reuseAddress" value="true" />   -->
<!--     </bean> -->
<!--     <bean id="tcpHandler" class="com.umaiw.socket.TCPHandler">     -->
<!--     </bean>  -->
    
<!--     <bean id="udpAcceptor" class="org.apache.mina.transport.socket.nio.NioDatagramAcceptor" init-method="bind" destroy-method="unbind">   -->
<!--         <property name="defaultLocalAddress" value=":10001" />   -->
<!--         <property name="handler" ref="udpHandler" />   -->
<!--         <property name="filterChainBuilder" ref="filterChainBuilder" />            -->
<!--     </bean> -->
<!--     <bean id="udpHandler" class="com.umaiw.socket.UdpHandler">     -->
<!--     </bean>  -->
	
	
<!-- 	作为多端口协议服务器 -->
<!-- 	 <bean id="ioAccepServer" class="org.apache.mina.integration.spring.IoAcceptorFactoryBean">  -->
<!--   		<property name="target">  -->
<!--    			<bean class="org.apache.mina.transport.socket.nio.NioSocketAcceptor" />  -->
<!--   		</property>  -->
<!--   		<property name="bindings">  -->
<!--     		<list> -->
<!--     			<bean class="org.apache.mina.integration.spring.Binding">  -->
<!--     				监听端口:8888  -->
<!--      				<property name="address" value=":8888" /> -->
<!--      				SampleHandler:引用定义的服务器handler  -->
<!--      				<property name="handler" ref="SampleHandler" /> -->
<!--      				<property name="serviceConfig">  -->
<!-- 	      				<bean class="org.apache.mina.transport.socket.nio.NioSocketAcceptor">  -->
<!-- 	       					<property name="filterChainBuilder" ref="filterChainBuilder" />  -->
<!-- 	       					<property name="reuseAddress" value="true" />  -->
<!-- 	      				</bean>  -->
<!--      				</property>  -->
<!--     			</bean>  -->
     
<!-- 			    <bean class="org.apache.mina.integration.spring.Binding">  -->
<!-- 				    <property name="address" value=":9999" />  -->
<!-- 				    <property name="handler" ref="bossSampleHandler" />  -->
<!-- 				    <property name="serviceConfig">  -->
<!-- 			      	<bean class="org.apache.mina.transport.socket.nio.NioSocketAcceptor">  -->
<!-- 			       		<property name="filterChainBuilder" ref="filterChainBuilder" />  -->
<!-- 			       		<property name="reuseAddress" value="true" />  -->
<!-- 			      	</bean>  -->
<!-- 			     	</property>  -->
<!-- 			    </bean>  -->
<!--    			</list>  -->
<!--   		</property>  -->
<!--  </bean> -->
  
<!--   自定义服务端handler -->
<!--  <bean id="SampleHandler" class="com.zxit.socket.NetSocketHandler" />  -->
<!--  <bean id="bossSampleHandler" class="com.zxit.socket.NetSocketHandler" /> -->

整体的配置就是这些,特别要注意Spring-Mina的配置和官网上的demo有点不一样。用官网那个配置跑不起来!

<!-- 设置 I/O 接受器,并指定接收到请求后交给 mainHandler 进行处理PropertyEditor -->
	<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
		<property name="customEditors">
			<map>
				<entry key="java.net.SocketAddress" value="org.apache.mina.integration.beans.InetSocketAddressEditor">
				</entry>
			</map>
		</property>
	</bean>


协议交换类。(注册、拦截器、WebsocketHandler)可以自行找,这里不再累述。

WebSocketServiceImpl.java 用于转发websocket封装好的JSON消息到页面。接口集成了WebsocketHandler。

/**
 * WebSocket处理器
 * @Date 2015年6月11日 下午1:19:50
 */
@Service("webSocketService")
public class WebSocketServiceImpl implements WebSocketService {

	public static final Map<String, WebSocketSession> userSocketSessionMap = new HashMap<String, WebSocketSession>();
	
	/**
	 * 给所有在线用户发送消息
	 * @param message
	 * @throws IOException
	 */
//	@Override
//	public void broadcast(TextMessage message) throws IOException {
//		System.out.println(userSocketSessionMap);
//		Iterator<Entry<String, WebSocketSession>> it = userSocketSessionMap
//				.entrySet().iterator();
//		// 多线程群发
//		while (it.hasNext()) {
//			final Entry<String, WebSocketSession> entry = it.next();
//			if (entry.getValue().isOpen()) {
//				//entry.getValue().sendMessage(message);
//				new Thread(new Runnable() {
//					public void run() {
//						try {
//							if (entry.getValue().isOpen()) {
//								entry.getValue().sendMessage(message);
//							}
//						} catch (IOException e) {
//							e.printStackTrace();
//						}
//					}
//				}).start();
//			}
//		}
//	}

	/**
	 * 给某个用户发送消息
	 * @param userid
	 * @param message
	 * @throws IOException
	 */
	public void sendMessageToUser(String userid, TextMessage message)throws IOException {
		WebSocketSession session = userSocketSessionMap.get(userid);
		System.out.println(message);
		if (session != null && session.isOpen()) {
			session.sendMessage(message);
		} else {
			System.out.println("用户:"+userid +"websocketSession已失效!");
		}
	}
	
	/**
	 * 建立连接后
	 * 接受到所有存在的会话用户上下文
	 * 并且存入websocekt的map集合
	 */
	@Override
	public void afterConnectionEstablished(WebSocketSession session)
			throws Exception {
		String uid = (String) session.getAttributes().get("uid");
		//Zdxxb zdxxb = (Zdxxb)session.getAttributes().get("zdxxb");
		if (userSocketSessionMap.get(uid) == null) {
			userSocketSessionMap.put(uid, session);
		}
	}

	/**
	 * sendMessage方法封装在下面
	 * 消息处理,在客户端通过Websocket API发送的消息会经过这里,然后进行相应的处理
	 */
	@Override
	public void handleMessage(WebSocketSession session,WebSocketMessage<?> message) throws Exception {
		if (message.getPayloadLength() == 0)
			return;
		Position position = new Gson().fromJson(message.getPayload().toString(),Position.class);
		System.out.println(position.toString());
		sendMessageToUser("RP01", new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(position)));
	}

	/**
	 * 消息传输错误处理
	 */
	@Override
	public void handleTransportError(WebSocketSession session,
			Throwable exception) {
		if (session.isOpen()) {
			try {
				session.close();
			} catch (IOException e) {
				// e.printStackTrace();
			}
		}
		Iterator<Entry<String, WebSocketSession>> it = userSocketSessionMap
				.entrySet().iterator();
		// 移除Socket会话
		while (it.hasNext()) {
			Entry<String, WebSocketSession> entry = it.next();
			if (entry.getValue().getId().equals(session.getId())) {
				userSocketSessionMap.remove(entry.getKey());
				System.out.println("Socket会话已经移除:用户ID" + entry.getKey());
				break;
			}
		}
	}

	/**
	 * 关闭连接后
	 */
	@Override
	public void afterConnectionClosed(WebSocketSession session,
			CloseStatus closeStatus) throws Exception {
		Iterator<Entry<String, WebSocketSession>> it = userSocketSessionMap.entrySet().iterator();
		// 移除Socket会话
		while (it.hasNext()) {
			Entry<String, WebSocketSession> entry = it.next();
			if (entry.getValue().getId().equals(session.getId())) {
				userSocketSessionMap.remove(entry.getKey());
				System.out.println("Socket会话已经移除用户ID=" + entry.getKey());
				System.out.println("Websocket:" + entry.getKey() + "已经关闭");
				break;
			}
		}
	}

	@Override
	public boolean supportsPartialMessages() {
		return false;
	}

	

}


NetAndWebMsgServiceImpl.java处理Socket信息并向注入的websocketService发送,打通Socket和Web页面通讯的桥梁。这是一个非常简陋的模型。

/**
 * NetSocket和WebSocket的初步整合
 * 注意:springmvc和spring的作用域不同,SpringMVC的IOC容器可以看作是springIOC的一个小型作用域
 * 所以从软件设计模式上来说:
 * SpringMVC 的 IOC 容器中的 bean 可以来引用 Spring IOC 容器中的 bean. 
 * Spring IOC 容器中的 bean不能来引用 SpringMVC IOC 容器中的 bean!
 * 不过我们如果依然希望拿到bean可以用@Bean来解决
 * @since 2017年1月17日
 * @author nanxiaofeng
 * @version 1.0
 * 更改人:
 * 更改日期:
 */
@Service("netAndWebMsgService")
public class NetAndWebMsgServiceImpl extends IoHandlerAdapter implements NetAndWebMsgService {


	public  NetAndWebMsgServiceImpl(){

	}

	private SystemConfig systemConfig;

	public SystemConfig getSystemConfig() {
		return systemConfig;
	}

	public void setSystemConfig(SystemConfig systemConfig) {
		this.systemConfig = systemConfig;
	}

	@Bean
	public WebSocketService webSocketService(){
	    return new WebSocketServiceImpl();
	}

//	@Bean
//	public ParseEntityService parseEntityService(){
//		return new ParseEntityServiceImpl();
//	}

	@Override
	public void sendSocketMsgToWeb(String msg) {
		JSONObject jsonObject = JSONObject.fromObject(msg);
		//这里只处理半包就行了
		Position position = UtilTools.convertToObj(jsonObject,Position.class);
		try {
			webSocketService().sendMessageToUser(position.getMsgTo(), new TextMessage(msg));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@Override
	public void messageReceived(IoSession session, Object message)
			throws Exception {
		IoBuffer bbuf = (IoBuffer) message;
		System.out.println("message = " + bbuf + bbuf.limit());    
		//直接推送,需要业务服务器自行处理
		byte[] byten = new byte[bbuf.limit()];
		bbuf.get(byten, bbuf.position(), bbuf.limit());
		String msg = new String(byten,Charset.forName("gb2312"));
		//启用缓存池
//      bbuf.get(byten);    
//		StringBuilder stringBuilder = new StringBuilder();  
//		for(int i = 0; i < byten.length; i++){      
//			stringBuilder.append((char) byten[i]); //可以根据需要自己改变类型      
//		}  
//		msg = stringBuilder.toString();
		
        System.out.println("客户端收到消息" + msg);
		sendSocketMsgToWeb(msg);
	}
	
	@Override
	public void messageSent(IoSession session, Object message) throws Exception {
		if (message instanceof IoBuffer) {
			IoBuffer buffer = (IoBuffer) message;
			byte[] bb = buffer.array();
			for (int i = 0; i < bb.length; i++) {
				System.out.print((char) bb[i]);
			}
		}
	}

	// 抛出异常触发的事件
	@Override
	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		cause.printStackTrace();
		session.close(true);
	}


	// 连接关闭触发的事件
	@Override
	public void sessionClosed(IoSession session) throws Exception {
		System.out.println("Session closed...");
	}

	

	// 建立连接触发的事件
	@Override
	public void sessionCreated(IoSession session) throws Exception {
		System.out.println("Session created...");
		SocketAddress remoteAddress = session.getRemoteAddress();
		System.out.println(remoteAddress);

	}

	// 会话空闲
	@Override
	public void sessionIdle(IoSession session, IdleStatus status)
			throws Exception {
		System.out.println("Session idle...");
	}

	

	/**
	* 打开连接触发的事件
	*它与sessionCreated的区别在于
	*一个连接地址(A)第一次请求Server会建立一个Session默认超时时间为1分钟
	*此时若未达到超时时间这个连接地址(A)再一次向Server发送请求即是sessionOpened
	*连接地址(A)第一次向Server发送请求或者连接超时后向Server发送请求时会同时触发sessionCreated和sessionOpened两个事件
	 */
	@Override
	public void sessionOpened(IoSession session) throws Exception {
		System.out.println("Session Opened...");
		SocketAddress remoteAddress = session.getRemoteAddress();
		System.out.println(remoteAddress);
	}

	
}

注意 webSocketService并不能像我们以前@Resource一样注入,这里我也想了一下,大概因为NetAndWebxxxxx.java是SpringIOC的域,而WebSocketService是SpringMVC IOC的作用域,所以这里直接注入,是null。具体,我也没时间整非常明白,后面需要着重看一下这个。这个类里面所有的@Resource都是null。

@Bean
	public WebSocketService webSocketService(){
	    return new WebSocketServiceImpl();
	}

至于百度的JS-Api就不说了,官网上有http://lbsyun.baidu.com/index.php?title=jspopular,自行了解。

下面是运行效果。

从虚拟机的发一个UDP消息给Web服务。

Web服务根据已有的用户Map进行转发。



页面接收到Json并解析定位!



下面是并发,处理之后,100毫秒一次的UDP协议。不存在粘包、半包的问题。可以看到,后台发送消息10万次,均被web正常解析,没有出现JS错误。



好了就到这里,实在没精力了写了。如果有什么建议,大家可以写到楼下,我会抓紧Fix。谢谢大家!





评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值