数据库日志文件被删除恢复方法

正常的日志文件被删除,直接附加的时候删掉附加日志文件,会自动创建一个日志。但这次遇到了特殊情况,在日志文件被删除之前,数据库主文件变成了只读,所以附加时会报主文件为只读,无法创建日志文件。通过以下操作可恢复。

  1. 新建一个同名数据库
  2. 不用分离或脱机,直接停止数据库服务,然后覆盖新建的数据库主文件
  3. 启动数据库服务,然后运行SQL
  4. alter database 无日志文件的数据库名称 set emergency 设置为紧急状态。
  5. alter database 无日志文件的数据库名称 set single_user 设置为单用户模式。
  6. dbcc checkdb('无日志文件的数据库名称',REPAIR_ALLOW_DATA_LOSS) 检查并重建日志文件,如报错再运行 dbcc checkdb('无日志文件的数据库名称',REPAIR_REBUILD) 进行恢复。没错误,跳过
  7. alter database 无日志文件的数据库名称 set multi_user 恢复成多用户模式
  8. 刷新数据库,你就可以看到已经修复好的数据库了。
2017/11/25 posted in  数据库

利用Vue、Socket.io 实现在线五子棋对战

网上有很多利用前端技术来做五子棋的Dome,所以本人为了学习socket.io 就自己也撸了一个。

游戏状态数据

棋盘由一个个方块组成,棋子落在横竖线的交界处

<table cellpadding="0" cellspacing="0">
    <tr v-for="(itemY,y) in Board">
        <td v-for="(itemX,x) in itemY" :class="getBoardCellClass(y,x)" style="position: relative;">
            <div class="cell"></div>
            <div :class="getPieceClass(y,x)" @click="clickPiece(y,x)"></div>
        </td>
    </tr>
</table>

这里我是用table和一个二维数组来画的棋盘,Board[y][x] === 0表示该位置为空,Board[y][x] === 1表示放置白子,Board[y][x] === -1表示放置黑子

下棋动作

由于给每个横竖交叉点都设置初始化数据,所以加上监听 clickPiece(y,x) 方法即可

判断输赢

这是五子棋最核心的地方,相对也是最麻烦的地方,每次落子都要从横向、竖向、两个斜向四个方向的其他棋子来判断结果。this.CurrentPieceRole 为当前落子角色

function verifySuccess(y, x) {
    function getContinuationPieceCount(callback) {
        var count = 1;
        for (var i = 1; i < 5; i++ , count++) {
            if (!callback(i)) {
                break;
            }
        }
        for (var z = -1; z > -5; z-- , count++) {
            if (!callback(z)) {
                break;
            }
        }
        return count === 5;
    }

    function legalY(i) {
        return i > -1 && i < boardY;
    }

    function legalX(i) {
        return i > -1 && i < boardX;
    }

    var yAxie = (() => {
        return getContinuationPieceCount((i) => {
            return legalY(y + i) && this.Board[y + i][x] === this.CurrentPieceRole;
        });
    });

    var xAxie = (() => {
        return getContinuationPieceCount((i) => {
            return legalX(x + i) && this.Board[y][x + i] === this.CurrentPieceRole;
        });
    });

    var yxAxie = (() => {
        return getContinuationPieceCount((i) => {
            return legalY(y + i) && legalX(x + i) && this.Board[y + i][x + i] === this.CurrentPieceRole;
        });
    });

    var xyAxie = (() => {
        return getContinuationPieceCount((i) => {
            return legalY(y - i) && legalX(x + i) && this.Board[y - i][x + i] === this.CurrentPieceRole;
        });
    });

    return xAxie() || yAxie() || yxAxie() || xyAxie();
}

利用 socket.io 进行联机

每个联机的用户都可以作为主机,每个联机的用户都有一个socket.id,可以通过选择在线的用户进行联机。

服务端

let socketClients = {} 当前在线用户对象,socket.id和socket对象映射关系 id:socket
let fiveDesk = [] 当前进行的棋局数组 { White : id1 , Black : id2 }

这里主要有两个事件
* 联机
* 落子

联机

监听联机事件,一方请求联机,通过服务器发送请求信息给对手,并新增棋局数据

socket.on('applyConnect', function (socketId) {
    var thisClient = socketClients[socket.id];
    if (!socketClients.hasOwnProperty(socketId)) {
        thisClient.socket.emit('errorResult', { IsSuccess: false, Message: '对方不在线' });
        return;
    }

    if (existsDesk(socketId)) {
        thisClient.socket.emit('errorResult', { IsSuccess: false, Message: '对方已加入其它棋局' });
        return;
    }

    var client = socketClients[socketId];
    if(!thisClient || !client)
        return;

    fiveDesk.push({
        White: thisClient.id,
        Black: client.id,
        Result: false
    });
    client.socket.emit('applyConnect', {
        IsSuccess: true,
        Message: thisClient.name,
        DeskId: fiveDesk.length - 1
    });
});

同意请求之后,双方开始棋局

