网络游戏是一个人的互动娱乐软件应用。因为它是交互式,当然,需要了解对方的通信。这需要通信Socket:我们今天要实现的主角即套接字。Socket的英文原义是“孔”或“插座”。正如其英文原意那样。象一个多孔插座。

一台电脑机器宛如布满各种插座的房间,每一个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电。有的则提供有线电视节目。
客户软件将插头插到不同编号的插座,就能够得到不同的服务。以下我们来看看下图,游戏中玩家移动是怎样通讯:

以下是Unity3d游戏通讯Socket实现: BaseGameSocket.cs

//@原创:dongfu.luo
using System;
using System.Collections.Generic;
using System.Net.Sockets;
public abstract class BaseGameSocket
{
//用来接收服务端发过来的缓冲Buff
private byte[] _data_buffer; //缓冲二进制数组从哪里開始读
private int _data_offset; //缓冲二进制数组读多少个byte
private int _data_size; //游戏serverIP地址
private string _ip;
private bool _is_read_head; //游戏server端口
private int _port; //要服务端发送的数据命令列表
private List<PacketOut> _send_list = new List<PacketOut>();
private Socket _sock;
private const int MAX_SEND_QUEUE = 0x3e8; //清空数据,通常是在断开重联时候调用
private void Clear()
{
this._ip = null;
this._port = 0;
this._sock = null;
List<PacketOut> list = this._send_list;
lock (list)
{
this._send_list.Clear();
}
this._is_read_head = false;
this._data_buffer = null;
this._data_offset = 0;
this._data_size = 0;
} //关闭client的Socket
public void Close()
{
try
{
Socket socket = this._sock;
this.Clear();
if ((socket != null) && socket.Connected)
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
}
catch (Exception exception)
{
UEDebug.LogError("Connect: " + exception.ToString());
this.Error(Lang.GetString("k3432"));
}
} //连接游戏服务端
public void Connect(string ip, int port)
{
try
{
this.Close();
this._ip = ip;
this._port = port;
this._sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this._sock.NoDelay = true;
this._sock.BeginConnect(this._ip, this._port, new AsyncCallback(this.OnConnect), this._sock);
}
catch (Exception exception)
{
UEDebug.LogError("Connect: " + exception.ToString());
this.Error(Lang.GetString("k3432"));
}
} //假设Socket没有关闭继续等server信息,假设有信息则開始读
private void ContinueRead()
{
try
{
if (this.IsConnected)
{
this._sock.BeginReceive(this._data_buffer, this._data_offset, this._data_size - this._data_offset, SocketFlags.None, new AsyncCallback(this.OnRead), null);
}
}
catch (Exception exception)
{
UEDebug.LogError(exception.ToString());
this.Error(Lang.GetString("k3432"));
}
} //从发送队列从不断向游戏server发送命令
private void ContinueSend()
{
PacketOut state = null;
List<PacketOut> list = this._send_list;
lock (list)
{
if (this._send_list.Count == 0)
{
return;
}
state = this._send_list[0];
if (state.start)
{
return;
}
state.start = true;
}
Socket sock = state.sock;
if (sock.Connected)
{
sock.BeginSend(state.buff, 0, state.buff.Length, SocketFlags.None, new AsyncCallback(this.OnSend), state);
}
}
//发送失败或错误,则关闭Socket通常是网络断了server关闭了
protected void Error(string msg)
{
this.Close();
this.OnError(msg);
} //程序员都知道这是析构函数
~PackedSocket()
{
this.Close();
}
public int GetSendQueueSize()
{
List<PacketOut> list = this._send_list;
lock (list)
{
return this._send_list.Count;
}
} //此函数由子类去处理
protected abstract void OnConnect(); //假设是第一次连接上了,解析消息头
private void OnConnect(IAsyncResult ret)
{
if (ret.AsyncState == this._sock)
{
try
{
this._sock.EndConnect(ret);
this.ReadHead();
this.OnConnect();
}
catch (Exception exception)
{
Debug.log(exception);
}
}
}
protected abstract void OnError(string msg);
protected abstract void OnPack(byte[] data); //有服务端信息来,開始解析,现解析信息头再解析消息体
private void OnRead(IAsyncResult ar)
{
try
{
if (this.IsConnected)
{
int num = this._sock.EndReceive(ar);
this._data_offset += num;
if (num <= 0)
{ }
else if (this._data_offset != this._data_size)
{
this.ContinueRead();
}
else if (this._is_read_head)
{
int num2 = BitConverter.ToInt32(this._data_buffer, 0);
this.ReadData(num2);
}
else
{
this.OnPack(this._data_buffer);
this.ReadHead();
}
}
}
catch (Exception exception)
{
Debug.log(exception);
}
} //假设命令发送成功。检查发送队列是否还有须要发送的命令。 假设有则继续发送
private void OnSend(IAsyncResult ar)
{
PacketOut asyncState = ar.AsyncState as PacketOut;
Socket sock = asyncState.sock;
if (sock.Connected)
{
sock.EndSend(ar);
List<PacketOut> list = this._send_list;
lock (list)
{
if (this._send_list.Contains(asyncState))
{
this._send_list.Remove(asyncState);
}
}
this.ContinueSend();
}
}
//读取消息体
private void ReadData(int pack_len)
{
this._is_read_head = false;
this._data_size = pack_len;
this._data_offset = 4;
this._data_buffer = new byte[this._data_size];
this.ContinueRead();
} //读取消息头
private void ReadHead()
{
this._is_read_head = true;
this._data_size = 4;
this._data_offset = 0;
this._data_buffer = new byte[this._data_size];
this.ContinueRead();
} //详细的发送信息,先把数据发到发送队列
public void Send(byte[] buff)
{
if (this.IsConnected)
{
PacketOut item = new PacketOut {
start = false,
buff = buff,
sock = this._sock
};
int count = 0;
List<PacketOut> list = this._send_list;
lock (list)
{
this._send_list.Add(item);
count = this._send_list.Count;
}
if (count > 0x3e8)
{ }
else
{
this.ContinueSend();
}
}
}
public bool IsClosed
{
get
{
return (this._sock == null);
}
}
public bool IsConnected
{
get
{
return ((this._sock != null) && this._sock.Connected);
}
}
private class PacketOut
{
public byte[] buff;
public Socket sock;
public bool start;
}
}

