ASP.NET垃圾?.NET垃圾?

由这次CSDN泄漏用户资料的事件引起,某群中讨论得热火朝天。

这时候,又听到言论:

CSDN是ASPX做的,所以ASP.NET真的很垃圾.Net也很垃圾!千万不要用ASP.NET!

上次有黑客兜售网游源码,都是asp.net的。

我很不喜欢这种言论,虽然我不是做.Net的,我一直觉得,技术没有什么垃圾不垃圾,只有你能不能驾驭住、能不能找到它合适的领域。同样的,我也很讨厌语言优劣论,不过这个偏题了,就不说了。

我用过一段时间的C#,算是个入门者。我同意现在很多ASPX的程序写的问题很多,但是,其他语言的程序出问题的也不少。

ASPX的从业者很多,水平差异也非常大,造成很多人不信任ASPX程序的主要原因我认为是:

一部分的ASPX程序员是从ASPX开始进入程序员生涯的,这部分人有很大一部分呢又是直接使用webform来入门的,当时很多ASPX程序员只知道拖拉控件然后改改属性。于是他们对HTTP协议和特性知道的有限、对ASPX的工作原理模糊不清。

然后呢,当时很多企业大量缺人,于是这部分人也顺利的进入公司工作,企业对代码质量没把关好,于是出现了很多劣质的ASPX程序。也造成很多人对ASPX的恶劣印象。

然后我了解到的情况是:现在这样的情况已经有了很大的改善,很多的ASPX程序员都成长起来了。

所以我认为说ASP.NET很垃圾.Net也很垃圾的言论是一种很没有依据的说法,而且我恰恰认为C#是一门优雅的语言,他出生比较晚,吸取了很多语言的优点,微软更新得也很勤快。

以上言论只是我一人的看法,我对.NET了解不多,所以如有诸多错误还请海涵。

–EOF

1 Comment

CSDN就是个笑话

CSDN这个网站,我忘记我是什么时候注册的了。只是知道当时注册的时候,我还是个菜鸟中的菜鸟,当时会注册CSDN的账号是因为那个时候我曾经问过一个前辈:您平时都上什么技术社区呢?得到的回答是CSDN,于是我就乐呼呼的去注册了。

第一个账号的用户名和密码现在早已经忘记。当时注册进去后还是兴奋了一下的,觉得好象找到了组织一样,看到社区里面的帖子里面的讨论的问题,很想进去参与一句,但是奈何当时技术实在是菜,所以貌似我那第一个账号一直没发出过自己的声音。

后来也不知道是怎么回事,就不上CSDN了,我好像都忘记了中国还有这号网站一样。

在CSDN上注册第二个账号的原因是找某个代码(或者某本书吧)找到CSDN的下载频道去了,于是后面的大家估计都清楚了:为了能下载东西不得不又注册了个账号。

之后,这个账号就一直用于有的时候下载资源用。要说的是,我很讨厌CSDN设置的下载要扣积分这套系统,觉得完全是给查找资源的人设置障碍的,有的资源的分设置得老高老高的,下载下来的东西却是一坨屎。而且一直觉得CSDN资源的质量都很差,如果能在其他地方找到类似的,我基本不会选择CSDN上的。

再后来,积累多了,基本下载这些资源也少很多了,所以又一次忘记了CSDN。

之前2天前,爆出CSDN用户资料外泄,而且CSDN还把用户密码明文存储之后,突然间Twiiter上很多CSDN的tweets了。昨天晚上耐不住寂寞就下载了这个文件,打开搜索自己后来的那个用户名,果然存在,密码也是对的,email也能对上,于是当时我就想找快板砖干掉CSDN的开发人员。

不过已经爆出来了,只有赶快修改部分密码相同的服务和网站。一番折腾后,常用的服务和网站密码都修改完毕,于是开始google这件事情的缘由,看到robbin同学是这么说的:

CSDN网站早期使用过明文密码,使用明文是因为和一个第三方chat程序整合验证带来的,后来的程序员始终未对此进行处理。一直到2009年4月当时的程序员修改了密码保存方式,改成了加密密码。

