Unity3d在线游戏Socket通讯

网络游戏是一个人的互动娱乐软件应用。因为它是交互式,当然,需要了解对方的通信。这需要通信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;
    }
}
时间: 2024-10-09 11:02:15

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

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

本文和大家 和大家说说 Photon 解压后的目录结构 这里面最基本的我们 以后开发要用到的目录 就是  deploy目录,这个目录里 放的是要挂载的 server 当然我们的 server端也要放在里面,它里面有自带的一些server端应用 doc  顾名思义  帮助不大 lib 我们开发server 时候在 Visual Studio 要引用的库文件 src-server 我们可以顾名思义哈萨克斯坦 这里这个权利哈 版权声明:本文博客原创文章,博客,未经同意,不得转载.

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

书籍信息:   书籍的名称: <Unity3D/2D 游戏开发从0到1>   书号(ISBN): 978-7-121-26239-5    出版社: 电子工业出版社   发行时间:2015年7月1日 写作背景:    2015年6月30日我收到电子工业出版社张迪老师寄来,正式发行的<Unity3D/2D游戏开发从0到1>书籍.这本凝结着大半年心血的作品让我感慨万千.   本人从事游戏.软件与教学十多年,一直梦想可以进一步服务于全国广大的游戏与软件从业开发人员.14年下半年电子工业出

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

今天看到CSDN上这么一篇< 彻底放弃没落的MFC,对新人的忠告!>, 作为一个一直在Windows上搞客户端开发的C++程序员,几年前也有过类似的隐忧(参见 落伍的感觉), 现在却有一些不同的想法. 首先,个人职业发展是否成功, 技术只是其中一小块,尤其是在大公司, 更多的是依靠所谓的软实力.作为一个对技术有追求的工匠,我们下面重点说技术相关的. 现在回头看计算机行业的发展,我们看到不同的发展阶段: 1. PC时代,这个时代离我们并不遥远, 也有是2000年前后, 该时代最鲜明的特征是Win

Unity3D 入门 游戏开发 Unity3D portal game development

Unity3D 入门 游戏开发 Unity3D portal game development 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:[email protected] E-mail: 313134555 @qq.com 视频学习链接:Video learning link: [教学视频]深入浅出Unity3D--第一篇-何韬-CSDN学院-在线学习教程 u3d 4.6 c#  性能上 比 java脚本 好一些 C # is better than a Java

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

去年个人编写的Unity书籍正式在2015年7月正式发行,现在补充介绍一下个人著作.书籍信息: 书籍的名称: <Unity3D/2D 游戏开发从0到1> 书号(ISBN): 978-7-121-26239-5 出版社: 电子工业出版社 发行时间:2015年7月1日 写作背景: 2015年6月30日我收到电子工业出版社张迪老师寄来,正式发行的<Unity3D/2D游戏开发从0到1>书籍.这本凝结着大半年心血的作品让我感慨万千. 本人从事游戏.软件与教学十多年,一直梦想可以进一步服务于

Unity3D RPG游戏制作 随堂笔记(2)

大家好,我是冰糖咖啡,来自山东的一名大三的Unity3D学习者,最近在通过泰课在线学习<Unity3D RPG游戏开发>,写下一些学到的东西: Water(Basic) 为场景中的河流添加水面,这里需要提醒一下,在Unity3D 5.0版本以前可以直接在菜单栏中找到Asset,单击它asset,再单击import package ,最后单击water(basic).弹出对话框,直接单击import,导入资源包. 而在Unity3D 5.x版本中,将水资源包挪到Environment中,所以需要

大型多人在线游戏服务器架构设计

由于大型多人在线游戏服务器理论上需要支持无限多的玩家,所以对服务器端是一个非常大的考验.服务器必须是安全的,可维护性高的,可伸缩性高的,可负载均衡的,支持高并发请求的.面对这些需求,我们在设计服务器的时候就需要慎重考虑,特别是架构的设计,如果前期设计不好,最后面临的很可能是重构. 一款游戏服务器的架构都是慢慢从小变大的,不可能一下子就上来一个完善的服务器构架,目前流行的说法是游戏先上线,再扩展.所以说我们在做架构的时候,一定要把底层的基础组件做好,方便以后扩展,但是刚开始的时候留出一些接口,并不

unity3d 赛车游戏——复位点检测

一直没有时间写博客 昨天我的CarWaypoints插件也告一段落了 今年没回家,过年就我一个人 挺无聊的,那就休息一天写几篇博客吧 我的代码可能很少,但是思路很重要 希望不懂的朋友别只copy代码 赛车游戏的话赛车难免会冲出跑道.掉入水坑.卡在障碍物上....等情况 那么问题来了,遇到这些情况怎么办呢? 玩家玩得好好的,难道就因为遇到这些情况要退出游戏重新进入吗? 那当然是不现实的,要是我的话果断卸载游戏 还要骂一句做游戏的人是脑残啊 我想你不希望玩家骂你是脑残吧,哈哈哈 新技能,赶快GET起

快节奏多人在线游戏网络入门系列教程(3):实体插值

简介 在第一篇文章中,我们介绍了权威服务器的概念和其防作弊的能力.然而,该框架简单的实现会导致一系列糟糕的响应性和可玩性.在第二篇文章中,我们介绍了客户端预测与服务器协调技术来克服这些缺点. 这两篇文章使用的技术使得单一玩家在在网络游戏上能够获取和单机游戏一样的游戏体验,即使在因特网具有一定延迟的情况下. 本文,我们将讨论多个玩家连接同一个权威服务器的情况. 服务器时间步长 上一篇文章中服务器的行为非常简单--读取客户端输入,更新游戏状态,返回给客户端.然而当有多个客户端存在的情况下,服务器的主