socket.on('agreeConnect', function (deskId) {
    if (fiveDesk.length > deskId) {
        var desk = fiveDesk[deskId];
        desk.Result = true;

        socketClients[desk.White].socket.emit('deskBegin', { role: 1, deskId: deskId });
        socketClients[desk.Black].socket.emit('deskBegin', { role: -1, deskId: deskId });
    }
});

落子

监听落子事件,接收坐标和落子信息,并通知给对手

socket.on('clickPiece', function (data) {
    var deskId = data.deskId;
    if (fiveDesk.length > deskId) {
        var desk = fiveDesk[deskId];
            if(!desk.Result)
                return;

        var socket;
        if (data.role === 1) {
            socket = socketClients[desk.Black].socket;
        } else {
            socket = socketClients[desk.White].socket;
        }

        socket.emit('serverClickPiece', { x: data.x, y: data.y })
    }
});

客户端

客户端比较简单,直接贴几个关键的事件

socket.emit('applyConnect', socketId); //请求联机

//发送落子信息, this.ServerRole 为当前客户端棋盘角色
socket.emit('clickPiece', {
    x: x,
    y: y,
    deskId: vueHander.getDeskId(),
    role: this.ServerRole
});

//接收对手落子
socket.on('serverClickPiece', function (data) {
    vueHander.clickPiece(data.y, data.x);
});

Demo

主要是用来学习socket.io,所以很多细节方面没有处理,bug多了点

2017/11/25 posted in  网站开发

导入大SQL文件

在sqlserver的查询分析器里面执行一个超过100MB的数据库脚本时,老是报”引发类型”System.OutOfMemoryException“的异常“,这是一个内存溢出错误。

解决办法:用Osql
资料:

使用方法:
在CMD里面输入 osql -? 就可以相关提示。
9AFC8626-0F03-46C0-9775-03FEE808E826

使用osql执行一个大脚本文件:

osql -E -i"%CD%/Desktop/sqlfilepath.sql"

-E 表示使用 Window 身份验证
也可以使用sa账户,具体的可以看看上面的命令

2017/11/25 posted in  数据库

IIS GET请求URL参数长度设置

IIS版本:IIS7

描述:一开始只是设置了system.webServer 节点,但是无效,后面又设置了system.web 节点,才有效,并且 maxQueryStringLength 属性的有效值为0~2097151,超过最大值会报错。

<httpRuntime maxQueryStringLength="2097151"/>
  • system.webServer 节点设置
<security>
    <requestFiltering>
        <requestLimits maxUrl="2097151" maxQueryString="2097151" />
    </requestFiltering>
</security>

PS:所谓的请求长度(这里的长度是指UrlEncode编码后的长度),都是浏览器和Web服务器决定的,各种浏览器和Web服务器的设定都不一样。IIS默认是2048

浏览器默认URL长度:

  1. IE 2083
  2. FireFox 65,536
  3. Chrome 8182
  4. Safari 80,000
  5. Opera 190,000
2017/11/25 posted in  服务器

作用域闭包

闭包,JavaScript这门语言中近乎神话的一个概念。如果你了解词法作用域的改变,那闭包这个概念几乎是不言自明的。JavaScript中闭包无处不在,你只需要能够给识别并拥抱它。

闭包是基于词法作用域书写代码时所产生的自然结果。当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。

function foo(){
    var a = 2;
    function bar(){
        console.log(a);
    }
    bar();
}

foo();

上面这段代码,从技术上来讲,也许是闭包。但根据前面的定义,确切的说并不是。这里更准确的解释是bar()对a的引用,是运用的词法作用域的查找规则,而这些规则只是闭包的一部分。

function foo(){
    var a = 2;
    function bar(){
        console.log(a);
    }
    return bar;
}

var baz = foo();
baz(); //2

上面这段代码中,我们将bar()函数本身当做一个值类型进行传递,而bar()函数的词法作用域能够访问foo()的内部作用域。

foo()执行后,得到的返回值会赋值给变量baz,并调用baz(),实际上只是通过不同的标识符引用调用了内部函数bar()。

在这个例子中,bar()在自己定义的词法作用域之外被执行,这就是闭包的效果。

在函数执行之后,正常情况下,内部的整个作用域都会被销毁。但在这个例子中不会被销毁,因为bar()本身在使用,它拥有涵盖foo()内部作用域的闭包,使得该作用域能够一直存活,以供bar()在之后任何时间进行引用。

循环和闭包

for(var i = 1;i <= 5;i++){
    setTimeout(function timer(){
        console.log(i);
    },1000);
}

上面这段代码,我们的预期是分别输出1~5,每秒一次,每次一个。但实际情况是每秒输出一个6。

这个6,是因为循环终止条件是i <= 5,条件首次成立时 i 值是 6。延迟函数的回调函数会在循环结束之后才会运行。

我们试图假设循环中的每个迭代在运行时都会给自己创建一个 i 的副本。但根据作用域工作原理,尽管循环中的五个函数是在各个迭代中分别定义的,但它们都被封闭在一个共享的全局作用域中,因此实际上只有一个 i 。因此我们需要更多的闭包作用域。

for(var i = 1;i <= 5;i++){
    (function(){
        setTimeout(function timer(){
            console.log(i);
        },1000);
    })();   
}