但部分老的明文密码未被清理,2010年8月底,对帐号数据库全部明文密码进行了清理。2011年元旦我们升级改造了CSDN帐号管理功能,使用了强加密算法,解决了CSDN帐号的各种安全性问题。

看到这个解释,我觉得相当可笑,为了和一个chat程序整合就明文保存用户密码,知道这有多危险么?这么看来CSDN是认为用户资料没有哪个神马狗屁Chat程序重要了?而且你说:

2011年元旦我们升级改造了CSDN帐号管理功能,使用了强加密算法,解决了CSDN帐号的各种安全性问题。

既然你是早期采用明文存储的,为嘛这么多年了都没改掉,还把用户资料这么导出又不安全存放。然后这份用户资料泄漏是年初就泄漏的,为嘛现在才改造?非要出了问题才改造?你说改造需要时间,可是这时间也太久了点吧。然后“使用了强加密算法”,请问是可逆的还是不可逆的呢?

然后CSDN作为一个主要用户群体为开发者的网站,居然没有加密用户的密码,这相当的讽刺。我记得在我还是一个超级菜鸟的时候,我就知道用户密码必须要单向加密后存储。

在写这篇博文的时候:看到CSDN上发表了一篇公告:【公告】致CSDN会员的公开道歉信,这篇狗屁的公告中就说神马执意歉意什么的。狗屁,道歉有个毛用!而且公告尾部居然还加粗了以下内容:

备注说明:我们针对泄露出来的600多万帐号进行了全部扫描,统计出2011年CSDN新注册的500多万用户中,泄露出的ID有几千个。这些ID的E-mail地址绝大部分是错误的,有些密码也是错误的(少数密码吻合的我们已经重置了密码),可以确认这部分2011年的数据非本站泄露,而是黑客从其他社会工程库凑出来的数据。特此提醒2010年9月后CSDN新注册或改过密码的用户不必担心。

你们还很自豪?真是没一点悔意。

twitter上看到一条tweet,具体怎么说的忘记了,大概是这么说的:

如果CSDN泄漏用户资料这件事情在美国或者其他国家,用户早就把它告上法庭了。

我没去过国外,也没什么国外的朋友,不知道这句话的真假。但是国人真的好欺负?

最后呢,今天又看到有报道说:人人、开心的资料也部分被泄漏了。新浪的用户系统(包括新浪微薄)密码采用的可逆算法加密的。。。。这些消息真是一个比一个重磅。

诅咒以CSDN为代表的这些网站早日关门大吉。

–EOF

2 Comments

解决TTThumbsViewController设置delegate为当前class导致回退按钮消失

今天在做一个照片墙的功能的时候突然发现Three20的一个问题:

我的一个ViewController继承自TTThumbsViewController,这个Controller是由一个UINavigationController通过pushViewController: animated: 方法push出来的。

一开始,一切正常。但是后来我突然间发现UINavigationController的导航按钮消失不见了。

因为在最开始的时候我并没有发现这个问题,直到我把这个功能做的大概七七八八后,在过一次流程的时候才突然发现这个的。

发现UINavigationController的导航按钮消失后,通过注释代码的方法发现了当把TTThumbsViewController的delegate设置为self的时候,导航按钮就消失了。

一开始不得其门道,想不通为嘛当把delegate设置为self后会发生这样的问题,在google半天未见结果的后,决定看下TTThumbsViewController的实现。

在TTThumbsViewController的implementation中发现了以下代码:

- (void)setDelegate:(id)delegate {
  _delegate = delegate;

  if (_delegate) {
    self.navigationItem.leftBarButtonItem =
    [[[UIBarButtonItem alloc] initWithCustomView:[[[UIView alloc] init]
                                                  autorelease]]
     autorelease];

    self.navigationItem.rightBarButtonItem =
    [[[UIBarButtonItem alloc] initWithTitle:TTLocalizedString(@"Done", @"")
                                      style:UIBarButtonItemStyleBordered
                                     target:self
                                     action:@selector(removeFromSupercontroller)] autorelease];
  }
}

看到这里就完全明白为什么UINavigationBar的导航按钮会消失不见了,那么要解决这个问题也就简单了,初步想到可以有两种解决方法:

  1. 注释掉self.navigationItem.leftBarButtonItem =
    [[[UIBarButtonItem alloc] initWithCustomView:[[[UIView alloc] init]
    autorelease]]
    autorelease];这句。
  2. 把TTThumbsViewController的delegate设置为一个其他的class。