Unity3d在线游戏Socket通讯的更多相关文章

  1. Photon + Unity3D 在线游戏开发 学习笔记(两)

    本文和大家 和大家说说 Photon 解压后的目录结构 这里面最基本的我们 以后开发要用到的目录 就是  deploy目录,这个目录里 放的是要挂载的 server 当然我们的 server端也要放在 ...

  2. 【坦克大战】Unity3D多人在线游戏(泰课的坦克大战--旋转的螺丝钉)

    [坦克大战]Unity3D多人在线游戏 http://www.taikr.com/my/course/937 1.NetworkManager的介绍: 说明:选择固定生成时会自动寻找有StartPos ...

  3. 关于socket通讯,如何才能高效?

    关于socket通讯,如何才能高效? 网络通讯,一个不朽的话题,今天和一个做游戏的朋友(以前的同事聊天),他向我诉说了他的痛苦 他之前是做客户端的,无奈人力资源紧张,也开始搞服务器,他说自己的服务器总 ...

  4. 《Unity3D/2D游戏开发从0到1(第二版本)》 书稿完结总结

    前几天,个人著作<Unity3D/2D游戏开发从0到1(第二版)>经过七八个月的技术准备以及近3个月的日夜编写,在十一长假后终于完稿.今天抽出一点时间来,给广大热心小伙伴们汇报一下书籍概况 ...

  5. 《Unity3D/2D游戏开发从0到1》正式出版发行

    <Unity3D/2D游戏开发从0到1>正式出版发行 去年个人编写的Unity书籍正式在2015年7月正式发行,现在补充介绍一下个人著作.书籍信息:      书籍的名称: <Uni ...

  6. Unity3D 入门 游戏开发 Unity3D portal game development

    Unity3D 入门 游戏开发 Unity3D portal game development 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@qq.com ...

  7. Unity3D手机游戏开发

    <Unity3D手机游戏开发> 基本信息 作者: 金玺曾 出版社:清华大学出版社 ISBN:9787302325550 上架时间:2013-8-7 出版日期:2013 年8月 开本:16开 ...

  8. 客户端技术的一点思考(数据存储用SQLite, XMPP通讯用Gloox, Web交互用LibCurl, 数据打包用Protocol Buffer, socket通讯用boost asio)

    今天看到CSDN上这么一篇< 彻底放弃没落的MFC,对新人的忠告!>, 作为一个一直在Windows上搞客户端开发的C++程序员,几年前也有过类似的隐忧(参见 落伍的感觉), 现在却有一些 ...

  9. c#Socket通讯

    参考http://bbs.cskin.net/thread-326-1-1.html的大神的代码 socket封装 /// <summary> /// 自定义Socket对象 /// &l ...

