【Netty4.x】Unity客户端与Netty服务器的网络通信(一)

来源:会编程的小毛驴 发布时间:2018-11-21 15:49:40 阅读量:991

一、我的开发环境

eclipse-jee-indigo-SR2-win32-x86_64

JDK1.7

windows-64

netty-all-4.1.1.Final-sources 下载地址:http://netty.io/ ,从【Downloads】选择下载netty-all-4.1.1.Final-sources。

创建一个java工程,选中项目右键Properties->Java Build Path->Add External JARS->将netty-all-4.1.1.Final.jar包导入到项目中

二  、Netty服务器端开发

   2.1代码示例

1 创建服务启动类HttpServer,既然作为服务器它肯定和tomcat服务器一样有个开关来开启/关闭服务器,可以在类中看到有个main函数。对的,它的启动方式就是右键->Run As->Java Application

package com.lll.game;

 

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

 

import io.netty.bootstrap.ServerBootstrap;

import io.netty.channel.ChannelFuture; 

import io.netty.channel.ChannelInitializer; 

import io.netty.channel.ChannelOption; 

import io.netty.channel.EventLoopGroup; 

import io.netty.channel.nio.NioEventLoopGroup; 

import io.netty.channel.socket.SocketChannel; 

import io.netty.channel.socket.nio.NioServerSocketChannel;

 

public class HttpServer {

    private static Log log = LogFactory.getLog(HttpServer.class);

    

    public static void main(String[] args) throws Exception {

        HttpServer server = new HttpServer();

        log.info("服务已启动...");

        server.start(8844);

    }

    

    public void start(int port) throws Exception {

    //配置服务端的NIO线程组

        EventLoopGroup bossGroup = new NioEventLoopGroup();

        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {

            ServerBootstrap b = new ServerBootstrap();

            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)

                    .childHandler(new ChannelInitializer<SocketChannel>() {

                                @Override

                                public void initChannel(SocketChannel ch) throws Exception {

                                ch.pipeline().addLast(new ServerHandler());

                                }

                            }).option(ChannelOption.SO_BACKLOG, 128) //最大客户端连接数为128

                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            //绑定端口,同步等待成功

            ChannelFuture f = b.bind(port).sync();

            //等待服务端监听端口关闭

            f.channel().closeFuture().sync();

        } finally {

        //优雅退出,释放线程池资源

            workerGroup.shutdownGracefully();

            bossGroup.shutdownGracefully();

        }

    }

}

2创建ServerHandler类来负责对网络事件进行读写操作

package com.lll.game;

 

import io.netty.channel.ChannelHandlerAdapter;

import io.netty.channel.ChannelHandlerContext;

 

public class ServerHandler extends ChannelHandlerAdapter{

@Override

public void handlerAdded(ChannelHandlerContext ctx) throws Exception {

super.handlerAdded(ctx);

System.out.println(ctx.channel().id()+"进来了");

}

@Override

public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {

super.handlerRemoved(ctx);

System.out.println(ctx.channel().id()+"离开了");

}

}


提示:本书作者使用的是netty是用的5.0,我在官方下载的时候只找到4.x包(据说5.0好像已经被官方废弃了)。2个版本相比较,不同版本ChannelHandler申明的方法不一样!!

   2.2 Netty5.x与4.x代码上的差异

  在4.x中,ChannelHandlerAdapter里的channelRead方法在ChannelInboundHandlerAdapter抽象类中,同5.X一样ChannelInboundHandlerAdapter也是基于ChannelHandler接口。这样用户可以方便地进行业务逻辑定制,例如:打印日志、统一封装异常信息、性能统计和消息编码解码等。

ChannelInboundHandlerAdapter的UML图如下:



   2.3 4.x ServerHandler类

package com.game.lll.net;

 

import java.util.Date;

 

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

 

import io.netty.buffer.ByteBuf;

import io.netty.buffer.Unpooled;

import io.netty.channel.ChannelHandlerContext;

import io.netty.channel.ChannelInboundHandlerAdapter;

 