因为我的很多代码直接写到了Controller中,所以我就直接采用了第一种方法了。注释掉那句代码后,可爱的UINavigationBar的导航按钮又回来了。

–EOF

6 Comments

鱼刺

今天周一,我没上班。是的,我请假了,不是我想偷懒,实在是没办法去上班,具体原因还得从昨天(2011-12-04)中午说起。

昨天是周末,决定去吃香辣蟹和香辣虾,到了地,点菜的时候也顺便点了点邮亭鲫鱼。

因为来的早,我们貌似是第一批客人,所以上菜也蛮快的。而且不知道怎么回事,以前不怎么喜欢吃这里的鲫鱼的,但是当时就是觉得特别好吃。

一直到吃完付款之前,什么事情都没有,生活还是那么的美好,不过在即将付款的时候,我鬼使神差的又夹了一块鱼肉放嘴里,而且我忘记了嘴里的是鱼肉!

是的,悲剧发生了,一嘴的鱼刺,忙往盘子里面吐。一番忙活后,除了一根估计比较恨我的鱼刺外,其余的都出来了。

卡个鱼刺呗,没什么,以前也不是没碰到过,咳嗽下基本就能出来了,再不行喝点醋神马的也能搞定,所以也没在意。

回家后就觉得不怎么对劲了,无论什么办法都弄不出来。喝醋、吃东西、喝一大口水然后快速吞下…都试过了,但是,全都无效。

顺便要说的是:喝醋、而且是要慢慢的喝下,这对一个平时都不怎么吃醋的人来说,痛苦可想而知!更痛苦的是喝了一大碗的醋,鱼刺依然还是鱼刺,位置也没挪动下。

不知道鱼刺卡在具体哪个地方,晚上睡觉的时候,喉咙剧痛无比,于是,破天荒的失眠了昨晚,一直到早上6点多才迷迷糊糊的睡了2个小时。

早上起床后急奔医院,我受不了了,太难受了,而且自己能感觉到喉咙里面是肿了的。在去医院的路上,就有种不好的预感:从小到大,被鱼刺卡住的事情虽然不多,但是从来没有哪一次有这次这么严重,一天都没弄出来,最后还需要去医院。总觉得想要取出这块该死的鱼刺没那么简单。

果然,怕什么就来什么。医生努力的半天,没弄出来- -||||,说鱼刺卡的太深了,需要照片神马的,要特殊的设备。所以让我们换家大医院(这是一家区级医院,设备不是很完善)。

当时我就晕倒了,没办法,于是我忍着嘴里很不舒服的感觉(喷了麻药)上了去某三甲医院的车。

这个医院我第一次来,找了半天终于找到耳鼻喉科,挂号后找到医生,医生很熟练的给我开了张“纤维镜”缴费单=。= 148元。

今天周一,医院的人很多很多,交单子的时候,照纤维镜的医生助理就和我说估计要多等下,于是漫长的等待中…………..

终于快到我了,发现我前面4人都是鱼刺卡住了喉咙,哈哈哈,看来周末大家都吃好的去了,今天集体来求救了。

做纤维镜很不好受,至少对于我来说,医生的原话是:我鼻子里面空间太小了(肉太多了?),纤维镜要通过鼻子我会很痛苦。。。。。

是的,真的很痛苦,不过幸运的是医生很快就找到了那该死的鱼刺,不过取出来貌似还不怎么好取,因为鱼刺全贴在肉壁上了,还好最后终于取出来了。

看到取出的那块十字造型的鱼刺,突然觉得好幸福。又回到了喉咙里面没异物的那种舒服自然的感觉了,果然,幸福真的很简单。

最后就是提醒童鞋们:如果遇到被鱼刺卡住,在尝试自己取出未果后,最好即时去医院,不然拖到最后倒霉的是自己。我托了一天,喉咙已经肿的很厉害了,肿了又加大了取出的难度,自己也更难受。

-EOF

4 Comments

在MySQL中快速的插入大量测试数据.

