公司游戏端和游戏后台端的通信用的是基于NIO的socket框架。
由于从未使用过socket 然后最近要接触到这方面的工作了,拿到代码看了一下流程。
整理如下:
socket中三次握手,四次挥手的体现
三次握手
a.客户端向服务器发送一个SYN J
b.服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
c.客户端再想服务器发一个确认ACK K+1
当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。
总结:客户端的connect在三次握手的第二个次返回,而服务器端的accept在三次握手的第三次返回
四次握手
某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;
一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;
接收到这个FIN的源发送端TCP对它进行确认。 这样每个方向上都有一个FIN和ACK
1.Naga框架中的常用类
NIOService :创建NIO流的服务对象
NIOServerSocket :相当于IO中ServerSocket
NIOSocket 相当于IO中Socket
ServerSocketObserverAdapter:服务器端ServerSocket的监听适配器
SocketObserver 客户端ServerSocket的监听事件
SocketObserverAdapter:客户端SOcket器端的监听适配器
2.服务端代码
SocketServerListener.java
//由于Socket应该基于项目启动,所以在ServletContextListener中重写contextInitialized方法
public class SocketServerListener implements
javax.servlet.ServletContextListener {
private static final Log logger = LogFactory.getLog(SocketServerListener.class);
private static NIOServerSocket serverSocket = null;
private static NIOService service = null;
@Override
public void contextDestroyed(ServletContextEvent arg0) {
// logger.info("[Application]:Close Socket Server.");
if(serverSocket != null && serverSocket.isOpen()){
logger.info("[Application]:Close Socket...");
serverSocket.close();
}
if(service != null && service.isOpen()){
logger.info("[Application]:Close Socket Service...");
service.close();
}
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
// logger.info("Init Socket Server...");
ServletContext servletContext = arg0.getServletContext();
WebApplicationContext webApplicationContext = (WebApplicationContext) servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
ContextHolder.getInstance().setApplicationContext(webApplicationContext);
new Thread() {
public void run() {
try {
//创建一个NIOService服务对像
service = new NIOService();
//创建一个NIOServerSocket服务端对象 端口号:700
serverSocket = service.openServerSocket(7000);
//服务端添加相关的监听器 用于监听socket信息
serverSocket.listen(new ServerSocketObserverAdapter() {
//当连接成功时
public void newConnection(NIOSocket nioSocket) {
// logger.info("Received connection: " + nioSocket);
try {
//保证Socket还是连接着的
nioSocket.socket().setKeepAlive(true);
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Listen on the connection.
//监听到Socket信息是处理方式
//备注此处采用的是Socket的观察者适配器
nioSocket.listen(new SocketObserverAdapter());
}
});
serverSocket.setConnectionAcceptor(ConnectionAcceptor.ALLOW);
while(true){
service.selectBlocking();
}
} catch (Exception e) {
if(serverSocket != null){
serverSocket.close();
}
logger.error(e.getMessage());
}
}
}.start();
}
}
SocketObserverAdapter.java
/**
*
*/
package com.ceapon.fire.ams.network;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import naga.NIOSocket;
import naga.SocketObserver;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author shixing
*
*/
public class SocketObserverAdapter implements SocketObserver {
private static final Log logger = LogFactory.getLog(SocketObserverAdapter.class);
private ByteBuffer byteBuffer;
public SocketObserverAdapter(){
byteBuffer = ByteBuffer.allocate(8192);//分配了一段内存空间,作为缓存
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
}
/**
* 连接发生异常时候的处理
**/
public void connectionBroken(NIOSocket nioSocket, Exception exception)
{
byteBuffer = ByteBuffer.allocate(0);
byteBuffer = null;
//SessionCache.getInstance().removeSession(nioSocket.getIp(), nioSocket.getPort());
//logger.info("Client "+ nioSocket.getTimeOpen()+ " :" + nioSocket.getIp() + ":" + nioSocket.getPort() + " disconnected.");
}
/**
* 接收数据包的信息
**/
public void packetReceived(NIOSocket socket, byte[] packet)
{
try {
byteBuffer.put(packet);
//InputStream inputStream = new ByteArrayInputStream(byteBuffer.array());
//MessageInputStream mis = new MessageInputStream(inputStream);
short re
}
catch(Exception e)
{
}
}
public void packetReceived2(NIOSocket socket, byte[] packet)
{
// logger.info("[WARN]-------socket start work-----------");
//logger.info("SocketObserverAdapter packetReceived...");
try {
InputStream inputStream = new ByteArrayInputStream(packet);
MessageInputStream mis = new MessageInputStream(inputStream);
MessageOutputStream os = new MessageOutputStream(socket.socket().getOutputStream());
//新开启一个线程去处理业务
HandlerUtil.handle(new LoginHandlerForTest());
// Create the outgoing packet.
out.flush();
//写数据到客户端
socket.write(byteArrayOutputStream.toByteArray());
// Close after the packet has finished writing.
//关闭当前写入流
socket.closeAfterWrite();
}catch (Exception e) {
socket.close();
e.printStackTrace();
}
}
/**
*连接打开时候执行的操作
**/
public void connectionOpened(NIOSocket nioSocket)
{
// logger.info("Receive MSG_ID_LOGIN_GSAS_PONG...");
}
public void packetSent(NIOSocket socket, Object tag)
{
}
/**
* 写数据的到客户端 相当于返回信息给发送方
* @param socket
* @param packet
*/
@SuppressWarnings("unused")
public void notifyReadPacket(NIOSocket socket, byte[] packet)
{
socket.write(packet);
}
}
接下来是模拟客户端向服务器端发送channelID 返回 content
HandlerUtil.java
public class HandlerUtil {
public static void handle(Runnable handler){
new Thread(handler).start();
}
}
testHandler .java
public class testHandler implements Runnable {
private static final Log logger = LogFactory.getLog(testHandler .class);
private testRequset request;
private NIOSocket socket;
public testHandler (NIOSocket socket,testRequset request){
this.request = request;
this.socket = socket;
}
@Override
public void run() {
try {
MessageOutputStream os = new MessageOutputStream(socket.socket().getOutputStream());
//业务逻辑
String content="你是傻蛋";
testResponse response= new testResponse ();
response.setContent(content);
Protocol.sendResponse(os, response);
socket.write(os.getBuf());
os.clear();
}
catch(Exception e)
{
logger.info("testHandler error"+JSONValue.toJSONString(request));
e.printStackTrace();
}
}
}
testRequset .java
private int channelID;
public int getChannelID() {
return channelID;
}
public void setChannelID(int channelID) {
this.channelID = channelID;
}
//读写数据
public boolean encode(MessageOutputStream os) {
super.encode(os);
os.write(channelID);
return true;
}
public boolean decode(MessageInputStream is) throws Exception {
super.decode(is);
channelID = is.read(channelID);
return true;
}
testResponse.java
private String content;
public boolean encode(MessageOutputStream os) {
super.encode(os);
os.write(content);
return true;
}
public boolean decode(MessageInputStream is) throws Exception {
super.decode(is);
content = is.read(content);
return true;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
客户端
由于我暂时还没看到客户端的写法 于是查阅资料,如果有错,在沟通
package com.easyway.space.sockets.naga;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.InetAddress;
import naga.NIOService;
import naga.NIOSocket;
import naga.SocketObserver;
import naga.SocketObserverAdapter;
import naga.packetreader.RegularPacketReader;
import naga.packetwriter.RegularPacketWriter;
/**
* Naga的远程客户端验证 的客户端的代码
* @author longgangbai
*
*/
public class NagaClient
{
NagaClient()
{}
/**
* Make a login request to the server.
*
* @param args assumed to be 4 strings representing host, port, account and password.
*/
public static void main(String... args)
{
try
{
// Parse arguments.
String hostName = "localhost";
int port = 8090;
//设置登录的账号信息
String account ="Admin";
String password = "password";
// Prepare the login packet, packing two UTF strings together
// using a data output stream.
//将信息写入流中发送到服务端
ByteArrayOutputStream stream = new ByteArrayOutputStream();
DataOutputStream dataStream = new DataOutputStream(stream);
dataStream.writeUTF(account);
dataStream.writeUTF(password);
dataStream.flush();
final byte[] content = stream.toByteArray();
dataStream.close();
// Start up the service.
//设置NIOService的服务对象
NIOService service = new NIOService();
//获取相关的ip的信息
InetAddress intaddress=InetAddress.getByName(hostName);
// Open our socket.
//打开当前请求的Socket的对象
NIOSocket socket = service.openSocket(intaddress.getHostAddress(), port);
// Use regular 1 byte header reader/writer
//设置读写信息包的信息
socket.setPacketReader(new RegularPacketReader(1, true));
socket.setPacketWriter(new RegularPacketWriter(1, true));
// Start listening to the socket.
//添加相关的监听事件用于监听客户端
socket.listen(new SocketObserver()
{
/** A null object used as the default observer */
SocketObserver NULL = new SocketObserverAdapter();
/**
* 连接成功时的相关的操作
*/
public void connectionOpened(NIOSocket nioSocket)
{
System.out.println("Sending login...");
nioSocket.write(content);
}
//用于接收服务端的信息
public void packetReceived(NIOSocket socket, byte[] packet)
{
try
{
// Read the UTF-reply and print it.
String reply = new DataInputStream(new ByteArrayInputStream(packet)).readUTF();
System.out.println("Reply was: " + reply);
// Exit the program.
System.exit(0);
}
catch (Exception e)
{
e.printStackTrace();
}
}
//用于接受服务端连接发生异常时的处理方式
public void connectionBroken(NIOSocket nioSocket, Exception exception)
{
System.out.println("Connection failed.");
// Exit the program.
System.exit(-1);
}
});
// Read IO until process exits.
//采用阻塞式读取IO信息,知道进程退出
while (true)
{
service.selectBlocking();
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}