SocketIO实现Java聊天服务端

  • Post author:
  • Post category:java


本文使用到netty-socketio开源库,以及mysql,所以首先在pom.xml中添加相应的依赖库

<dependency>
        <groupId>com.corundumstudio.socketio</groupId>
        <artifactId>netty-socketio</artifactId>
        <version>1.7.11</version>
</dependency>
<dependency>
        <groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
</dependency>

添加SpringBoot配置文件

wss.server.port=8081
wss.server.host=localhost
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/springlearn
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.jpa.database = MYSQL
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

修改Application文件,添加nettysocket的相关配置信息

package com.zd.sunt;
 
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
 
import com.corundumstudio.socketio.AuthorizationListener;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.HandshakeData;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;
 
@SpringBootApplication
public class NettySocketSpringApplication {
 
	@Value("${wss.server.host}")
    private String host;
 
    @Value("${wss.server.port}")
    private Integer port;
    
    @Bean
    public SocketIOServer socketIOServer() 
    {
    	Configuration config = new Configuration();
        config.setHostname(host);
        config.setPort(port);
        
        //该处可以用来进行身份验证
        config.setAuthorizationListener(new AuthorizationListener() {
			@Override
			public boolean isAuthorized(HandshakeData data) {
				//http://localhost:8081?username=test&password=test
				//例如果使用上面的链接进行connect,可以使用如下代码获取用户密码信息,本文不做身份验证
//				String username = data.getSingleUrlParam("username");
//				String password = data.getSingleUrlParam("password");
				return true;
			}
		});
        
        final SocketIOServer server = new SocketIOServer(config);
        return server;
    }
	
	@Bean
    public SpringAnnotationScanner springAnnotationScanner(SocketIOServer socketServer) {
        return new SpringAnnotationScanner(socketServer);
    }
	
	public static void main(String[] args) {
		SpringApplication.run(NettySocketSpringApplication.class, args);
	}
}

添加消息结构类MessageInfo.java

package com.zd.sunt.message;
 
public class MessageInfo {
	//源客户端id
	private String sourceClientId;
	//目标客户端id
	private String targetClientId;
	//消息类型
	private String msgType;
	//消息内容
	private String msgContent;
	
	public String getSourceClientId() {
		return sourceClientId;
	}
	public void setSourceClientId(String sourceClientId) {
		this.sourceClientId = sourceClientId;
	}
	public String getTargetClientId() {
		return targetClientId;
	}
	public void setTargetClientId(String targetClientId) {
		this.targetClientId = targetClientId;
	}
	public String getMsgType() {
		return msgType;
	}
	public void setMsgType(String msgType) {
		this.msgType = msgType;
	}
	public String getMsgContent() {
		return msgContent;
	}
	public void setMsgContent(String msgContent) {
		this.msgContent = msgContent;
	}
}

添加客户端信息,用来存放客户端的sessionid

package com.zd.sunt.bean;
 
import java.util.Date;
 
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
 
@Entity
@Table(name="t_clientinfo")
public class ClientInfo {
	@Id
	@NotNull
	private String clientid;
	private Short connected;
	private Long mostsignbits;
	private Long leastsignbits;
	private Date lastconnecteddate;
	public String getClientid() {
		return clientid;
	}
	public void setClientid(String clientid) {
		this.clientid = clientid;
	}
	public Short getConnected() {
		return connected;
	}
	public void setConnected(Short connected) {
		this.connected = connected;
	}
	public Long getMostsignbits() {
		return mostsignbits;
	}
	public void setMostsignbits(Long mostsignbits) {
		this.mostsignbits = mostsignbits;
	}
	public Long getLeastsignbits() {
		return leastsignbits;
	}
	public void setLeastsignbits(Long leastsignbits) {
		this.leastsignbits = leastsignbits;
	}
	public Date getLastconnecteddate() {
		return lastconnecteddate;
	}
	public void setLastconnecteddate(Date lastconnecteddate) {
		this.lastconnecteddate = lastconnecteddate;
	}
	
}

添加查询数据库接口ClientInfoRepository.java

package com.zd.sunt.repository;
 
import org.springframework.data.repository.CrudRepository;
 
import com.zd.sunt.bean.ClientInfo;
 
public interface ClientInfoRepository extends CrudRepository<ClientInfo, String>{
	ClientInfo findClientByclientid(String clientId);
}

添加消息处理类MessageEventHandler.java

package com.zd.sunt.message;
 
import java.util.Date;
import java.util.UUID;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.zd.sunt.bean.ClientInfo;
import com.zd.sunt.repository.ClientInfoRepository;
 
@Component
public class MessageEventHandler 
{
	private final SocketIOServer server;
	
	@Autowired
	private ClientInfoRepository clientInfoRepository;
	
