网络游戏是一个人的互动娱乐软件应用。因为它是交互式,当然,需要了解对方的通信。这需要通信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. 动态生成Zip

    动态生成Zip文档   通过前面一篇烂文的介绍,大伙儿知道,ZipArchive类表示一个zip文档实例,除了用上一篇文章中所列的方法来读写zip文件外,还可以直接通过ZipArchive类,动态生成 ...

  2. Coco2d-x android win7 Python 游戏开发环境的搭建

    1:我用的电脑配置 win7 3 核 内存8G 桌面.一直想学习Coco2d 游戏开发,所以,一个星期后,需要找到,最终建立了一个良好的环境 2:我使用的版本号版本号,至于建筑android开发环境略 ...

  3. UVA 1364 - Knights of the Round Table (获得双连接组件 + 二部图推理染色)

    尤其是不要谈了些什么,我想A这个问题! FML啊.....! 题意来自 kuangbin: 亚瑟王要在圆桌上召开骑士会议.为了不引发骑士之间的冲突. 而且可以让会议的议题有令人惬意的结果,每次开会前都 ...

  4. 【SSH三框架】Hibernate基金会七:许多附属业务

    相对于上述一关系,在这里,下一个一对多关系说明. 另外,在上述.我们描述了许多人描述的一一对应关系.在关系数据库是多对一的关系.但也有许多关系. 但,只知道它是不够的,Hibernate它是一种面向对 ...

  5. 华为-on练习--重复的字符过滤

    称号: 请写一个字符串过滤程序,如果使用多个相同的字符出现在字符串中,字符首次出现在非过滤,. 比方字符串"abacacde"过滤结果为"abcde". 演示样 ...

  6. Web版RSS阅读器(二)——使用dTree树形加载rss订阅分组列表

    在上一边博客<Web版RSS阅读器(一)——dom4j读取xml(opml)文件>中已经讲过如何读取rss订阅文件了.这次就把订阅的文件读取到页面上,使用树形结构进行加载显示. 不打算使用 ...

  7. Hadoop之——HBase注意事项

    转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46447573 1.HBase(NoSQL)的数据模型 1.1 表(table) 存 ...

  8. Android 滑动界面实现---Scroller类别 从源代码和开发文档了解(让你的移动布局)

    在android学习,行动互动是软件的重要组成部分,其中Scroller是提供了拖动效果的类,在网上.比方说一些Launcher实现滑屏都能够通过这个类去实现.. 样例相关博文:Android 仿 窗 ...

  9. DFS-leetcode Combination Sum I/I I

    深度优先搜索(DFS)它是一个搜索算法.第一次接触DFS它应该是一个二进制树的遍历内部,二叉树预订.序和后序实际上属于深度遍历-first.在本质上,深度优先搜索,遍历中则看到了更纯正的深度优先搜索算 ...

  10. SQL Prompt——SQL智能提示插件

    数据库是大家在项目开发中肯定会用到的,C#项目用的最多的就是微软自家的SQL Server了.不可否认,微软的Visual Studio开发平台很好用,很直观的体现就是智能提示.敲几个字符,相关的信息 ...