但上面的代码还是不行,为什么?因为作用域是空的,仅仅是将它们封闭起来是不够的。它需要一点实质内容才行。

for(var i = 1;i <= 5;i++){
    (function(){
        var j = i;
        setTimeout(function timer(){
            console.log(j);
        },1000);
    })();   
}

这样就可以了。再改进一下。

for(var i = 1;i <= 5;i++){
    (function(j){
        setTimeout(function timer(){
            console.log(j);
        },1000);
    })(i);  
}
块作用域

上面我们用IIFE在每次迭代时都创建一个新的作用域,其实就是每次迭代我们都需要一个块作用域。

for(var i = 1;i <= 5;i++){
    let j = i; //闭包的块作用域
    setTimeout(function timer(){
        console.log(j);
    },1000);
}

本质上这是将一个块转换成一个被关闭的作用域。

for(let i = 1;i <= 5;i++){
    setTimeout(function timer(){
        console.log(i);
    },1000);
}

在for循环头部用let声明 i 会有一个特殊行为,每次迭代都会声明一次。随后的每次迭代都会使用上一次迭代结束时的值来初始化这个变量。

模块

function CoolModule(){
    var something = 'cool';
    var another = [1,2,3];
    
    function doSomething(){
        console.log(something);
    }

    function doAnother(){
        console.log(another.join('!'));
    }

    return {
        doSomething: doSomething,
        another: another
    }
}

var foo = CoolModule();
foo.doSomething(); //cool
foo.another(); //1!2!3!

上面这段代码的模式,在JavaScript中被称为模块。最常见的实现模块模式的方法通常被称为模块暴露。

通过调用CoolModule() 来创建一个模块实例。如果不执行,内部作用域和闭包都无法被创建。

CoolModule() 返回一个用对象字面量语法来表示的对象,没有返回内部数据变量的引用,这是为了保持内部数据变量的隐藏和私有的状态。可以将这个对象类型的返回值看做是模块的公共API。

从模块中返回一个对象并不是必须的,也可以直接返回一个内部函数。jQuery就是最好的例子。

模块模式必备两个必要条件:
1. 必须有外部的封闭函数,该函数必须至少被调用一次。
2. 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态。

一个具有函数属性的对象本身并不是真正的模块。从方便观察的角度看,一个从函数调用所返回的,只有数据属性而没有闭包函数的对象不是真正的模块。

var foo = (function CoolModule(id){
    function change(){
        publicAPI.identity = identity2;
    }

    function identity1(){
        console.log(id);
    }

    function identity2(){
        console.log(id.toUpperCase());
    }

    var publicAPI = {
        change: change,
        identity: identity1
    }

    return publicAPI;
})("foo module");

foo.identity();  //foo module
foo.change();
foo.identity();  //FOO MODULE

通过在模块实例的内部保留对公共API对象的内部引用,可以从内部对模块实例进行修改,包括添加或删除方法和属性,以及修改它们的值。

现代的模块机制

var MyModules = (function(){
    var modules = {};

    function define(name, deps, impl){
        for(var i = 0;i < deps.length;i++){
            deps[i] = modules[deps[i]];
        }
        modules[name] = impl.apply(impl,deps);
    }

    function get(name) {
        return modules[name];
    }

    return {
        define: define,
        get: get
    }
})();

MyModules.define('bar',[],function(){
    function hello(who){
        return 'Let me introduce: ' + who;
    }

    return {
        hello: hello
    }
});

MyModules.define('foo',['bar'],function(bar){
    var hungry = 'hippo';
    
    function awesome(){
        console.log(bar.hello(hunpry).toUpperCase());
    }

    return {
        awesome: awesome
    }
});

var bar = MyModules.get('bar');
var foo = MyModules.get('foo');
console.log(bar.hello('hippo')); //Let me introduce: hippo
foo.awesome(); //LET ME INTRODUCE: HIPPO

这段代码最核心的就是 modules[name] = impl.apply(impl, deps) 为模块定义引入包装函数,并将返回值,也就是模块的API,存储在一个根据名字来管理的模块列表中。换句话说,模块就是模块,即使在它们外层加上一个友好的包装工具也不会发生任何变化。

未来的模块机制

ES6中为模块增加了一级语法支持,一个文件为一个模块。与基于函数的模块相比,ES6模块API更加稳定。

//bar.js

function hello(who){
    return 'Let me introduce: ' + who;
}

export hello;
//foo.js

//仅从‘bar’模块导入hello()
import hello from 'bar';

var hungry = 'hippo';

function awesome(){
    console.log(hello(hungry).toUpperCase());
}

export awesome;
//导入完成的‘foo’和‘bar’模块
import bar from 'bar';
import foo from 'foo';

console.log(bar.hello('rhino'));
foo.awesome();

import 可以将一个模块中的一个或多个API导入到当前作用域中,并分别绑定在一个变量上。module会将整个模块的API导入并绑定到一个变量上。export会将当前模块的一个标识符(变量、函数)导出为公共API。

模块文件中的内容会被当做好像包含在作用域闭包中一样来处理。

2017/11/25 posted in  JavaScript