give me a coffee give me a tea
give me a coffeegive me a tea 粘包現象
give me
a coffeegive me a tea 分包現象
粘包和分包出現的原因是:沒有一個穩定數據結構
分割符
give me a coffee|give me a tea|
give me a coffee|
give me a tea|
長度 + 數據
16give me a coffee13give me a tea
16give me a coffee
13give me a tea
爲什麼要自定義數據包,因爲傳輸消息會出現粘包、分包等現象,導致數據不準確。
* 數據包格式
* +——----——+——-----——+——----——+——----——+——-----——+
* | 包頭 | 模塊號 | 命令號 | 長度 | 數據 |
* +——----——+——-----——+——----——+——----——+——-----——+
目錄結構:
先實現common工具類:
Request
package com.cxb.common.model;
/**
* 請求對象
*/
public class Request {
/**
* 請求模塊
*/
private short module;
/**
* 命令號
*/
private short cmd;
/**
* 數據部分
*/
private byte[] data;
public short getModule() {
return module;
}
public void setModule(short module) {
this.module = module;
}
public short getCmd() {
return cmd;
}
public void setCmd(short cmd) {
this.cmd = cmd;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public int getDataLength(){
if(data == null){
return 0;
}
return data.length;
}
}
Response
package com.cxb.common.model;
/**
* 返回對象
*/
public class Response {
/**
* 請求模塊
*/
private short module;
/**
* 命令號
*/
private short cmd;
/**
* 狀態碼
*/
private int stateCode;
/**
* 數據部分
*/
private byte[] data;
public short getModule() {
return module;
}
public void setModule(short module) {
this.module = module;
}
public short getCmd() {
return cmd;
}
public void setCmd(short cmd) {
this.cmd = cmd;
}
public int getStateCode() {
return stateCode;
}
public void setStateCode(int stateCode) {
this.stateCode = stateCode;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public int getDataLength(){
if(data == null){
return 0;
}
return data.length;
}
}
StateCode
package com.cxb.common.model;
public interface StateCode {
/**
* 成功
*/
static int SUCCESS = 0;
/**
* 失敗
*/
static int FAIL = 1;
}
ConstantValue
package com.cxb.common.constant;
public interface ConstantValue {
/**
* 包頭
*/
static final int FLAG = -659865956;
}
FightRequest
package com.cxb.common.module.fuben.request;
import com.cxb.common.serial.Serializer;
public class FightRequest extends Serializer {
/**
* 副本id
*/
private int fubenId;
/**
* 次數
*/
private int count;
public int getFubenId() {
return fubenId;
}
public void setFubenId(int fubenId) {
this.fubenId = fubenId;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
protected void read() {
this.fubenId = readInt();
this.count = readInt();
}
@Override
protected void write() {
writeInt(fubenId);
writeInt(count);
}
}
FightResponse
package com.cxb.common.module.fuben.response;
import com.cxb.common.serial.Serializer;
public class FightResponse extends Serializer {
/**
* 獲取金幣
*/
private int gold;
public int getGold() {
return gold;
}
public void setGold(int gold) {
this.gold = gold;
}
@Override
protected void read() {
this.gold = readInt();
}
@Override
protected void write() {
writeInt(gold);
}
}
BufferFactory
package com.cxb.common.serial;
import java.nio.ByteOrder;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
/**
* buff工廠
*/
public class BufferFactory {
public static ByteOrder BYTE_ORDER = ByteOrder.BIG_ENDIAN;
/**
* 獲取一個buffer
*
* @return
*/
public static ChannelBuffer getBuffer() {
ChannelBuffer dynamicBuffer = ChannelBuffers.dynamicBuffer();
return dynamicBuffer;
}
/**
* 將數據寫入buffer
* @param bytes
* @return
*/
public static ChannelBuffer getBuffer(byte[] bytes) {
ChannelBuffer copiedBuffer = ChannelBuffers.copiedBuffer(bytes);
return copiedBuffer;
}
}
Serializer
package com.cxb.common.serial;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.jboss.netty.buffer.ChannelBuffer;
/**
* 自定義序列化接口
*/
public abstract class Serializer {
public static final Charset CHARSET = Charset.forName("UTF-8");
protected ChannelBuffer writeBuffer;
protected ChannelBuffer readBuffer;
/**
* 反序列化具體實現
*/
protected abstract void read();
/**
* 序列化具體實現
*/
protected abstract void write();
/**
* 從byte數組獲取數據
* @param bytes 讀取的數組
*/
public Serializer readFromBytes(byte[] bytes) {
readBuffer = BufferFactory.getBuffer(bytes);
read();
readBuffer.clear();
return this;
}
/**
* 從buff獲取數據
* @param readBuffer
*/
public void readFromBuffer(ChannelBuffer readBuffer) {
this.readBuffer = readBuffer;
read();
}
/**
* 寫入本地buff
* @return
*/
public ChannelBuffer writeToLocalBuff(){
writeBuffer = BufferFactory.getBuffer();
write();
return writeBuffer;
}
/**
* 寫入目標buff
* @param buffer
* @return
*/
public ChannelBuffer writeToTargetBuff(ChannelBuffer buffer){
writeBuffer = buffer;
write();
return writeBuffer;
}
/**
* 返回buffer數組
*
* @return
*/
public byte[] getBytes() {
writeToLocalBuff();
byte[] bytes = null;
if (writeBuffer.writerIndex() == 0) {
bytes = new byte[0];
} else {
bytes = new byte[writeBuffer.writerIndex()];
writeBuffer.readBytes(bytes);
}
writeBuffer.clear();
return bytes;
}
public byte readByte() {
return readBuffer.readByte();
}
public short readShort() {
return readBuffer.readShort();
}
public int readInt() {
return readBuffer.readInt();
}
public long readLong() {
return readBuffer.readLong();
}
public float readFloat() {
return readBuffer.readFloat();
}
public double readDouble() {
return readBuffer.readDouble();
}
public String readString() {
int size = readBuffer.readShort();
if (size <= 0) {
return "";
}
byte[] bytes = new byte[size];
readBuffer.readBytes(bytes);
return new String(bytes, CHARSET);
}
public <T> List<T> readList(Class<T> clz) {
List<T> list = new ArrayList<>();
int size = readBuffer.readShort();
for (int i = 0; i < size; i++) {
list.add(read(clz));
}
return list;
}
public <K,V> Map<K,V> readMap(Class<K> keyClz, Class<V> valueClz) {
Map<K,V> map = new HashMap<>();
int size = readBuffer.readShort();
for (int i = 0; i < size; i++) {
K key = read(keyClz);
V value = read(valueClz);
map.put(key, value);
}
return map;
}
@SuppressWarnings("unchecked")
public <I> I read(Class<I> clz) {
Object t = null;
if ( clz == int.class || clz == Integer.class) {
t = this.readInt();
} else if (clz == byte.class || clz == Byte.class){
t = this.readByte();
} else if (clz == short.class || clz == Short.class){
t = this.readShort();
} else if (clz == long.class || clz == Long.class){
t = this.readLong();
} else if (clz == float.class || clz == Float.class){
t = readFloat();
} else if (clz == double.class || clz == Double.class){
t = readDouble();
} else if (clz == String.class ){
t = readString();
} else if (Serializer.class.isAssignableFrom(clz)){
try {
byte hasObject = this.readBuffer.readByte();
if(hasObject == 1){
Serializer temp = (Serializer)clz.newInstance();
temp.readFromBuffer(this.readBuffer);
t = temp;
}else{
t = null;
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
throw new RuntimeException(String.format("不支持類型:[%s]", clz));
}
return (I) t;
}
public Serializer writeByte(Byte value) {
writeBuffer.writeByte(value);
return this;
}
public Serializer writeShort(Short value) {
writeBuffer.writeShort(value);
return this;
}
public Serializer writeInt(Integer value) {
writeBuffer.writeInt(value);
return this;
}
public Serializer writeLong(Long value) {
writeBuffer.writeLong(value);
return this;
}
public Serializer writeFloat(Float value) {
writeBuffer.writeFloat(value);
return this;
}
public Serializer writeDouble(Double value) {
writeBuffer.writeDouble(value);
return this;
}
public <T> Serializer writeList(List<T> list) {
if (isEmpty(list)) {
writeBuffer.writeShort((short) 0);
return this;
}
writeBuffer.writeShort((short) list.size());
for (T item : list) {
writeObject(item);
}
return this;
}
public <K,V> Serializer writeMap(Map<K, V> map) {
if (isEmpty(map)) {
writeBuffer.writeShort((short) 0);
return this;
}
writeBuffer.writeShort((short) map.size());
for (Entry<K, V> entry : map.entrySet()) {
writeObject(entry.getKey());
writeObject(entry.getValue());
}
return this;
}
public Serializer writeString(String value) {
if (value == null || value.isEmpty()) {
writeShort((short) 0);
return this;
}
byte data[] = value.getBytes(CHARSET);
short len = (short) data.length;
writeBuffer.writeShort(len);
writeBuffer.writeBytes(data);
return this;
}
public Serializer writeObject(Object object) {
if(object == null){
writeByte((byte)0);
}else{
if (object instanceof Integer) {
writeInt((int) object);
return this;
}
if (object instanceof Long) {
writeLong((long) object);
return this;
}
if (object instanceof Short) {
writeShort((short) object);
return this;
}
if (object instanceof Byte) {
writeByte((byte) object);
return this;
}
if (object instanceof String) {
String value = (String) object;
writeString(value);
return this;
}
if (object instanceof Serializer) {
writeByte((byte)1);
Serializer value = (Serializer) object;
value.writeToTargetBuff(writeBuffer);
return this;
}
throw new RuntimeException("不可序列化的類型:" + object.getClass());
}
return this;
}
private <T> boolean isEmpty(Collection<T> c) {
return c == null || c.size() == 0;
}
public <K,V> boolean isEmpty(Map<K,V> c) {
return c == null || c.size() == 0;
}
}
RequestDecoder
package com.cxb.common.codc;
import com.cxb.common.constant.ConstantValue;
import com.cxb.common.model.Request;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
/**
* 請求解碼器
* <pre>
* 數據包格式
* +——----——+——-----——+——----——+——----——+——-----——+
* | 包頭 | 模塊號 | 命令號 | 長度 | 數據 |
* +——----——+——-----——+——----——+——----——+——-----——+
* </pre>
* 包頭4字節
* 模塊號2字節short
* 命令號2字節short
* 長度4字節(描述數據部分字節長度)
*
*
*/
public class RequestDecoder extends FrameDecoder{
/**
* 數據包基本長度
*/
public static int BASE_LENTH = 4 + 2 + 2 + 4;
@Override
protected Object decode(ChannelHandlerContext arg0, Channel arg1, ChannelBuffer buffer) throws Exception {
//可讀長度必須大於基本長度
if(buffer.readableBytes() >= BASE_LENTH){
//防止socket字節流攻擊
if(buffer.readableBytes() > 2048){
buffer.skipBytes(buffer.readableBytes());
}
//記錄包頭開始的index
int beginReader;
while(true){
beginReader = buffer.readerIndex();
buffer.markReaderIndex();
if(buffer.readInt() == ConstantValue.FLAG){
break;
}
//未讀到包頭,略過一個字節
buffer.resetReaderIndex();
buffer.readByte();
//長度又變得不滿足
if(buffer.readableBytes() < BASE_LENTH){
return null;
}
}
//模塊號
short module = buffer.readShort();
//命令號
short cmd = buffer.readShort();
//長度
int length = buffer.readInt();
//判斷請求數據包數據是否到齊
if(buffer.readableBytes() < length){
//還原讀指針
buffer.readerIndex(beginReader);
return null;
}
//讀取data數據
byte[] data = new byte[length];
buffer.readBytes(data);
Request request = new Request();
request.setModule(module);
request.setCmd(cmd);
request.setData(data);
//繼續往下傳遞
return request;
}
//數據包不完整,需要等待後面的包來
return null;
}
}
RequestEncoder
package com.cxb.common.codc;
import com.cxb.common.constant.ConstantValue;
import com.cxb.common.model.Request;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
/**
* 請求編碼器
* <pre>
* 數據包格式
* +——----——+——-----——+——----——+——----——+——-----——+
* | 包頭 | 模塊號 | 命令號 | 長度 | 數據 |
* +——----——+——-----——+——----——+——----——+——-----——+
* </pre>
* 包頭4字節
* 模塊號2字節short
* 命令號2字節short
* 長度4字節(描述數據部分字節長度)
*
*/
public class RequestEncoder extends OneToOneEncoder{
@Override
protected Object encode(ChannelHandlerContext context, Channel channel, Object rs) throws Exception {
Request request = (Request)(rs);
ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
//包頭
buffer.writeInt(ConstantValue.FLAG);
//module
buffer.writeShort(request.getModule());
//cmd
buffer.writeShort(request.getCmd());
//長度
buffer.writeInt(request.getDataLength());
//data
if(request.getData() != null){
buffer.writeBytes(request.getData());
}
return buffer;
}
}
ResponseDecoder
package com.cxb.common.codc;
import com.cxb.common.constant.ConstantValue;
import com.cxb.common.model.Response;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
/**
* response解碼器
* <pre>
* 數據包格式
* +——----——+——-----——+——----——+——----——+——-----——+——-----——+
* | 包頭 | 模塊號 | 命令號 | 狀態碼 | 長度 | 數據 |
* +——----——+——-----——+——----——+——----——+——-----——+——-----——+
* </pre>
* 包頭4字節
* 模塊號2字節short
* 命令號2字節short
* 長度4字節(描述數據部分字節長度)
*
*/
public class ResponseDecoder extends FrameDecoder{
/**
* 數據包基本長度
*/
public static int BASE_LENTH = 4 + 2 + 2 + 4;
@Override
protected Object decode(ChannelHandlerContext arg0, Channel arg1, ChannelBuffer buffer) throws Exception {
//可讀長度必須大於基本長度
if(buffer.readableBytes() >= BASE_LENTH){
//記錄包頭開始的index
int beginReader = buffer.readerIndex();
while(true){
if(buffer.readInt() == ConstantValue.FLAG){
break;
}
}
//模塊號
short module = buffer.readShort();
//命令號
short cmd = buffer.readShort();
//狀態碼
int stateCode = buffer.readInt();
//長度
int length = buffer.readInt();
if(buffer.readableBytes() < length){
//還原讀指針
buffer.readerIndex(beginReader);
return null;
}
byte[] data = new byte[length];
buffer.readBytes(data);
Response response = new Response();
response.setModule(module);
response.setCmd(cmd);
response.setStateCode(stateCode);
response.setData(data);
//繼續往下傳遞
return response;
}
//數據包不完整,需要等待後面的包來
return null;
}
}
ResponseEncoder
package com.cxb.common.codc;
import com.cxb.common.constant.ConstantValue;
import com.cxb.common.model.Response;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
/**
* 請求編碼器
* <pre>
* 數據包格式
* +——----——+——-----——+——----——+——----——+——-----——+——-----——+
* | 包頭 | 模塊號 | 命令號 | 狀態碼 | 長度 | 數據 |
* +——----——+——-----——+——----——+——----——+——-----——+——-----——+
* </pre>
* 包頭4字節
* 模塊號2字節short
* 命令號2字節short
* 長度4字節(描述數據部分字節長度)
*
*/
public class ResponseEncoder extends OneToOneEncoder{
@Override
protected Object encode(ChannelHandlerContext context, Channel channel, Object rs) throws Exception {
Response response = (Response)(rs);
ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
//包頭
buffer.writeInt(ConstantValue.FLAG);
//module
buffer.writeShort(response.getModule());
//cmd
buffer.writeShort(response.getCmd());
//狀態碼
buffer.writeInt(response.getStateCode());
//長度
buffer.writeInt(response.getDataLength());
//data
if(response.getData() != null){
buffer.writeBytes(response.getData());
}
return buffer;
}
}
接下來實現client
HiHandler
package com.cxb.client;
import com.cxb.common.model.Response;
import com.cxb.common.module.fuben.response.FightResponse;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
/**
* 消息接受處理類
*/
public class HiHandler extends SimpleChannelHandler {
/**
* 接收消息
*/
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
Response message = (Response)e.getMessage();
if(message.getModule() == 1){
if(message.getCmd() == 1){
FightResponse fightResponse = new FightResponse();
fightResponse.readFromBytes(message.getData());
System.out.println("gold:" + fightResponse.getGold());
}else if(message.getCmd() == 2){
}
}else if (message.getModule() == 1){
}
}
/**
* 捕獲異常
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
System.out.println("exceptionCaught");
super.exceptionCaught(ctx, e);
}
/**
* 新連接
*/
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("channelConnected");
super.channelConnected(ctx, e);
}
/**
* 必須是鏈接已經建立,關閉通道的時候纔會觸發
*/
@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("channelDisconnected");
super.channelDisconnected(ctx, e);
}
/**
* channel關閉的時候觸發
*/
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("channelClosed");
super.channelClosed(ctx, e);
}
}
Client
package com.cxb.client;
import java.net.InetSocketAddress;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.cxb.common.codc.RequestEncoder;
import com.cxb.common.codc.ResponseDecoder;
import com.cxb.common.model.Request;
import com.cxb.common.module.fuben.request.FightRequest;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
/**
* netty客戶端入門
*/
public class Client {
public static void main(String[] args) throws InterruptedException {
//服務類
ClientBootstrap bootstrap = new ClientBootstrap();
//線程池
ExecutorService boss = Executors.newCachedThreadPool();
ExecutorService worker = Executors.newCachedThreadPool();
//socket工廠
bootstrap.setFactory(new NioClientSocketChannelFactory(boss, worker));
//管道工廠
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new ResponseDecoder());
pipeline.addLast("encoder", new RequestEncoder());
pipeline.addLast("hiHandler", new HiHandler());
return pipeline;
}
});
//連接服務端
ChannelFuture connect = bootstrap.connect(new InetSocketAddress("127.0.0.1", 8008));
Channel channel = connect.sync().getChannel();
System.out.println("client start");
Scanner scanner = new Scanner(System.in);
while(true){
System.out.println("請輸入fubenId:");
int fubenId = Integer.parseInt(scanner.nextLine());
System.out.println("請輸入count:");
int count = Integer.parseInt(scanner.nextLine());
FightRequest fightRequest = new FightRequest();
fightRequest.setFubenId(fubenId);
fightRequest.setCount(count);
Request request = new Request();
request.setModule((short) 1);
request.setCmd((short) 1);
request.setData(fightRequest.getBytes());
//發送請求
channel.write(request);
}
}
}
服務端
HelloHandler
package com.cxb.server;
import com.cxb.common.model.Request;
import com.cxb.common.model.Response;
import com.cxb.common.model.StateCode;
import com.cxb.common.module.fuben.request.FightRequest;
import com.cxb.common.module.fuben.response.FightResponse;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
/**
* 消息接受處理類
*/
public class HelloHandler extends SimpleChannelHandler {
/**
* 接收消息
*/
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
Request message = (Request)e.getMessage();
if(message.getModule() == 1){
if(message.getCmd() == 1){
FightRequest fightRequest = new FightRequest();
fightRequest.readFromBytes(message.getData());
System.out.println("fubenId:" +fightRequest.getFubenId() + " " + "count:" + fightRequest.getCount());
//回寫數據
FightResponse fightResponse = new FightResponse();
fightResponse.setGold(9999);
Response response = new Response();
response.setModule((short) 1);
response.setCmd((short) 1);
response.setStateCode(StateCode.SUCCESS);
response.setData(fightResponse.getBytes());
ctx.getChannel().write(response);
}else if(message.getCmd() == 2){
}
}else if (message.getModule() == 1){
}
}
/**
* 捕獲異常
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
System.out.println("exceptionCaught");
super.exceptionCaught(ctx, e);
}
/**
* 新連接
*/
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("channelConnected");
super.channelConnected(ctx, e);
}
/**
* 必須是鏈接已經建立,關閉通道的時候纔會觸發
*/
@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("channelDisconnected");
super.channelDisconnected(ctx, e);
}
/**
* channel關閉的時候觸發
*/
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("channelClosed");
super.channelClosed(ctx, e);
}
}
Server
package com.cxb.server;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.cxb.common.codc.RequestDecoder;
import com.cxb.common.codc.ResponseEncoder;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
/**
* netty服務端入門
*/
public class Server {
public static void main(String[] args) {
//服務類
ServerBootstrap bootstrap = new ServerBootstrap();
//boss線程監聽端口,worker線程負責數據讀寫
ExecutorService boss = Executors.newCachedThreadPool();
ExecutorService worker = Executors.newCachedThreadPool();
//設置niosocket工廠
bootstrap.setFactory(new NioServerSocketChannelFactory(boss, worker));
//設置管道的工廠
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new RequestDecoder());
pipeline.addLast("encoder", new ResponseEncoder());
pipeline.addLast("helloHandler", new HelloHandler());
return pipeline;
}
});
bootstrap.bind(new InetSocketAddress(8008));
System.out.println("start...");
}
}
測試,啓動客戶端和服務端
驗證成功!
代碼下載