这个例子是某网站的一个视频教程的例子,但教程没有提供代码,跟随教程敲代码根本做不出东西,所以第一课的时候有1700多人参加学习,完整听完的人数只有不到40,很多人都是第4课就放弃了,因为老师没有提供相关的工具代码,字符串解析根本无法通信,造成了听课人员的流失,其实听过这个课程之后,我觉得课程还是不错的,虽然没有提供工程样例和公共部分的代码,但如果有c#网络经验的话,完成这个例子其实也是可以的,因为,毕竟服务器端的代码是提供了,我觉得,弄到一起完全可以当成3d网游的一般框架来学习,看下我自己做的客户端的运行效果,界面的问题就不要吐槽了,因为没有什么都没提供,所以只好在自己电脑里面东拼西凑需要的东西,有些界面不太好看是很正常的,我们只是看这个的原理,
基本代码,net连接
using UnityEngine;
using System.Collections;
using System.Net.Sockets;
using System.IO;
using System;
using System.Text;
using LitJson;
using System.Threading;
using System.Collections.Generic;
public class NetWorkScript
{
private static NetWorkScript script;
private Socket socket;
public static string host ="127.0.0.1";//服务器IP地址
private int port = 10100;//服务器端口
private byte[] readM = new byte[1024];
private ByteArray ioBuff=new ByteArray();
private int dataSize;
private List<SocketModel> messages = new List<SocketModel>();
private bool isRead = false;
//获取连接对象
public static NetWorkScript getInstance() {
if (script == null) {
//第一次调用的时候 创建单例对象 并进行初始化操作
script = new NetWorkScript();
script.init();
}
return script;
}
private void init() {
try
{
//创建socket连接对象
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//连接到服务器
socket.Connect(host, port);
//连接后开始从服务器读取网络消息
socket.BeginReceive(readM, 0, 1024, SocketFlags.None, ReceiveCallBack,readM);
Debug.Log("socket session open");
}
catch (Exception e) {
//连接失败 打印异常
Debug.Log("Connect error:"+e.Message);
}
}
public void sendMessage(int type, int area, int command, string message) {
ByteArray arr= new ByteArray();
arr.WriteInt(type);
arr.WriteInt(area);
arr.WriteInt(command);
if (message != null)
{
arr.WriteInt(message.Length);
arr.WriteUTFBytes(message);
// arr.WriteUTFBytes(message);
}
else {
arr.WriteInt(0);
}
try
{
socket.Send(arr.Buffer);
}
catch {
Debug.Log("socket error");
}
}
public void onData()
{
//消息读取完成后开始解析
if(ioBuff.Length<4){
//包头为长度4的整型
isRead = false;
return;
}
dataSize=ioBuff.ReadInt();
Debug.Log("dataSize"+dataSize);
if(dataSize>ioBuff.Length-4){
//包长不够 等下个包的到来
Debug.Log("包长不够");
ioBuff.Postion=0;
isRead = false;
return;
}
ByteArray ioData=new ByteArray();
ioData.WriteBytes(ioBuff.Buffer,4,dataSize);
ioBuff.Postion+=dataSize;
int type = ioData.ReadInt();//表示消息类型 我们这里有两种
int area = ioData.ReadInt();//这里表示消息的区域码 在登录这样的服务器单例模块中 没有效果 在地图消息的时候用于区分属于哪张地图来的消息
int command = ioData.ReadInt();//模块内部协议---具体稍后描述
int len = ioData.ReadInt();
string m=null;
if (len > 0) { m = ioData.ReadUTFBytes((uint)len); }//这里开始就是读取服务器传过来的消息对象了 是一串json字符串
//转换为Socket消息模型
SocketModel model= new SocketModel();
model.type = type;
model.area = area;
model.command = command;
model.message = m;
Debug.Log(type+" "+area+" "+command+"length"+(16+len));
//消息接收完毕后,存入收到消息队列
messages.Add(model);
ByteArray bytes=new ByteArray();
bytes.WriteBytes(ioBuff.Buffer, ioBuff.Postion, ioBuff.Buffer.Length - ioBuff.Postion);
ioBuff=bytes;
onData();
//Debug.Log("插入队列后"+arr.Buffer[12+len+1]);
}
//这是读取服务器消息的回调--当有消息过来的时候BgenReceive方法会回调此函数
private void ReceiveCallBack(IAsyncResult ar)
{
int readCount = 0;
try
{
//读取消息长度
readCount = socket.EndReceive(ar);//调用这个函数来结束本次接收并返回接收到的数据长度。
Debug.Log("读取消息长度" + readCount);
byte[] bytes = new byte[readCount];//创建长度对等的bytearray用于接收
Buffer.BlockCopy(readM, 0, bytes, 0, readCount);//拷贝读取的消息到 消息接收数组
ioBuff.WriteBytes(bytes);
Debug.Log(ioBuff.Buffer.Length);
if (!isRead){
isRead = true;
onData();//消息读取完成
}
}
catch (SocketException)//出现Socket异常就关闭连接
{
socket.Close();//这个函数用来关闭客户端连接
return;
}
socket.BeginReceive(readM, 0, 1024, SocketFlags.None, ReceiveCallBack,readM);
}
public List<SocketModel> getList() {
return messages;
}
}
消息管理
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
//消息管理用于分发,消息管理器
public class MessageManager : MonoBehaviour {
private LoginHandler login;
private UserHandler user;
private MapHandler map;
// Use this for initialization
void Start () {
login=GetComponent<LoginHandler>();
user=GetComponent<UserHandler>();
map=GetComponent<MapHandler>();
}
// Update is called once per frame
void Update () {
List<SocketModel> list=NetWorkScript.getInstance().getList();
for(int i=0;i<8;i++){
if(list.Count>0){
//Debug.Log("onmessage");
SocketModel model=list[0];
//xiaoxichhuanru
OnMessage(model);
//yichudiyitiaoxiaoxi
list.RemoveAt(0);
}else{
//Debug.Log("messmaneer");
break;
}
}
}
public void OnMessage(SocketModel model){
Debug.Log("model"+model.type);
// if(model.type==520093696){
// login.OnMessage(model);
// }
switch(model.type){
case Protocol.LOGIN:
login.OnMessage(model);
break;
case Protocol.USER:
user.OnMessage(model);
break;
case Protocol.MAP:
map.OnMessage(model);
break;
default:
WindowConstans.windowList.Add(WindowConstans.SOCKET_TYPE_FAIL);
break;
}
}
}
using UnityEngine;
using System.Collections;
public class LoginHandler : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void OnMessage(SocketModel model){
switch(model.command){
case LoginProtocol.LOGIN_SRES:
//Debug.Log("chuli消息"+model.Command);
loginResult(model.message);
break;
case LoginProtocol.RES_SRES:
RegResult(model.message);
break;
}
}
private void loginResult(string message){
Debug.Log(message);
StringDTO dto=Coding<StringDTO>.decode(message);
GameInfo.ACC_ID=dto.value;
if(dto.value==null||dto.value==string.Empty){
WindowConstans.windowList.Add(WindowConstans.LOGIN_FAIL);
}else{
GameInfo.ACC_ID=dto.value;
//GameInfo.GAME_STATE=GameState.LOADING;
BroadcastMessage ("Loading",1);
}
}
private void RegResult(string message){
Debug.Log(message);
BoolDTO dto=Coding<BoolDTO>.decode(message);
if(dto.value){
WindowConstans.windowList.Add(WindowConstans.ACC_REG_OK);
}else{
WindowConstans.windowList.Add(WindowConstans.ACC_REG_FAIL);
}
}
}
using UnityEngine;
using System.Collections;
public class BoolDTO {
public bool value;
public BoolDTO(){}
public BoolDTO(bool v){
this.value=v;
}
}
运行,因为服务器使用的go语言,所以我们用liteide打开
using UnityEngine;
using System.Collections;
using LitJson;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class Coding<T> {
public static string encode(T model){
return JsonMapper.ToJson(model);
}
public static T decode(string message){
return JsonMapper.ToObject<T>(message);
}
}
启动服务器
全部的代码我就不做说明了,因为那是个很大的话题,可能篇幅要比仙剑demo还要大,这里就不找麻烦了,需要可以去看网上的一套免费课程,只不过那个课程没有提供源码和工程样例而已。
这里贴出工程地址(提取码:852e)