为了能让计算机和计算机之间相互通信.
计算机网络是由多台不同功能的计算机,通过传输介质,连接起来.以传输协议为约束,进行通信的一个网络.
网络协议为计算机网络中进行数据交换而建立的规则,标准或约定的集合. 作用类似于普通话.
例子 0001 0010 1101 0010 1011 1111
协议:前四位表示这串数据的类型,第二个四位表示后面数据的长度…
网络可以分为四层、五层或七层,七层是最标准的。
学习计算机网络就是学习每一层的协议是什么.对于传输层,网络层,数据链路层,物理层的传输协议要求不是很高.重点是IP协议个应用层的协议.
TCP/IP是一堆协议的组合,是应用层、传输层、IP层、物理网络层的协议统称。
该层为上层(数据链路层)协议提供了一个传输数据的可靠的物理媒体。简单地说,物理层确保原始的数据可以在各种的物理媒体上传输。(比如说光纤、网线)不同的物理媒介有不同的协议。
将源自网络层的数据可靠的传输到相邻节点的目标机网络层。
基本数据单位为帧。
主要协议:以太网协议
两个重要设备名称: 网桥和交换机
**简单理解: 将数据打包成数据块,数据块在数据链路层进行传输,每个数据块就是一帧。数据传错了,丢失了都是数据链路层进行处理。
实现两个端系统之间的数据透明传送
基本数据单位为IP数据报
包含的主要协议:IP协议、IMPC协议、ARP协议、RARP协议
重要的设备:路由器
IP协议:因特网互联协议
提供不可靠的没有连接的传送服务
无连接的数据报传送、数据路由的选择,差错控制
IMPC协议:因特网控制报文协议
不负责数据的传送,包含网络是否可达和路由本身的一些消息
ping 就是ICMP协议
ARP协议: 地址解析协议
根据IP地址找到每块网卡的物理地址
RARP协议:逆地址解析协议
和ARP协议做相反的事情
根据物理地址获取IP地址
传输层负责将上层数据分段并提供端到端的、可靠后不可靠的传输以及端到端的差错控制和流量控制问题
包含主要协议:TCP协议(Transmission Control Prorocol,传输控制协议)、UDP协议(User Datagram Protocol,用户数据报协议).
重要设备: 网关
王者荣耀就是UDP协议,是不可靠传输,但速度块.
TCP是可靠协议,每次传输后都会进行验证,保证数据是准确的,按顺序收到的.
会话层,表示层,应用层都是可以在代码中进行实际的控制了
会话层管理主机之间的会话进程,既负责建立、管理、终止进程之间的会话。会话层还利用在数据中插入校验点来实现数据的同步。
表示层对上层数据或信息进行变换以保证一个主机应用层信息可以被另一个主机的应用程序理解.表示层的数据转换包括数据的加密、压缩、格式转换等。(是否加密、压缩、格式转换等都是我们可以控制的)
为操作系统或网络应用程序提供访问网络服务的接口。
会话层、表示层和应用层的重点:
数据传输基本单位为报文(可以理解为数据包)
包含的主要协议:FTP(文件传送协议)、Telnet(远程登录协议)、DNS(域名解析协议)、SMTP(邮件传送协议)、POP3协议(邮局协议)、HTTP协议(Hyper Text Transfer Protocol)。
应用场景
FTP:从论坛上下载文件
Tenlnet:在我们的电脑上登录服务器
DNS:将域名www解析成IP地址
SMTP和POP3:收发邮件
HTTP协议:浏览网页
在网络上能表明我们本机地址的格式.每台计算机都有一个公有IP和一个私有IP.
现在计算机太多啦,IPv4 32位快不够用了
最大的IP地址就是255.255.255.255
例 192.168.0.1
192.168.0是网络地址
1是主机地址
一台路由器连接了一台电脑主机地址是1,又连了一个手机,主机地址就是2,再来个平板IP地址就是192.168.0.3
一个网络下最多承载254个主机(为啥不是255,之后讲)
私有网络
A类: 10.0.0.0,保留了一个A类网络
B类:172.16.0.0 ~ 172.31.0.0,保留了16个B类网络
C类:192.168.0.0 ~ 192.168.255.0,保留了256个C类网络
不在这些范围内的就都是公有IP
例如192.167.0.1肯定是公网IP
特殊IP地址
1.主机ID全为0的地址:特指某个网段,
比如: 192.168.10.0,指192.168.10.0网段
2.主机ID全为1的地址:特指该网段的全部主机
比如: 192.168.10.255 (解释了之前为什么最多连254个主机)
3.127.0.0.1: 是本地环回地址,指本机地址,一般用来测试使用.回送地址(127.x.x.x)是本机回送地址(Loopback Address),即主机IP堆栈内部的IP地址.
4.169.253.0.0: 169.254.0.0-169.254.255.255实际上是自动私有IP地址
5.0.0.0.0: 如果计算机的IP地址和网络中的其他计算机地址冲突,使用ipconfig命令看到的就是0,0,0,0,子网掩码也是0,0,0,0 。
先回想下TCP/UDP是哪个层的传输协议?
在网络技术中,端口(Port)大致有两种意思:
网络连接需要知道对方的IP和程序监听的端口号
通过TCP发出的消息都能保证对方接收得到并且是完整的。
控制位:URG ACK PSH RST SYN FIN,共6个,每一个标志位表示一个控制能力。
简单来说是“先关读,后关写”,一共需要四个阶段。一款客户端发起关闭连接为例:
- 服务器读通道关闭
- 客户机写通道关闭
- 客户机读通道关闭
- 服务器写通道关闭
假设发送方是客户端,接收方式服务器
每次接收,接收方都会进行验证,如果发现有问题就会告诉发送方,我这个包有问题,你再发一份吧。发送方收到重发消息后就会重新发送一份数据
但是也要明白TCP和UDP的工作原理
例如我们需要获取好友列表,get就是将整个好友列表获取到,而post我们可以添加参数,比如我们只想获取前100名好友。其实两种本质区别不大
open() 建立连接
初始化HTTP请求参数,例如URL和HTTP方法,但是并不发送请求
send() 发送请求
发送HTTP请求,使用传递给open()方法的参数,以及传递给该方法的可选请求体
abort() 断开连接
取消当前响应,关闭连接并且结束任何未决的网络活动
/**
*@author Qiu
*@description http请求
*/
const {ccclass, property} = cc._decorator;
@ccclass
export default class HttpRequestTest extends cc.Component {
onLoad() {
//拿到request对象
let request = cc.loader.getXMLHttpRequest();
let url = "http://www.baidu.com";
//打开连接
request.open("GET",url,true); //第三个参数:是否是异步的
//当状态发生变化的时候
request.onreadystatechange = (ev: Event): any => {
console.log("=====request",request.readyState)
if (request.readyState == 4 //request请求已接收 状态
&& (request.status >= 200 && request.status < 300)) { //请求成功 状态码
//请求的返回结果
let response = request.responseText;
console.log("====response",response);
}
}
//发送请求
request.send();
console.log("=======request sended=========");
}
}
运行到浏览器,如下报错
原因:跨域了。因为creator本身预览的时候就是一个网页
实际上就是本地的一个http服务,就不能在去请求另一个域名的http服务,会有安全问题。
给快捷方式加上
–disable-web-security --user-data-dir=F:/MyChromeDevUserData
在目录下新建一个ts文件,并在文件里写一行代码
console.log("TypeScript Start!");
在该目录下的cmd中输入 tsc
看目录下就会生成一个js文件了
运行Js
初始化工程 npm init
在cmd里打开配置nodeJs的文件夹,还有个简单地方法:直接在文件夹里按住Shift
再右键,点击在此处打开窗口
输完 npm init
后会有一些确认信息,如果没问题一直按回车就好。
初始化完会多出来一个文件
安装WS库 npm install --save-dev ws
安装完后就会有一个node_moudles文件夹,里面是websocket依赖的所有的库
安装WS提示库 npm install @types/ws
提供typescript使用,用来代码提示
全部安装完后就可以使用WebSocket了
//start.ts
/**创建websocket服务器 */
import * as WebSocket from 'ws';
import * as http from 'http';
/**用来监听端口 */
let ws = new WebSocket.Server({port:8080});
//建立连接之后回调
ws.on('connection',(socket: WebSocket, request: http.IncomingMessage) => {
//ws是类似管理者,只负责监听接口状态
//socket是服务者,是与客户端对接的websocket
console.log("server get connection");
//让服务端给客户端发送一句话
socket.send("hello client, I'm server!");
//收到消息后回调
socket.on("message",(data: WebSocket.Data) => {
console.log("message is receive",data);
})
})
/**
*@author Qiu
*@description websocket客户端 连接传数据
*/
const {ccclass, property} = cc._decorator;
@ccclass
export default class WsClient extends cc.Component {
onLoad() {
console.log("client try to connect server");
//cocos自带websocket 直接用
//连接地址:ws:// + 本机ip + 端口号
//为啥要加ws://? 王八的屁股,规定。
let ws = new WebSocket("ws://127.0.0.1:8080");
//客户端和服务器连接上后
ws.onopen = (ev:Event) => {
ws.send("hello,server.I'm client!");
}
//收到消息后回调
ws.onmessage = (ev:Event) => {
console.log("on message",ev);
}
}
}
let ws = new WebSocket("ws://192.168.18.28:8080");
每节先记下重点,代码最后在放上吧.
做游戏,尤其是网络游戏,要注意逻辑层和UI层要分层
逻辑和数据分开写
初始棋盘上是下满了棋子的,只是隐藏起来,下了哪个就显示哪个,再根据颜色换node显隐。
设计分辨率是750x1334
这样婶滴~
断点后看数据发现有问题:
数组里明明有14个元素,但长度只显示8?
因为数组是从0开始的,而我的数组是从-7到7.
要改成从0开始
数组索引不能出现负数,不然可能会引发问题
规则:连成五个子就赢了
每下一个子,就判断该子横向/纵向/斜向(四个方向)能不能连成五个或以上
npm init -y
npm install typescript --save-dev
npm install ws --save-dev
npm install @types/ws --save-dev
tsc --init
npm install ts-node --save-dev
npm install nodemon --save-dev
index.ts
/**
*@author Qiu
*@description 服务器入口文件
*/
import WebSocket = require ('ws');
import * as http from 'http';
let ws = new WebSocket.Server({port: 8080});
ws.on("connection", (scoket: WebSocket,request: http.IncomingMessage)=>{
console.log("Server get new Connection");
} )
//打印当前时间戳
console.log(`ServerStart ${new Date().getTime()}`);
修改
"script" {
"start" : "npm run build:live",
"build:live" : "nodemon --exec ./node_modules/.bin/ts-node -- ./index.ts"
}
实时编译的作用就是改变start.js后保存再回到命令行,就会自动检测到改变并再次编译
这里标红了,说没有初始化
是因为ts的配置检查
可以在tsconfig.json里关掉
代表: MySQL , Oracle , Microsoft SQL Server
关系模式就是二维表格模型
每个实体之间都有关系
代表: MongoDB , Redis
以键值(Key-Value)来存储,且结构可变,每一个元组(数据)都可以有不一样的字段,这种就不会局限于固定的结构,可以减少一些时间和空间的开销.
使用这种方式,为了获取用户的不同信息,不需要像关系型数据库中,需要进行多表查询.仅仅需要根据Key来取出对应的Value值即可.
再接着"next",直到安装完毕.
来到安装目录里,找到启动程序,双击运行
启动后,可以看到数据库已经运行起来了,而且正在监听端口27017
创建集合 db.createCollection(NAME,OPTIONS)
OPTIONS可加选项(一般不会用到):
输入db
可以查看当前操作的数据库
插入文档 db.COLLECTION_NAME.insert(document)
其实document就是json的格式
更新文档 db.COLLECTION_NAME.update(QUERY,UPDATE,{OPTIONS})
query: update的查询条件,类似sql update查询内where后面的
update: update的对象和一些更新的操作符(如$,$inc) 等,也可以理解为sqlupdate查询内set后面的
upsert: 可选.这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入
multi: 可选.默认是false,只更新找到的第一条记录,如果为true,则把按条件查询出来的文档全部更新
writeConcern: 可选, 抛出异常的级别
可见04-安装依赖库
就在DemoServer目录里如下安装
安装MongoDB库 npm install mongodb
安装TS提示 npm install @types/mongodb
启动MongoDB服务 mongo --dbpath $YOUR_DB_PATH
mongo是MongoDB目录下的mongod.exe
$YOUR_DB_PATH代表你的MongoDB的安装路径,就是安装目录下的data文件夹
--dbpath
DemoServer/index.ts
/**
*@author Qiu
*@description 服务器入口文件
*/
import WebSocket = require ('ws');
import * as http from 'http';
import Client from './src/Client';
import ClientManager from './src/ClientManager';
import {MongoClient, MongoError } from 'mongodb';
let ws = new WebSocket.Server({port: 8080});
ws.on("connection", (scoket: WebSocket,request: http.IncomingMessage)=>{
console.log("Server get new Connection");
//当有一个客户端连接进来就创建一个客户端类,来保管socket连接
let client = new Client(scoket);
ClientManager.getInstance().addClient(client);
} )
//建立与MongoDB的连接
//相对于MongoDB,服务器就是客户端了
let dburl = "mongodb://localhost:27017";
MongoClient.connect(dburl,(error: MongoError, db: MongoClient)=>{
if (error) {
console.log("Error!",error);
return;
}
console.log("连接数据库成功!")
/**进行查询 */
let goBangDB = db.db("GoBang");
//查询user集合里的所有文档
goBangDB.collection("user").find({}).toArray((error: MongoError, result) => {
if (error) {
console.log("Error!",error);
return;
}
console.log("查询结果:",result);
//用完数据库后记得关掉连接
db.close();
});
});
//打印当前时间戳
console.log(`ServerStart ${new Date().getTime()}`);
UUID: 基于网卡MAC地址和计时器生成唯一的ID,不会重复
MD5: 根据字符串生成对应的加密串(比如:用户密码123我们需要把它存到数据库里,如果直接存123,如果数据库被攻破了,人家一看都是明文密码,不就完蛋了.所以要加密)
MD5在线破解
安装UUID库
cmd来到DemoServer目录里,也就是服务器目录里
输入npm install uuid
安装UUID提示库
npm install @types/uuid
安装MD5库
npm install ts-md5
机器里本身是自带MD5的
安装后自带安装提示
因为代码各处都会用到数据库,所以最好用单例来重写数据库
单例写的太多了,不想写了,那就弄一个单例基类
/**
*@author Qiu
*@description 单例基类
*/
export default class Singleton<T> {
private static _instance = null;
//c: {new(): T} 代表传进来的参数c有构造函数
public static getInstance<T>(c: {new(): T}): T {
//可以用this是因为该方法也是static
//而且最好使用this,如果用Singleton那所有的单例类都会是同一个_instance
if (this._instance == null) {
this._instance = new c();
}
return this._instance;
}
}
我下的uuid的版本是8.3.2和老师不同,所以老师的版本是
uuid.v1()
,而我的版本是v1()
感觉放在github能更方便看每节的进度,所以试着放到了GitHub上,这样就能看每节都改了哪里.
GitHub链接
有客户端和服务端
查看代码变化
点击注册后,会在上方文本显示返回结果
可以在数据库里查到注册的数据
当我们匹配成功后,需要切换场景,但是Gate场景下的WSClient就会随着场景被销毁,那我们想保存的连接就没有了
我们就可以使用cc.game.addPersistRootNode(this.node);
将WSClient节点保存下来,不会随着场景被销毁掉
注意,常驻节点要在场景的根节点下,和Canvas平级
当登录匹配完成,进入main场景,开始下棋
诶,WSClient咋成undefined了?不是常驻了么
监听声明周期后发现,并没有onDestroy,但是有两个onEnable
哦,对了,main场景下也有一个WSClient节点,删掉,OK!
当游戏中的玩家断开网络时存储棋局数据,当用户重新进入后检查是否有正在进行的棋局,有则拉取数据,继续游戏.
课程结束了,之后会继续把游戏写完,写完就上传到csdn里