	@Autowired
    public MessageEventHandler(SocketIOServer server) 
	{
        this.server = server;
    }
	//添加connect事件,当客户端发起连接时调用,本文中将clientid与sessionid存入数据库
	//方便后面发送消息时查找到对应的目标client,
	@OnConnect
    public void onConnect(SocketIOClient client)
    {
		String clientId = client.getHandshakeData().getSingleUrlParam("clientid");
		ClientInfo clientInfo = clientInfoRepository.findClientByclientid(clientId);
		if (clientInfo != null)
		{
			Date nowTime = new Date(System.currentTimeMillis());
			clientInfo.setConnected((short)1);
			clientInfo.setMostsignbits(client.getSessionId().getMostSignificantBits());
			clientInfo.setLeastsignbits(client.getSessionId().getLeastSignificantBits());
			clientInfo.setLastconnecteddate(nowTime);
			clientInfoRepository.save(clientInfo);
		}
    }
	
	//添加@OnDisconnect事件,客户端断开连接时调用,刷新客户端信息
	@OnDisconnect
	public void onDisconnect(SocketIOClient client)
	{
		String clientId = client.getHandshakeData().getSingleUrlParam("clientid");
		ClientInfo clientInfo = clientInfoRepository.findClientByclientid(clientId);
		if (clientInfo != null)
		{
			clientInfo.setConnected((short)0);
			clientInfo.setMostsignbits(null);
			clientInfo.setLeastsignbits(null);
			clientInfoRepository.save(clientInfo);
		}
	}
	
	//消息接收入口,当接收到消息后,查找发送目标客户端,并且向该客户端发送消息,且给自己发送消息
	@OnEvent(value = "messageevent")
    public void onEvent(SocketIOClient client, AckRequest request, MessageInfo data) 
	{
		String targetClientId = data.getTargetClientId();
		//通过目标id获取目标连接信息
		ClientInfo clientInfo = clientInfoRepository.findClientByclientid(targetClientId);
		if (clientInfo != null && clientInfo.getConnected() != 0)
		{
			UUID uuid = new UUID(clientInfo.getMostsignbits(),   
			clientInfo.getLeastsignbits());
			System.out.println(uuid.toString());
			MessageInfo sendData = new MessageInfo();
			sendData.setSourceClientId(data.getSourceClientId());
			sendData.setTargetClientId(data.getTargetClientId());
			sendData.setMsgType("chat");
			sendData.setMsgContent(data.getMsgContent());
			client.sendEvent("messageevent", sendData);
			server.getClient(uuid).sendEvent("messageevent", sendData);
		}
		
    }
}

启动一个socket线程类
package com.xiaofangtech.sunt.message;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
 
import com.corundumstudio.socketio.SocketIOServer;
 
@Component
public class ServerRunner implements CommandLineRunner {
	private final SocketIOServer server;
 
    @Autowired
    public ServerRunner(SocketIOServer server) {
        this.server = server;
    }
 
    @Override
    public void run(String... args) throws Exception {
        server.start();
    }
}

使用index.html简单调试一下

<!DOCTYPE html>
<html>
<head>
 
        <meta charset="utf-8" />
 
        <title>Demo Chat</title>
 
        <link href="bootstrap.css" rel="stylesheet">
 
	<style>
		body {
			padding:20px;
		}
		#console {
			height: 400px;
			overflow: auto;
		}
		.username-msg {color:orange;}
		.connect-msg {color:green;}
		.disconnect-msg {color:red;}
		.send-msg {color:#888}
	</style>
 
 
	<script src="js/socket.io/socket.io.js"></script>
        <script src="js/moment.min.js"></script>
        <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
 
	<script>
 
        var clientid = 'testclient1';
		var targetClientId= 'testclient2';
		
		var socket =  io.connect('http://localhost:8081?clientid='+clientid);
 
		socket.on('connect', function() {
			output('<span class="connect-msg">Client has connected to the server!</span>');
		});
 
		socket.on('messageevent', function(data) {
			output('<span class="username-msg">' + data.sourceClientId + ':</span> ' + data.msgContent);
		});
 
		socket.on('disconnect', function() {
			output('<span class="disconnect-msg">The client has disconnected!</span>');
		});
 
                function sendDisconnect() {
                        socket.disconnect();
                }
 
		function sendMessage() {
                        var message = $('#msg').val();
                        $('#msg').val('');
 
                        var jsonObject = {sourceClientId: clientid,
                                          targetClientId: targetClientId,
										  msgType: 'chat',
										  msgContent: message};
                        socket.emit('messageevent', jsonObject);
		}
 
		function output(message) {
                        var currentTime = "<span class='time'>" +  moment().format('HH:mm:ss.SSS') + "</span>";
                        var element = $("<div>" + currentTime + " " + message + "</div>");
			$('#console').prepend(element);
		}
 
        $(document).keydown(function(e){
            if(e.keyCode == 13) {
                $('#send').click();
            }
        });
	</script>
</head>
 
<body>
 
	<h1>Netty-socketio Demo Chat</h1>
 
	<br/>
 
	<div id="console" class="well">
	</div>
 
        <form class="well form-inline" onsubmit="return false;">
           <input id="msg" class="input-xlarge" type="text" placeholder="Type something..."/>
           <button type="button" onClick="sendMessage()" class="btn" id="send">Send</button>
           <button type="button" onClick="sendDisconnect()" class="btn">Disconnect</button>
        </form>
 
 
 
</body>
 
</html>



版权声明:本文为weixin_41012481原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。