[egret+pomelo]实时游戏杂记(1)
资料
准备工作
1.下载并搭建pomelo项目
2.下载pomelo捡宝项目(github上下载的,最好是看一遍git上的教程,再进行搭建会比较顺利)
3.下载的捡宝项目[Treasures] 中有简略的项目教程,可以帮助我们快速搭建和熟悉捡宝项目。
开始创建Egret项目:
因个人比较熟悉egret引擎,在论坛中找到egret pomelo的第三方库
1.客户端代码:
使用egret wing 创建游戏项目,在项目src目录下,创建network文件夹,在文件夹下新建PomeloSocket类用来链接Pomelo服务端
module network {
/**
* 链接pomelo服务端
*/
export class PomeloSocket {
public constructor() {
} private pomelo: Pomelo;
/**
* 当前正在操作的是服务端
*/
private currServer: network.PomeloService;
/**
* 服务端状态 是否开启
*/
private running: boolean = false; init() {
if (this.pomelo == null) {
this.pomelo = new Pomelo(); this.pomelo.on('server_push_message', (msg) => {
var route = msg["route"];
//根据服务端返回派发事件
{
switch (route) {
case "addEntities":
Global.dispatchEvent(events.PomeloServerEvents.ADDENTITIES, msg);
break;
case "rankUpdate":
Global.dispatchEvent(events.PomeloServerEvents.RANKUPDATE, msg);
break;
case "onUserLeave":
Global.dispatchEvent(events.PomeloServerEvents.USERLEAVE, msg);
break;
case "removeEntities":
Global.dispatchEvent(events.PomeloServerEvents.REMOVEENTITIES, msg);
break;
case "onMove":
Global.dispatchEvent(events.PomeloServerEvents.ENTITYMOVE, msg);
break;
case "onChangeStage":
Global.dispatchEvent(events.PomeloServerEvents.STAGECHANGE, msg);
break;
default:
trace("收到新的需要处理的事件~~~~~~~~~~~~~~待处理信息为:");
trace(msg);
break;
}
}
}); this.pomelo.on('onKick', (msg) => {
trace("onKick");
}); this.pomelo.on('heartbeat_timeout', () => {
trace("heartbeat_timeout");
}); this.pomelo.on('close', (e: CloseEvent) => {
trace(e.currentTarget["url"] + "的链接被断开");
});
}
} /**
* 打开服务端
* @param serverType:服务端类型
* @param host:ip
* @param port:端口
* @param callback:回调函数
* @param log:是否启用日志
*/
open(serverType: network.PomeloService, host: string, port: number, callback?: Function, log: boolean = true) {
this.pomelo.init({ host: host, port: port, log: log }, false, (succeedRes) => {
this.currServer = serverType;
this.running = true;
switch (serverType) {
case network.PomeloService.GATE:
Global.dispatchEvent(events.PomeloServerEvents.CONNECTION_GATE_SUCCEED);
break;
case network.PomeloService.CONNECTION:
Global.dispatchEvent(events.PomeloServerEvents.CONNECTION_CONNECT_SUCCEED);
break;
default:
trace("========================试图打开程序中未知服务器,请求被拒绝=========================================");
break;
}
}, (errRES) => {
switch (serverType) {
case network.PomeloService.GATE:
Global.dispatchEvent(events.PomeloServerEvents.CONNECTION_GATE_ERROR);
break;
case network.PomeloService.CONNECTION:
Global.dispatchEvent(events.PomeloServerEvents.CONNECTION_CONNECT_ERROR);
break;
default:
trace("========================试图打开程序中未知服务器,请求被拒绝=========================================");
break;
}
}, (closeRes) => {
trace("一个服务端关闭完成。");
}, null);
} /**
* 发起请求
* @param route: 路由 (服务端处理函数)
* @param msg:内容
* @param callback:回调函数
* @param thisArg:参数
*/
request(route: string, msg: any, callback: Function, thisArg?: any): void {
this.pomelo.request(route, msg, (response) => {
callback.call(thisArg, response);
});
} /**
* 通知
*/
notify(route: string, msg: any): void {
this.pomelo.notify(route, msg);
} /**
* 关闭当前服务
*/
disconnect() {
this.pomelo.disconnect();
this.running = false;
Global.dispatchEvent(events.PomeloServerEvents.DISCONNECT_SUCCEED, { currServer: this.currServer });
} /**
* 获取当前的服务端
*/
getCurrServer(): PomeloService {
return this.currServer;
}
/**
* 获取当前的服务端状态
*/
isRunning(): boolean {
return this.running;
}
}
}
在文件夹下新建PomeloService类用来链接Pomelo服务端
module network {
/**
* 服务端模块列表
*/
export class PomeloService {
public constructor() {
}
/**
* Gate模块
*/
public static GATE: string = "PomeloService_GATE";
/**
* Connect 模块操作
*/
public static CONNECTION: string = "PomeloService_CONNECTION";
}
}
在项目src目录下创建pomeloTest文件,链接pomelo相应的服务端
class PomeloTest { private connectIp: string;
private connectPort: number; public constructor() {
Global.addEventListener(events.PomeloServerEvents.CONNECTION_GATE_SUCCEED, this.onGateSucceed, this);
Global.addEventListener(events.PomeloServerEvents.CONNECTION_GATE_ERROR, this.onGateError, this);
Global.addEventListener(events.PomeloServerEvents.CONNECTION_CONNECT_SUCCEED, this.onConnectSucceed, this);
Global.addEventListener(events.PomeloServerEvents.CONNECTION_CONNECT_ERROR, this.onConnectError, this);
} connectGate() {
config.Config.pomelo.init();
config.Config.pomelo.open(network.PomeloService.GATE, config.Config.gateServer.ip, config.Config.gateServer.port);
} private onGateSucceed() {
Global.addEventListener(events.PomeloServerEvents.DISCONNECT_SUCCEED, this.onGateClosed, this); config.Config.pomelo.request("gate.gateHandler.queryEntry", { uid: config.Config.player.name }, this.onGateMsg); trace("Gate服务端链接成功");
} private onGateError() {
trace("Gate服务端链接失败");
} private onGateMsg(gate_data) {
this.connectIp = gate_data.host;
this.connectPort = gate_data.port;
config.Config.pomelo.disconnect();
trace("正在尝试链接connect服务端...");
config.Config.pomelo.open(network.PomeloService.CONNECTION, this.connectIp, this.connectPort);
} private onGateClosed() {
trace("Gate服务端成功断开链接");
// trace("正在尝试链接connect服务端...");
// config.global.pomelo.open(network.PomeloService.CONNECTION, this.connectIp, this.connectPort);
} private onConnectSucceed() {
trace("CONNECT服务端链接成功");
trace("开始注册服务端信息...");
config.Config.pomelo.request('connector.entryHandler.entry', { name: config.Config.player.name }, this.onEntryMsg);
} private onConnectError() {
trace("CONNECT服务端链接失败...");
} private onEntryMsg(entry_data) {
if (entry_data.code === 200) {
trace("注册信息成功");
trace("开始申请进入游戏...");
config.Config.pomelo.request('area.playerHandler.enterScene', { name: config.Config.player.name, playerId: entry_data.playerId }, (respose) => {
Global.dispatchEvent(events.PomeloServerEvents.MAPMSG, respose);
trace("进入游戏成功");
trace("开始解析地图信息");
});
} else {
trace("注册服务端信息出现问题,请检查提交信息");
}
} move(x: number, y: number, targetId: string) {
config.Config.pomelo.notify('area.playerHandler.move', { targetPos: { x: x, y: y }, target: targetId });
} changeStage(s: string) {
config.Config.pomelo.notify('area.playerHandler.changeStage', { S: s });
}
}
2.服务端代码
以上步骤都是准备工作,各语言间的链接方式和代码都不相同,如果没有使用egret可以使用pomelo项目中自带的web-server项目就可以轻松搭建起来的,接下来,就是服务端中的代码说明,因为本人的代码写的并不是很好,所以既然想做个好点的游戏,一步一步剖析pomelo的运行方式是很重要的一步。
2.1代码执行流程
在game-server文件夹下 直接使用pomelo命令启动app.js
pomelo start
出现这样的界面,就证明pomelo服务端启动成功了。那首先的一步 就是查看app文件中的代码
var bearcat = require('bearcat');
var pomelo = require('pomelo'); /**
* Init app for client.
*/
var app = pomelo.createApp();
当代码执行到 var app = pomelo.createApp(); 这句时,将执行game-server/node_modules/pomelo/pomelo.js 文件中的 createApp方法
var application = require('./application');
/**
* Create an pomelo application.
*
* @return {Application}
* @memberOf Pomelo
* @api public
*/
Pomelo.createApp = function (opts) {
var app = application;
//初始化
app.init(opts);
self.app = app;
return app;
};
这个方法中一共四行执行代码,第一行是引用了pomelo.js同级目录下的application.js文件,对pomelo文件中的application对象进行初始化,并将初始化的对方返回给调用该方法的app.js中的app对象。下面我们看一下,这个app.init(opts);这句话具体做了些什么呢? 我们进入application文件看一下。
前面标红的地方,是对当前application文件中的信息进行一个初始化,因为createApp()调用时并未对其传递相关的opts参数,所以,这里涉及到opts变量相关的应该是“undefined”。
appUtil.defaultConfiguration(this);是做什么的呢?我们转到【 var appUtil = require('./util/appUtil');】 game-server/node_modules/pomelo/util/appUtil.js文件查找defaultConfiguration方法。
setupEnv (环境配置)
var setupEnv = function (app, args) {
app.set(Constants.RESERVED.ENV, args.env || process.env.NODE_ENV || Constants.RESERVED.ENV_DEV, true);
};
app.set方法:(设置配置文件,并返回设置的值)
/**
* Assign `setting` to `val`, or return `setting`'s value.
*
* Example:
*
* app.set('key1', 'value1');
* app.get('key1'); // 'value1'
* app.key1; // undefined
*
* app.set('key2', 'value2', true);
* app.get('key2'); // 'value2'
* app.key2; // 'value2'
*
* @param {String} setting the setting of application
* @param {String} val the setting's value
* @param {Boolean} attach whether attach the settings to application
* @return {Server|Mixed} for chaining, or the setting value
* @memberOf Application
*/
Application.set = function (setting, val, attach) {
if (arguments.length === 1) {
return this.settings[setting];
}
this.settings[setting] = val;
if(attach) {
this[setting] = val;
}
return this;
};
loadMaster(加载master json文件)
var loadMaster = function (app) {
app.loadConfigBaseApp(Constants.RESERVED.MASTER, Constants.FILEPATH.MASTER);
app.master = app.get(Constants.RESERVED.MASTER);
};
app.loadConfigBaseApp方法:(递归方式 加载json配置文件)
/**
* Load Configure json file to settings.(support different enviroment directory & compatible for old path)
*
* @param {String} key environment key
* @param {String} val environment value
* @param {Boolean} reload whether reload after change default false
* @return {Server|Mixed} for chaining, or the setting value
* @memberOf Application
*/
Application.loadConfigBaseApp = function (key, val, reload) {
var self = this;
var env = this.get(Constants.RESERVED.ENV);
var originPath = path.join(Application.getBase(), val);
var presentPath = path.join(Application.getBase(), Constants.FILEPATH.CONFIG_DIR, env, path.basename(val));
var realPath;
if(fs.existsSync(originPath)) {
realPath = originPath;
var file = require(originPath);
if (file[env]) {
file = file[env];
}
this.set(key, file);
} else if(fs.existsSync(presentPath)) {
realPath = presentPath;
var pfile = require(presentPath);
this.set(key, pfile);
} else {
logger.error('invalid configuration with file path: %s', key);
} if(!!realPath && !!reload) {
fs.watch(realPath, function (event, filename) {
if(event === 'change') {
delete require.cache[require.resolve(realPath)];
self.loadConfigBaseApp(key, val);
}
});
}
};
master 的json文件加载完成了,下一步就是加载server的json文件
/**
* Load server info from config/servers.json.
*/
var loadServers = function (app) {
app.loadConfigBaseApp(Constants.RESERVED.SERVERS, Constants.FILEPATH.SERVER);
var servers = app.get(Constants.RESERVED.SERVERS);
var serverMap = {}, slist, i, l, server;
for (var serverType in servers) {
slist = servers[serverType];
for (i = 0, l = slist.length; i < l; i++) {
server = slist[i];
server.serverType = serverType;
if (server[Constants.RESERVED.CLUSTER_COUNT]) {
utils.loadCluster(app, server, serverMap);
continue;
}
serverMap[server.id] = server;
if (server.wsPort) {
logger.warn('wsPort is deprecated, use clientPort in frontend server instead, server: %j', server);
}
}
}
app.set(Constants.KEYWORDS.SERVER_MAP, serverMap);
};
首先加载server的json文件并存储于app中,遍历读取到的servers的serverType,通过servers[serverType]可获取到对应的服务端配置组,使用utils.loadCluster(app, server, serverMap);方法操作, game-server/node_modules/pomelo/util/util.js,下面来看一下loadCluster方法是来做什么的。
/**
* Load cluster server.
*
*/
utils.loadCluster = function(app, server, serverMap) {
var increaseFields = {};
var host = server.host;
var count = parseInt(server[Constants.RESERVED.CLUSTER_COUNT]);
var seq = app.clusterSeq[server.serverType];
if(!seq) {
seq = 0;
app.clusterSeq[server.serverType] = count;
} else {
app.clusterSeq[server.serverType] = seq + count;
} for(var key in server) {
var value = server[key].toString();
if(value.indexOf(Constants.RESERVED.CLUSTER_SIGNAL) > 0) {
var base = server[key].slice(0, -2);
increaseFields[key] = base;
}
} var clone = function(src) {
var rs = {};
for(var key in src) {
rs[key] = src[key];
}
return rs;
};
for(var i=0, l=seq; i<count; i++,l++) {
var cserver = clone(server);
cserver.id = Constants.RESERVED.CLUSTER_PREFIX + server.serverType + '-' + l;
for(var k in increaseFields) {
var v = parseInt(increaseFields[k]);
cserver[k] = v + i;
}
serverMap[cserver.id] = cserver;
}
};
这个方法可以看出,是用来做集群间的负载均衡,将设置app中的clusterSeq 属性值,以用来存储集群的ID。
processArgs(创建进程启动服务端)
/**
* Process server start command
*/
var processArgs = function (app, args) {
var serverType = args.serverType || Constants.RESERVED.MASTER;
var serverId = args.id || app.getMaster().id;
var mode = args.mode || Constants.RESERVED.CLUSTER;
var masterha = args.masterha || 'false';
var type = args.type || Constants.RESERVED.ALL;
var startId = args.startId; app.set(Constants.RESERVED.MAIN, args.main, true);
app.set(Constants.RESERVED.SERVER_TYPE, serverType, true);
app.set(Constants.RESERVED.SERVER_ID, serverId, true);
app.set(Constants.RESERVED.MODE, mode, true);
app.set(Constants.RESERVED.TYPE, type, true);
if (!!startId) {
app.set(Constants.RESERVED.STARTID, startId, true);
} if (masterha === 'true') {
app.master = args;
app.set(Constants.RESERVED.CURRENT_SERVER, args, true);
} else if (serverType !== Constants.RESERVED.MASTER) {
app.set(Constants.RESERVED.CURRENT_SERVER, args, true);
} else {
app.set(Constants.RESERVED.CURRENT_SERVER, app.getMaster(), true);
}
};
这个方法设置了app的一些属性参数值,后两步的日志文件和生命周期,放到后面的章节再研究,现在app的信息已经完善,至此,appUtil.appdefaultConfiguration方法执行完成,Pomelo.createApp执行完成,并将app返回给app.js文件中的app对象,思路回到app.js中,代码继续向下走
var bearcat = require('bearcat');
var pomelo = require('pomelo'); /**
* Init app for client.
*/
var app = pomelo.createApp(); var Configure = function() {
app.set('name', 'treasures'); app.configure('production|development', 'gate', function() {
app.set('connectorConfig', {
connector: pomelo.connectors.hybridconnector
});
}); app.configure('production|development', 'connector', function() {
app.set('connectorConfig', {
connector: pomelo.connectors.hybridconnector,
heartbeat: 100,
useDict: true,
useProtobuf: true
});
}); app.configure('production|development', 'area', function() {
var areaId = app.get('curServer').areaId;
if (!areaId || areaId < 0) {
throw new Error('load area config failed');
} var areaService = bearcat.getBean('areaService');
var dataApiUtil = bearcat.getBean('dataApiUtil');
areaService.init(dataApiUtil.area().findById(areaId));
});
}
app赋值完成之后,声明了Configure对象,这里貌似是接收消息使用的,下面来去到application.configure。
function load(path, name) {
if (name) {
return require(path + name);
}
return require(path);
} /**
* connectors
*/
Pomelo.connectors = {};
Pomelo.connectors.__defineGetter__('sioconnector', load.bind(null, './connectors/sioconnector'));
Pomelo.connectors.__defineGetter__('hybridconnector', load.bind(null, './connectors/hybridconnector'));
Pomelo.connectors.__defineGetter__('udpconnector', load.bind(null, './connectors/udpconnector'));
Pomelo.connectors.__defineGetter__('mqttconnector', load.bind(null, './connectors/mqttconnector'));
Pomelo.connectors.__defineGetter__('hybridconnector', load.bind(null, './connectors/hybridconnector')); 将 game-server/node_modules/pomelo/connectors/hybridconnector.js的引用赋值给app中的connectorConfig属性设置,这个connector可以看做是一个链接的控制器,后续的操作将围绕着这个connector对象来开展。
至此,app的创建准备工作便完成了。
下面是重要的一步,程序开始,通过start方法启动服务端
关于更多请关注 bearcat 的介绍
其它
从代码中可以看出这个app已经启动完成,在这个期间有还有一个在application文件中的对象Constants,constants文件是记录程序中的一些基础的配置。
module.exports = {
KEYWORDS: {
BEFORE_FILTER: '__befores__',
AFTER_FILTER: '__afters__',
GLOBAL_BEFORE_FILTER: '__globalBefores__',
GLOBAL_AFTER_FILTER: '__globalAfters__',
ROUTE: '__routes__',
BEFORE_STOP_HOOK: '__beforeStopHook__',
MODULE: '__modules__',
SERVER_MAP: '__serverMap__',
RPC_BEFORE_FILTER: '__rpcBefores__',
RPC_AFTER_FILTER: '__rpcAfters__',
MASTER_WATCHER: '__masterwatcher__',
MONITOR_WATCHER: '__monitorwatcher__'
}, FILEPATH: {
MASTER: '/config/master.json',
SERVER: '/config/servers.json',
CRON: '/config/crons.json',
LOG: '/config/log4js.json',
SERVER_PROTOS: '/config/serverProtos.json',
CLIENT_PROTOS: '/config/clientProtos.json',
MASTER_HA: '/config/masterha.json',
LIFECYCLE: '/lifecycle.js',
SERVER_DIR: '/app/servers/',
CONFIG_DIR: '/config'
}, DIR: {
HANDLER: 'handler',
REMOTE: 'remote',
CRON: 'cron',
LOG: 'logs',
SCRIPT: 'scripts',
EVENT: 'events',
COMPONENT: 'components'
}, RESERVED: {
BASE: 'base',
MAIN: 'main',
MASTER: 'master',
SERVERS: 'servers',
ENV: 'env',
CPU: 'cpu',
ENV_DEV: 'development',
ENV_PRO: 'production',
ALL: 'all',
SERVER_TYPE: 'serverType',
SERVER_ID: 'serverId',
CURRENT_SERVER: 'curServer',
MODE: 'mode',
TYPE: 'type',
CLUSTER: 'clusters',
STAND_ALONE: 'stand-alone',
START: 'start',
AFTER_START: 'afterStart',
CRONS: 'crons',
ERROR_HANDLER: 'errorHandler',
GLOBAL_ERROR_HANDLER: 'globalErrorHandler',
AUTO_RESTART: 'auto-restart',
RESTART_FORCE: 'restart-force',
CLUSTER_COUNT: 'clusterCount',
CLUSTER_PREFIX: 'cluster-server-',
CLUSTER_SIGNAL: '++',
RPC_ERROR_HANDLER: 'rpcErrorHandler',
SERVER: 'server',
CLIENT: 'client',
STARTID: 'startId',
STOP_SERVERS: 'stop_servers',
SSH_CONFIG_PARAMS: 'ssh_config_params'
}, COMMAND: {
TASKSET: 'taskset',
KILL: 'kill',
TASKKILL: 'taskkill',
SSH: 'ssh'
}, PLATFORM: {
WIN: 'win32',
LINUX: 'linux'
}, LIFECYCLE: {
BEFORE_STARTUP: 'beforeStartup',
BEFORE_SHUTDOWN: 'beforeShutdown',
AFTER_STARTUP: 'afterStartup',
AFTER_STARTALL: 'afterStartAll'
}, SIGNAL: {
FAIL: 0,
OK: 1
}, TIME: {
TIME_WAIT_STOP: 3 * 1000,
TIME_WAIT_KILL: 5 * 1000,
TIME_WAIT_RESTART: 5 * 1000,
TIME_WAIT_COUNTDOWN: 10 * 1000,
TIME_WAIT_MASTER_KILL: 2 * 60 * 1000,
TIME_WAIT_MONITOR_KILL: 2 * 1000,
TIME_WAIT_PING: 30 * 1000,
TIME_WAIT_MAX_PING: 5 * 60 * 1000,
DEFAULT_UDP_HEARTBEAT_TIME: 20 * 1000,
DEFAULT_UDP_HEARTBEAT_TIMEOUT: 100 * 1000,
DEFAULT_MQTT_HEARTBEAT_TIMEOUT: 90 * 1000
}
};
由于基础太差需要好好吸收一下,本次就学到这,下章继续~
[egret+pomelo]实时游戏杂记(1)的更多相关文章
- [egret+pomelo]实时游戏杂记(3)
[egret+pomelo]学习笔记(1) [egret+pomelo]学习笔记(2) [egret+pomelo]学习笔记(3) 服务端的请求流程走完了一遍,下面就该看一下,在目前的服务端中,各服务 ...
- [egret+pomelo]实时游戏杂记(2)
[egret+pomelo]学习笔记(1) [egret+pomelo]学习笔记(2) [egret+pomelo]学习笔记(3) pomelo pomelo服务端介绍(game-server/con ...
- [egret+pomelo]实时游戏杂记(4)
了解了前后端的通信,下面就可以开始自己的业务逻辑了,首先玩家输入名称,选择角色后进入游戏世界. 服务端的demo中已经提供了一些简单的角色信息和属性,数据地址位于 game-server/config ...
- [egret+pomelo]实时对战游戏杂记(5)
之前大体了解了pomelo服务端的运行的大体运行流程,下面详细的学习一下在服务端比较重要的一个容器模块bearcat,在bearcat的wiki中我们可以对其有个大概的了解,在服务端示例的代码中也大量 ...
- Egret 之 消除游戏 开发 PART 6 Egret elimination game development PART 6
Egret 之 消除游戏 开发 PART 6 Egret elimination game development PART 6 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱: ...
- Pomelo分布式游戏服务器框架
Pomelo介绍&入门 目录 前言&介绍 安装Pomelo 创建项目并启动 创建项目 项目结构说明 启动 测试连接 聊天服务器 新建gate和chat服务器 配置master.json ...
- 为什么MOBA和吃鸡类游戏不推荐用tcp协议 延迟不利于实时游戏
http://news.gamedog.cn/a/20171221/2287418.html 我们知道,不同类型的游戏因为玩法.竞技程度不一样,采用的同步算法不一样,对网络延迟的要求也不一样.例如,M ...
- 微信小游戏 查看egret的小游戏支持库版本
在开发者工具 console输入egret.wxgame
- egret之消除游戏开发
1.地图 (1)地图形状不同,尺寸不变 (2)背景图变化 2.步数 (1)不同关卡步数不同 (2)步数为01,游戏失败 3.道具 4.消除 (1)>=3可消除 (2)不可消除时,自动打乱 5.数 ...
随机推荐
- leetCode 58.Length of Last Word (最后单词的长度) 解题思路和方法
Length of Last Word Given a string s consists of upper/lower-case alphabets and empty space charact ...
- EXCEL最大行数问题:org.apache.xmlbeans.impl.store.Saver$TextSaver.resize(Saver.java:1700)
今天在使用POI导出数据时,出现如下错误: ES查询阅读推荐比: resList: start: 写入excel Exception in thread "main" java.l ...
- Nginx学习——Nginx进程间的通信
nginx进程间的通信 进程间消息传递 共享内存 共享内存还是Linux下提供的最主要的进程间通信方式,它通过mmap和shmget系统调用在内存中创建了一块连续的线性地址空间,而通过munmap或者 ...
- UNP学习笔记(第六章 I/O复用)
I/O模型 首先我们将查看UNIX下可用的5种I/O模型的基本区别: 1.阻塞式I/O 2.非阻塞式I/O 3.I/O复用(select和poll) 4.信号驱动式I/O(SIGIO) 5.异步I/O ...
- <LeetCode OJ> 189. Rotate Array
189. Rotate Array Total Accepted: 55073 Total Submissions: 278176 Difficulty: Easy Rotate an array o ...
- 【解决方法】INF file txtsetup.sif is corrupt or missing /// 使用WinSetupFromUSB来U盘安装windows2003(不使用win PE系统)
[解决方法]INF file txtsetup.sif is corrupt or missing http://blog.csdn.net/zhyl8157121/article/details/8 ...
- ExtJs5.1多选下拉框CheckComb
ExtJs这么多个版本号了.可就是不提供多选下拉框,老外不用这个玩意吗? 5都出来这么久了,新写的项目就用5吧,把曾经Extjs4.2的时搜到前人的CheckComb改巴改巴.能用了就赶紧贴上来,没有 ...
- 命令行高效操作Git,看这篇就够了
原文地址:http://blog.jboost.cn/2019/06/16/use-git.html 对于软件开发人员来说,git几乎是每天都需要接触的工具.但对于相处如此亲密的工作伙伴,你对它的了解 ...
- C#中判断某个值是否存在于枚举
我有一个枚举类型: #region -酒的种类- public enum WineType { 白酒 = 3, 葡萄酒 = 4, 洋酒 = 5, 老年陈酒 = 16, 啤酒 = 17 } #endre ...
- 新手学习JSP+Servlet笔记一
作为一个新手,初次接触jsp,servlet,习惯了后台的开发,前台的知识一窍不通,利用闲暇时间,给自己补补,从MyEclipse开始. 安装好MyEclipse之后,没有安装程序的可以下载 http ...