來源:會編程的小毛驢 發(fā)布時間:2018-11-21 15:49:40 閱讀量:1171
一、我的開發(fā)環(huán)境
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。
創(chuàng)建一個java工程,選中項目右鍵Properties->Java Build Path->Add External JARS->將netty-all-4.1.1.Final.jar包導(dǎo)入到項目中
二 、Netty服務(wù)器端開發(fā)
2.1代碼示例
1 創(chuàng)建服務(wù)啟動類HttpServer,既然作為服務(wù)器它肯定和tomcat服務(wù)器一樣有個開關(guān)來開啟/關(guān)閉服務(wù)器,可以在類中看到有個main函數(shù)。對的,它的啟動方式就是右鍵->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("服務(wù)已啟動...");
server.start(8844);
}
public void start(int port) throws Exception {
//配置服務(wù)端的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) //最大客戶端連接數(shù)為128
.childOption(ChannelOption.SO_KEEPALIVE, true);
//綁定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
//等待服務(wù)端監(jiān)聽端口關(guān)閉
f.channel().closeFuture().sync();
} finally {
//優(yōu)雅退出,釋放線程池資源
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
2創(chuàng)建ServerHandler類來負責(zé)對網(wǎng)絡(luò)事件進行讀寫操作
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包(據(jù)說5.0好像已經(jīng)被官方廢棄了)。2個版本相比較,不同版本ChannelHandler申明的方法不一樣!!
2.2 Netty5.x與4.x代碼上的差異
在4.x中,ChannelHandlerAdapter里的channelRead方法在ChannelInboundHandlerAdapter抽象類中,同5.X一樣ChannelInboundHandlerAdapter也是基于ChannelHandler接口。這樣用戶可以方便地進行業(yè)務(wù)邏輯定制,例如:打印日志、統(tǒng)一封裝異常信息、性能統(tǒng)計和消息編碼解碼等。
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,它用于對網(wǎng)絡(luò)事件進行讀寫操作,通常我們只需要關(guān)注channelRead和exceptionCaught方法。下面對這兩個方法進行簡單說明。
2.3.1 channelRead()操作
第30行做類型轉(zhuǎn)換,將msg轉(zhuǎn)換成Netty的ByteBuf對象。通過ByteBuf的readableBytes方法可以獲取緩沖區(qū)可讀的字節(jié)數(shù),根據(jù)可讀的字節(jié)數(shù)創(chuàng)建byte數(shù)組,通過ByteBuf的readBytes方法將緩沖區(qū)中的字節(jié)數(shù)組復(fù)制到新建的byte數(shù)組中,最后通過new String構(gòu)造函數(shù)獲取請求信息。這是對請求消息進行判斷,如果是“QUERY TIME ORDER”則創(chuàng)建應(yīng)答信息,通過ChannelHandlerContext的write方法異步發(fā)送應(yīng)答消息給客戶端。
2.3.2 exceptionCaught()操作
第43行,當發(fā)生異常時,關(guān)閉ChannelHandlerContext,釋放和ChannelHandlerContext相關(guān)聯(lián)的句柄等資源。
三 、Unity客戶端開發(fā)
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 ("連接服務(wù)器成功\r\n");
Thread threadReceive = new Thread(ReceiveMsg);
threadReceive.IsBackground = true;
threadReceive.Start();
}catch
{
Debug.Log ("連接服務(wù)器失敗\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);
//區(qū)分是客戶端來了,還是消息來了
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 ("服務(wù)器說:" + msg + "\r\n");
msg = "";
}
if (!string.IsNullOrEmpty(ip))
{
ip = "";
}
}
void OnApplicationQuit()
{
client.Shutdown(SocketShutdown.Both);
client.Close();
}
}
3.2 控制臺
3.2.1 啟動服務(wù)器端
3.2.2 啟動客戶端
3.2.3 控制臺(服務(wù)器)
3.2.4 控制臺(客戶端)
---------------------