随机推荐

  1. Windows下 C++ WT +VS2013配置

    引出 最近在学习使用C++,另外对建站有点兴趣,所以就找到了WT.对于WT的详细介绍,这里不讲,直接看官网就好. 此文为本人原创,转载请注明出处. 先丢出官网上的干货: WT官方网站: https:/ ...

  2. 走向DBA[MSSQL篇] 面试官最喜欢的问题 ----索引+C#面试题客串

    原文:走向DBA[MSSQL篇] 面试官最喜欢的问题 ----索引+C#面试题客串 对大量数据进行查询时,可以应用到索引技术.索引是一种特殊类型的数据库对象,它保存着数据表中一列或者多列的排序结果,有 ...

  3. 端口扫描之王——nmap入门精讲(转)

    端口扫描在百度百科上的定义是: 端口扫描是指某些别有用心的人发送一组端口扫描消息,试图以此侵入某台计算机,并了解其提供的计算机网络服务类型(这些网络服务均与端口号相关),但是端口扫描不但可以为黑客所利 ...

  4. TestNg它@Factory详细解释------如何更改参数值测试

    原创文章,版权所有所有.转载,归因:http://blog.csdn.net/wanghantong TestNg的@Factory注解从字面意思上来讲就是採用工厂的方法来创建測试数据并配合完毕測试 ...

  5. [SignalR]异常信息捕获以及处理

    原文:[SignalR]异常信息捕获以及处理 异常处理,一般采用try..catch方式处理,而signalR里面有HubPipelineModule类可以捕获到Hub内发生的异常信息. 从上图中,可 ...

  6. Struts2+Spring+Hibernate step by step 11 ssh拦截验证用户登录到集成

    注意:该系列文章从教师王健写了一部分ssh集成开发指南 引言: 之前没有引入拦截器之前,我们使用Filter过滤器验证用户是否登录,在使用struts2之后,全然能够使用拦截器,验证用户是否已经登录, ...

  7. zTree实现访问到第一节点在相同水平当前所选节点数目

    zTree实现访问到第一节点在相同水平当前所选节点数目 1.实现源代码 <!DOCTYPE html> <html> <head> <title>zTr ...

  8. 一个人ACM(我们赶上了ACM)

    时间过得真快,不经意间我已经花了两年的大学生活,现在是时候写的东西.纪念馆两年左右的时间,最近一直在玩博客.我写了一个博客.纪念我们终将逝去的青春. 就从报考说起吧.高考成绩一般,自己选择了土建类的学 ...

  9. Kinect的学习笔记发展一Kinect引进和应用

    Kinect开发学习笔记之(一)Kinect介绍和应用 zouxy09@qq.com http://blog.csdn.net/zouxy09 一.Kinect简单介绍 Kinectfor Xbox ...

  10. mysql 在创建批处理脚本日志表信息

    mysql在批处理脚本通过存储过程如下所示创建日志信息表: drop PROCEDURE if EXISTS reqSp; DELIMITER // create procedure reqSp(sT ...