本项目用了
pom.xml
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
io.netty
netty-all
4.1.66.Final
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.2.0
mysql
mysql-connector-java
com.alibaba
druid
1.2.6
runtime
com.alibaba
fastjson
1.2.78
commons-codec
commons-codec
org.apache.commons
commons-lang3
3.12.0
org.springframework.boot
spring-boot-maven-plugin
2.5.4
src/main/java
**/*.xml
src/main/resources
application.properties
#mysql
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/user
NettyServer
package com.cocos.login.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Service("nettyServer")
public class NettyServer {
private static final int port = 8080;
private NioEventLoopGroup boss = new NioEventLoopGroup();
private NioEventLoopGroup worker = new NioEventLoopGroup();
@Autowired
private MyChannelInitializer myChannelInitializer;
@PostConstruct
public void start() {
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(boss,worker)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.DEBUG))
.option(ChannelOption.SO_BACKLOG,1024)
.childHandler(myChannelInitializer);
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
MyChannelInitializer
package com.cocos.login.netty;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.json.JsonObjectDecoder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service("myChannelInitializer")
public class MyChannelInitializer extends ChannelInitializer {
@Resource
private ServerHandler serverHandler;
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("myCodec",new HttpServerCodec());
//发送大量数据浏览器会响应多个http请求,用一个处理器将多个段聚合
ch.pipeline().addLast("myAggregator",new HttpObjectAggregator(64 * 1024));
//json格式解码器,当检测到匹配数量的"{" 、”}”或”[””]”时,则认为是一个完整的json对象或者json数组。
ch.pipeline().addLast(new JsonObjectDecoder());
ch.pipeline().addLast("myServerHandler",serverHandler);
}
}
ServerHandler
package com.cocos.login.netty;
import com.alibaba.fastjson.JSON;
import com.cocos.login.pojo.User;
import com.cocos.login.service.UserService;
import com.cocos.login.utils.Md5Utils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("myServerHandler")
@ChannelHandler.Sharable
public class ServerHandler extends SimpleChannelInboundHandler {
@Autowired
UserService userService;
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
if (msg instanceof FullHttpRequest) {
FullHttpRequest httpRequest = (FullHttpRequest) msg;
System.out.println("请求的Uri为:" + httpRequest.uri());
if (httpRequest.method() == HttpMethod.POST) {
String string = httpRequest.content().toString(CharsetUtil.UTF_8);
User user = JSON.parseObject(string, User.class);
System.out.println(user.toString());
if (httpRequest.uri().equals("/login")) {
User userById = userService.getUserById(user.getId());
if (Md5Utils.code(user.getPassword()).equals(userById.getPassword())) {
FullHttpResponse response = Response(JSON.toJSONString(userById));
ctx.writeAndFlush(response);
} else {
ctx.writeAndFlush(Response("0"));
}
} else if (httpRequest.uri().equals("/add")) {
User addUser = new User(user.getId(),"tom", Md5Utils.code(user.getPassword()),0,"hello");
int result = userService.addUser(addUser);
ctx.writeAndFlush(Response(JSON.toJSONString(result)));
}
} else {
System.out.println("仅支持POST请求");
}
}
}
//响应
private FullHttpResponse Response(String string){
ByteBuf content = Unpooled.copiedBuffer(string, CharsetUtil.UTF_8);
FullHttpResponse response =
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.OK,content);
response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN,"*");//解决跨域问题
response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain; charset=UTF-8");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
return response; //返回响应
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("连接的客户端地址:" + ctx.channel().remoteAddress());
super.channelActive(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("异常了......");
ctx.close();
super.exceptionCaught(ctx, cause);
}
}
实体类User
package com.cocos.login.pojo;
public class User {
private String id;
private String username;
private String password;
private int heroCount;
private String message;
public User(String id, String password) {
this.id = id;
this.password = password;
}
public User(String id, String username, String password, int heroCount, String message) {
this.id = id;
this.username = username;
this.password = password;
this.heroCount = heroCount;
this.message = message;
}
//省略get 、set、toString方法
用了Mybatis框架,所有有mapper层
UserService
package com.cocos.login.service;
import com.cocos.login.mapper.UserMapper;
import com.cocos.login.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowired
UserMapper userMapper;
public int addUser(User user) {
return userMapper.addUser(user);
}
public int deleteUserById(String id) {
return userMapper.deleteUserById(id);
}
public int updateUserById(User user) {
return userMapper.updateUserById(user);
}
public User getUserById(String id) {
return userMapper.getUserById(id);
}
public String getPwdById(String id) {
return userMapper.getPwdById(id);
}
public List getAllUsers() {
return userMapper.getAllUsers();
}
}
UserMapper
package com.cocos.login.mapper;
import com.cocos.login.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserMapper {
int addUser(User user);
int deleteUserById(String id);
int updateUserById(User user);
User getUserById(String id);
String getPwdById(String id);
List getAllUsers();
}
UserMapper.xml
INSERT INTO user(id,username,password,heroCount,message) VALUE (#{id},#{username},#{password},#{heroCount},#{message});
DELETE FROM user WHERE id=#{id};
UPDATE user set password=#{password} WHERE id=#{id};
Md5Utils
package com.cocos.login.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Md5Utils {
public static String code(String str){
try{
//1.获取MessageDigest对象 生成一个MD5加密计算摘要
MessageDigest md = MessageDigest.getInstance("MD5") ;
/*
str.getBytes()
* 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中.
此方法多用在字节流中,用与将字符串转换为字节。
* */
// 计算md5函数 使用指定的字节数组更新摘要md
md.update(str.getBytes());
/*
* digest()最后确定返回md5 hash值,返回值为8的字符串。
* 因为md5 hash值是16位的hex值,实际上就是8位的
* */
byte[] byteDigest = md.digest() ;
int i ;
StringBuffer buf = new StringBuffer("") ;
//遍历byteDigest
//加密逻辑,可以debug自行了解一下加密逻辑
for(int offset = 0 ; offset
LoginServerApplication
package com.cocos.login;
import com.cocos.login.netty.NettyServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class LoginServerApplication {
public static void main(String[] args) {
// 启动嵌入式的 Tomcat 并初始化 Spring 环境及其各 Spring 组件
ConfigurableApplicationContext context = SpringApplication.run(LoginServerApplication.class, args);
NettyServer nettyServer = context.getBean(NettyServer.class);
nettyServer.start();
}
}
名为user的数据库
名为user的表
字段根据user表创建
我们提前创建一个数据,配合/add请求,持久化一条含有md5的密码的数据,用于测试
到此服务器模块完成,先用postman测试一下,发现运行正常
打码部分是我测试用的,跟本项目无关,把未打码部分完成就好了,用到的组件在ts代码中查看。
user
export class User {
private _id: string = "";
private _username: string = "";
private _password: string = "";
private _heroCount: number = 0;
private _message: string = "";
constructor(id: string, password: string) {
this._id = id;
this._password = password;
}
public get id(): string {
return this._id;
}
public set id(value: string) {
this._id = value;
}
public get username(): string {
return this._username;
}
public set username(value: string) {
this._username = value;
}
public get password(): string {
return this._password;
}
public set password(value: string) {
this._password = value;
}
public get heroCount(): number {
return this._heroCount;
}
public set heroCount(value: number) {
this._heroCount = value;
}
public get message(): string {
return this._message;
}
public set message(value: string) {
this._message = value;
}
}
http
const {ccclass, property} = cc._decorator;
@ccclass()
export class Http{
/**
* Get请求
* @param url 站点:http://www.baidu.com
* @param path 子路径 /index.html
* @param params key1=value1&key2=value2&key3=value3
* @param callback 当这个请求有回应的时候调用这个callback函数;
* @returns
*/
get(url: string, path: string, params: string, callback: Function): any{
var xhr = cc.loader.getXMLHttpRequest();
xhr.timeout = 5000;
var requestURL = url + params;
if (params) {
requestURL = requestURL + "?" + params;
}
xhr.open("GET", requestURL, true);
if (cc.sys.isNative) {
xhr.setRequestHeader("Accept-Encoding", "text/plain;charset=UTF-8");
}
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)) {
console.log("http res("+ xhr.responseText.length + "):" + xhr.responseText);
}
}
xhr.send();
return xhr;
}
/**
* POST请求
* @param url
* @param path
* @param params
* @param body 数据body
* @param callback
* @returns
*/
post(url: string, path: string, params: string, body: any, callback: Function) {
var xhr = cc.loader.getXMLHttpRequest();
xhr.timeout = 5000;
var requestURL = url + path;
if (params) {
requestURL = requestURL + "?" + params;
}
xhr.open("POST", requestURL, true);
if (cc.sys.isNative) {
xhr.setRequestHeader("Accept-Encoding","text/plain;charset=UTF-8");
}
if (body) {
xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
}
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)){
try {
//var ret = xhr.responseText;
var ret = JSON.parse(xhr.responseText);
if(callback !== null){
callback(null, ret);
}
} catch (e) {
console.log("err:" + e);
callback(e, null);
}
}
};
if (body) {
xhr.send(body);
}
return xhr;
}
download(url, path, params, callback) {
var xhr = cc.loader.getXMLHttpRequest();
xhr.timeout = 5000;
var requestURL = url + path;
if (params) {
requestURL = requestURL + "?" + params;
}
xhr.responseType = "arraybuffer"; // 指定我们的数据类型
xhr.open("GET",requestURL, true);
if (cc.sys.isNative){
xhr.setRequestHeader("Accept-Encoding","application/json;charset=UTF-8");
}
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)){
var buffer = xhr.response;
var data = new Uint8Array(buffer); // arraybuffer, new Unit8Array
callback(null, data);
}
};
xhr.send();
return xhr;
}
};
场景管理类 Manage
const {ccclass, property} = cc._decorator;
import { Http } from "./http";
import { User } from "./user";
@ccclass()
export class Manage extends cc.Component {
@property(cc.Node)
area_node: cc.Node = null;
@property(cc.EditBox)
user_node: cc.EditBox = null;
@property(cc.EditBox)
pwd_node: cc.EditBox = null;
@property(cc.Button)
btn_node: cc.Button = null;
onLoad () {
this.btn_node.node.on(cc.Node.EventType.TOUCH_END, this.login, this);
//editing-did-began、editing-did-ended、text-changed、editing-return
this.user_node.node.on("editing-did-ended", this.onEditDidEnded, this);
}
onDestroy(){
this.btn_node.node.off(cc.Node.EventType.TOUCH_END, this.login, this);
this.user_node.node.off("editing-did-ended", this.onEditDidEnded, this);
}
private login (): void {
var user = new User(this.user_node.string, this.pwd_node.string);
//JSON.parse():json字符串转对象、JSON.stringify():对象转json字符串
let msg = JSON.stringify(user);
const http = new Http();
http.post("http://localhost:8080/", "login", null, msg, (err, data)=>{
if (err) {
console.log(err);
return;
}
console.log(data);
});
}
private onEditDidEnded() {
// if (this.user_node.string === "") {
// return;
// }
}
}
到此客户端也完成了
运行spring boot和CoCos Creator
此处时CoCos Creator代码中的简单打印,以后可以根据返回的对象值执行更多的操作
spring boot 后台打印
连接的客户端地址:/0:0:0:0:0:0:0:1:53926
请求的Uri为:/login
User{id='123456789', username='', password='1234556789', heroCount=0, message=''}
2021-08-29 22:18:54.340 INFO 692 --- [ntLoopGroup-3-1] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
连接的客户端地址:/0:0:0:0:0:0:0:1:59113
请求的Uri为:/login
User{id='123456789', username='', password='123456789', heroCount=0, message=''}