很多时候为了测试数据库设计是否恰当,优化SQL语句,需要在表中插入大量的数据,怎么插入大量的数据就是个问题了。

最开始想到的办法就是写一个程序通过一个很大的循环来不停的插入,比如这样:

int i = LOOP_COUNT;
while(i-->=0){
    //insert data here.
}

不过我在这么做的时候发现这样插入数据非常的慢,一秒钟插入的数据量还不到100条,于是想到不要一条一条的插入,而是通过

INSERT INTO TABLE VALUES (),(),(),()...

这样的方式来插入。于是修改程序为:

int i = LOOP_COUNT;
StringBuilder stringBuilder;
while(i-->=0){
    if(LOOP_COUNT!=i && i%5000==0){
     //通过insert values的方式插入这5000条数据并清空stringBuilder
    }
    stringBuilder.append("(数据)");
}
//插入剩余的数据

这样做的插入速度是上升了很多,不过如果想要插入大量的输入,比如上亿条,那么花费的时间还是非常长的。

查询MySQL的文档,发现了一个页面:LOAD DATA INFILE 光看这个名字,觉得有戏,于是仔细看了下。

官方对于这个命令的描述是:

LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name'
    [REPLACE | IGNORE]
    INTO TABLE tbl_name
    [CHARACTER SET charset_name]
    [{FIELDS | COLUMNS}
        [TERMINATED BY 'string']
        [[OPTIONALLY] ENCLOSED BY 'char']
        [ESCAPED BY 'char']
    ]
    [LINES
        [STARTING BY 'string']
        [TERMINATED BY 'string']
    ]
    [IGNORE number LINES]
    [(col_name_or_user_var,...)]
    [SET col_name = expr,...]

命令不复杂,具体的每个参数的意义和用法请看官方的解释 http://dev.mysql.com/doc/refman/5.5/en/load-data.html

那么现在做的就是生成数据了,我习惯用\t作为数据的分隔符、用\n作为一行的分隔符,所以生成数据的代码如下:

long start = System.currentTimeMillis() / 1000;
try {
    File file = new File(FILE);

    if (file.exists()) {
        file.delete();
    }
    file.createNewFile();

    FileOutputStream outStream = new FileOutputStream(file, true);

    StringBuilder builder = new StringBuilder(10240);
    DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
    Random rand = new Random();

    String tmpDate = dateFormat.format(new Date());
    Long tmpTimestamp = System.currentTimeMillis() / 1000;

    int i = 0;
    while (i++ < LOOP) {
        if (i > 0 && i % 30000 == 0) {
            System.out.println("write offset:" + i);
            outStream.write(builder.toString().getBytes(CHARCODE));
            builder = new StringBuilder(10240);
        }

        if (tmpTimestamp.compareTo(System.currentTimeMillis() / 1000) != 0) {
            tmpDate = dateFormat.format(new Date());
            tmpTimestamp = System.currentTimeMillis() / 1000;
        }

        builder.append(tmpDate);
        builder.append("\t");
        builder.append(rand.nextInt(999));
        builder.append("\t");
        builder.append(Encrypt.md5(System.currentTimeMillis() + "" + rand.nextInt(99999999)));
        builder.append("\t");
        builder.append(rand.nextInt(999) % 2 == 0 ? "AA." : "BB");
        builder.append("\t");
        builder.append(rand.nextFloat() * 2000);
        builder.append("\t");
        builder.append(rand.nextInt(9));
        builder.append("\n");
    }

    System.out.println("write data:" + i);
    outStream.write(builder.toString().getBytes(CHARCODE));

    outStream.close();
} catch (Exception e) {
    e.printStackTrace();
}

System.out.println(System.currentTimeMillis() / 1000 - start);

这段代码会生成一个数据文件,每一行为一条记录,然后再使用上面提到的 LOAD DATA 来导入数据就可以了,我在公司的电脑下(2G内存+垃圾双核CPU,MySQL直接装在windows下,没任何优化,developer模式)每秒能达到近万条的插入速度,比其他方式都快很多。

另外如果想直接用GUI工具操作也可以,比如SQLYog中,右键要导入的表,选择Import – Import CSV Data Using Load Local.. 然后设置好编码、分隔符后就可以直接导入了。

–EOF

No Comments