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;
}
}
Unity3d在线游戏Socket通讯的更多相关文章
- Photon + Unity3D 在线游戏开发 学习笔记(两)
本文和大家 和大家说说 Photon 解压后的目录结构 这里面最基本的我们 以后开发要用到的目录 就是 deploy目录,这个目录里 放的是要挂载的 server 当然我们的 server端也要放在 ...
- 【坦克大战】Unity3D多人在线游戏(泰课的坦克大战--旋转的螺丝钉)
[坦克大战]Unity3D多人在线游戏 http://www.taikr.com/my/course/937 1.NetworkManager的介绍: 说明:选择固定生成时会自动寻找有StartPos ...
- 关于socket通讯,如何才能高效?
关于socket通讯,如何才能高效? 网络通讯,一个不朽的话题,今天和一个做游戏的朋友(以前的同事聊天),他向我诉说了他的痛苦 他之前是做客户端的,无奈人力资源紧张,也开始搞服务器,他说自己的服务器总 ...
- 《Unity3D/2D游戏开发从0到1(第二版本)》 书稿完结总结
前几天,个人著作<Unity3D/2D游戏开发从0到1(第二版)>经过七八个月的技术准备以及近3个月的日夜编写,在十一长假后终于完稿.今天抽出一点时间来,给广大热心小伙伴们汇报一下书籍概况 ...
- 《Unity3D/2D游戏开发从0到1》正式出版发行
<Unity3D/2D游戏开发从0到1>正式出版发行 去年个人编写的Unity书籍正式在2015年7月正式发行,现在补充介绍一下个人著作.书籍信息: 书籍的名称: <Uni ...
- Unity3D 入门 游戏开发 Unity3D portal game development
Unity3D 入门 游戏开发 Unity3D portal game development 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@qq.com ...
- Unity3D手机游戏开发
<Unity3D手机游戏开发> 基本信息 作者: 金玺曾 出版社:清华大学出版社 ISBN:9787302325550 上架时间:2013-8-7 出版日期:2013 年8月 开本:16开 ...
- 客户端技术的一点思考(数据存储用SQLite, XMPP通讯用Gloox, Web交互用LibCurl, 数据打包用Protocol Buffer, socket通讯用boost asio)
今天看到CSDN上这么一篇< 彻底放弃没落的MFC,对新人的忠告!>, 作为一个一直在Windows上搞客户端开发的C++程序员,几年前也有过类似的隐忧(参见 落伍的感觉), 现在却有一些 ...
- c#Socket通讯
参考http://bbs.cskin.net/thread-326-1-1.html的大神的代码 socket封装 /// <summary> /// 自定义Socket对象 /// &l ...
随机推荐
- Programming from the ground up(0)
这本书的英文版是开源.我读了一些.但是,支持的英语水平不走太,然后还有那些谁译的书,但感觉不是太干脆翻译,在一些地方难以清除作者的思路,所以,我要揍很难理解他自己翻译一下原来的地方,这将更好地了解一点 ...
- Visual Studio Team Services使用教程--Readers tfs组成员添加
- linux如果不进入window磁盘
最近,在windows8.1下安装ubuntu14.04,在windows沉睡.开放时间和进入选择进入系统选项,当时没有引起重视.他选择进入linux系统.但进入后,发现无法进入windows磁盘,百 ...
- IOS 数据库管理系统(SQLite)
嵌入式数据库 SQLite嵌入式数据库的优势 1.支持事件,你并不需要配置,无需安装,不需要管理员 2.支持部分脂肪SQL92 3.完整的数据库被存储在磁盘上的文件的顶部,相同的数据库文件可以在不同机 ...
- 【J2SE】java实现简单照片查看器
程序执行结果: project结构图: 程序代码: import java.awt.BorderLayout; import java.awt.FileDialog; import java.awt. ...
- SQL Server审计功能入门:SQL Server审核 (SQL Server Audit)
原文:SQL Server审计功能入门:SQL Server审核 (SQL Server Audit) 介绍 Audit是SQL Server 2008之后才有的功能,它能告诉你"谁什么时候 ...
- TP-Link WR842N VPN错误619 不能建立到远程计算机的连接
一直在用Tenacy这个VPN,不限时间不限流量的,可是近期发现链接VPN总是失败.在网上查了一下,发现居然是路由器的问题!回忆一下果然是路由器出事儿了,换这个842N之前,一直是能够链接VPN的,所 ...
- Hadoop 它们的定义Writable NullpointerException
Hadoop周边环境:Hadoop2.4 定义中的Hadoop的Writable时间,有时你需要使用数组,而不是简单的单一值或串.例如,下面的代码: package test; import java ...
- 好记心不如烂笔头,ssh登录 The authenticity of host 192.168.0.xxx can't be established. 的问题
用ssh登录一个机器(换过ip地址),提示输入yes后,屏幕不断出现y,仅仅有按ctrl + c结束 错误是:The authenticity of host 192.168.0.xxx can't ...
- HDOJ--4821--String【弦hash】
联系:http://acm.hdu.edu.cn/showproblem.php?pid=4821 题意:给一个字符串,选m个长度为l的子串组成新的串.要求这m个子串互不同样,问有多少种组合. 字符串 ...