public class ServerHandler extends ChannelInboundHandlerAdapter{

private static Log log = LogFactory.getLog(ServerHandler.class);

@Override

public void handlerAdded(ChannelHandlerContext ctx) throws Exception {

super.handlerAdded(ctx);

System.out.println(ctx.channel().id()+"进来了");

}

@Override

public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {

super.handlerRemoved(ctx);

System.out.println(ctx.channel().id()+"离开了");

}

@Override

public void channelRead(ChannelHandlerContext ctx, Object msg)

throws Exception {

ByteBuf buf = (ByteBuf)msg;

byte[] req = new byte[buf.readableBytes()];

buf.readBytes(req);

String body = new String(req,"UTF-8");

log.info(req);

String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body)?

new Date(System.currentTimeMillis()).toString():"BAD ORDER";

ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());

ctx.write(resp);

}

@Override

public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

ctx.flush();

}

@Override

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)

throws Exception {

// TODO Auto-generated method stub

ctx.close();

}

}

  ServerHandler继承自ChannelInboundHandlerAdapter,它用于对网络事件进行读写操作,通常我们只需要关注channelRead和exceptionCaught方法。下面对这两个方法进行简单说明。

   2.3.1 channelRead()操作

  第30行做类型转换,将msg转换成Netty的ByteBuf对象。通过ByteBuf的readableBytes方法可以获取缓冲区可读的字节数,根据可读的字节数创建byte数组,通过ByteBuf的readBytes方法将缓冲区中的字节数组复制到新建的byte数组中,最后通过new String构造函数获取请求信息。这是对请求消息进行判断,如果是“QUERY TIME ORDER”则创建应答信息,通过ChannelHandlerContext的write方法异步发送应答消息给客户端。


   2.3.2 exceptionCaught()操作

  第43行,当发生异常时,关闭ChannelHandlerContext,释放和ChannelHandlerContext相关联的句柄等资源。


三  、Unity客户端开发

   3.1 代码示例

using System.Net.Sockets;

using System.Text;

using System.Threading;

using UnityEngine;

using UnityEngine.UI;

using System.Collections.Generic;

 

public class HttpClient : MonoBehaviour

{

private const string IP = "127.0.0.1";

private const int PORT = 8844;

 

public UILabel userName;

public UILabel password;

 

private Socket client;

private string msg,ip;

 

public void start()

{

try {

client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

client.Connect(IP, PORT);

Debug.Log ("连接服务器成功\r\n");

 

Thread threadReceive = new Thread(ReceiveMsg);

threadReceive.IsBackground = true;

threadReceive.Start();

}catch

{

Debug.Log ("连接服务器失败\r\n");

}

}

 

public void Send()

{

if(client == null)

{

start ();

}

 

byte[] buffer = Encoding.UTF8.GetBytes("userName:"+userName.text+" password"+password.text);

client.Send(buffer);

 

}

 

private void ReceiveMsg()

{

byte[] buffer = new byte[1024 * 1024];

int len = 0;

while (true)

{

len = client.Receive(buffer);

//区分是客户端来了,还是消息来了

if (buffer[0] == 1)//客户端

{

ip=Encoding.UTF8.GetString(buffer, 1, len - 1);

}else//文本消息

{

msg = Encoding.UTF8.GetString(buffer, 1, len-1);

}

}

print (msg);

}

 

void Update()

{

if (!string.IsNullOrEmpty(msg))

{

Debug.Log ("服务器说:" + msg + "\r\n");

msg = "";

}

if (!string.IsNullOrEmpty(ip))

{

 

ip = "";

}

}

 

void OnApplicationQuit()

{

client.Shutdown(SocketShutdown.Both);

client.Close();

}

}

   3.2 控制台

   3.2.1 启动服务器端



   3.2.2 启动客户端



   3.2.3 控制台(服务器)



   3.2.4 控制台(客户端)


--------------------- 



标签: 服务器搭建
分享:
评论:
你还没有登录,请先