说说TCP中的TIME_WAIT状态

状态的形成

TIME_WAIT是一个比较纠结的状态:有三个状态可以转换成为它,分别是FIN_WAIT_1 、 FIN_WAIT_2和CLOSING.

1:由FIN_WAIT_1进入TIME_WAIT状态

在FIN_WAIT_1状态时候,收到了对方同时带FIN标志和ACK标志的报文时,那么将会发送ACK,同时直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。

2:由FIN_WAIT_2进入TIME_WAIT状态

在FIN_WAIT状态时候,接收到对方的FIN标志报文,那么将会发送ACK报文,同时进入TIME_WAIT状态.

3:由CLOSING进入TIME_WAIT状态

CLOSING这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。这种情况下只要接收到了对方发送的ACK,那么将会进入TIME_WAIT状态.

 

状态的作用

UNP上的对TIME_WAIT状态存在的理由列举有2条:

  • 可靠的实现TCP全双工连接的终止
  • 允许老的重复分节在网络中消逝

怎么解释TIME_WAIT状态可以可靠的实现TCP全双工连接的终止呢.

假设LAST_ACK丢失,被动关闭的一方将会重新发送他的最后的FIN,因此主动关闭的一方必须维护状态信息,用来允许它重新发送LAST_ACK.不然将会响应RST,而这个分解将会被被动关闭的一方解释为一个错误.如果TCP打算执行所有必要的工作来彻底终止两个方向上的数据流,他就必须正确处理连接终止序列4个分解中任何一个分节丢失的状况.

第二个理由,允许老的重复分节在网络中消逝.

假设某2个台机器在某端口之间已经建立了一个TCP连接,我们先关闭这个连接,一段时间以后,我们要在这2台机器上相同的端口重新建立一个连接,那么这个新建立的连接就是上一个连接的化身.

TCP必须防止某个连接的老的重复分组在这个连接已经终止后重新出现,从而被误解成为属于同一个连接的某个新的化身.

为了做到这点,TCP将不给处于TIME_WAIT状态的连接发起新的化身.因为TIME_WAIT的状态持续时间一般为MSL的2倍,那么就足以让某个方向上的分组最多存活MSL后被丢弃,另外一个方向上的应答最多存活MSL后也被丢弃.

这样,我们就能保证每次成功建立一个TCP连接时,来自该连接先前化身的老的重复分组都已经消逝于网络中了.

No Comments

换个思路实现iOS的密码解锁界面

一个耽搁了很长时间的项目,断断续续做了半年了吧,期间太多事情,二周都不写一句代码的事情经常出现,对这个项目的前景也越来越不看好。不过想到自己做了太多的半途而废的项目,不管怎么样,这个项目还是咬咬牙坚持下去吧。

于是,就这么一直坚持下来了,虽然现在时间更忙了,基本下班后就没时间写代码什么的。

最近,做到一个需要输入解锁密码的功能。

最开始想法是通过UITextFieldDelegate来实现,先回顾UITextFieldDelegate的定义:

Managing Editing
– textFieldShouldBeginEditing:
– textFieldDidBeginEditing:
– textFieldShouldEndEditing:
– textFieldDidEndEditing:
Editing the Text Field’s Text
– textField:shouldChangeCharactersInRange:replacementString:
– textFieldShouldClear:
– textFieldShouldReturn:

OK,看到这些,基本上是可以做出想要的效果了。

不过在实际的开发中,发现没那么简单,做出来的东西总不对,总是有问题不好解决。

然后又想到去监视键盘事件,不过觉得这样做会更麻烦。

正在烦躁中突然想到一个办法,把四个密码输入框的enable设置为NO,真正的密码输入的TextField其实是一个大小为0的不可见的一个TextField,然后每次输入一个字符后把响应的字符赋值到4个密码框中的一个,让看起来是输入到四个密码框中。

部分粗略代码如下:

-(void) setTextFieldStyle:(UITextField*) textField
{
    [textField setBorderStyle:UITextBorderStyleRoundedRect];
    [textField setFont:[UIFont systemFontOfSize:32]];
    [textField setTextAlignment:UITextAlignmentCenter];
    textField.contentVerticalAlignment	= UIControlContentVerticalAlignmentCenter;
    textField.keyboardType 		= UIKeyboardTypeNumberPad;
    textField.secureTextEntry 		= YES;
    textField.enabled 			= NO;
}

-(BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    if (range.location > 3) {
        m_erLabel.hidden = YES;
        return  NO;
    }         

    UITextField* field = (UITextField*)[self.view viewWithTag:(IPUT_TAG_ONE + range.location)];
    field.text 	       = string;
    m_erLabel.hidden   = YES;
    if (3 == range.location && ![string isEqualToString:@""]) {
		NSString inputPassword = [textField.text stringByAppendingString:string];
		m_erLabel.hidden = NO;
		[self verify];
    }

    return YES;
}

-(void) textFieldDidBeginEditing:(UITextField *)textField
{
    textField.text = @"";
}

-(void) becomeFirst
{
    [self setIsAtFirstView:YES];
    [self clearAllInput];
    m_erLabel.hidden = YES;

    [m_trueInput becomeFirstResponder];
}

这样,一个简单的密码输入解锁界面就OK了。比开始的思路实现简单很多。

–EOF

6 Comments

升级VPS上的Nginx、PHP版本

今天又搞到一个VPS(大笑)。

在搭建必要的环境的时候突然想起自己博客所在的VPS所用Nginx和PHP版本都比较老了,正好前不久PHP又爆出一个很严重的”Hash冲突拒绝服务攻击的”漏洞,于是想升级到最新的版本。

PHP的升级要稍微麻烦一些,首先要找到配置目前的 PHP 的 ./configure的参数,根据PHP官方文档的方法,这个参数可以用下面方式获取:

http://www.php.net/manual/zh/faq.build.php#faq.build.upgrade

要么在你当前的 PHP 的安装目录查看 config.nice 文件,如果没有,只要运行此脚本:

phpinfo();
在输出的顶端显示了用来配置此 PHP 的 ./configure参数。

不过个人觉得这个方法获取到的 configure参数是不完整的,不过还好我当初保存了 configure的参数,就为了方便以后升级。

PHP configure后直接make 和 make install后就可以完成升级,配置文件因为以前配置了,所以也不用去管,不过一些扩展如eAccelerator需要重新编译。

扩展的重新编译非常简单,直接切换到以前make的目录,也不用执行phpize了,直接make和make install就完成升级。配置文件和PHP一样,不用理会,直接使用以前的。

Nginx的升级比php简单很多,要确定当前运行的Nginx的 configure参数,运行下面的命令就可以得到。感谢twitter上的@vnline和@JavasBoy告诉我这个方法。

nginx -V

使用得到的configure参数配置新版本的代码,然后编译,就可以安装了。不过重新安装之前,需要关闭正在运行中的Nginx。

这样,Nginx和PHP的版本就升级完成了。

2 Comments

C语言的sizeof和#pragma pack

今天有人在某QQ群中问下面这个结构体有多大:

#pragma pack(4)
struct STV
{
	char a;
	short int b;
	char c;
};

当时我第一反应是12,不过又总觉得有点不对劲,还是写了段代码测试下,发现结果居然是6.

嗯?6,难道编译器没看到这句话么:#pragma pack(4)?

但是当我把结构体的定义修改为

#pragma pack(1)
struct STV
{
	char a;
	short int b;
	char c;
};

后,结果又变成了4,那么#pragma pack是在起作用的。然后又试着改为#pragma pack(8)和#pragma pack(16),结果还是6.

心中大概有点明白为什么了。

google之,原来对于#pragma pack的处理方式是这样进行的:

当结构体中最长宽度的数据成员的宽度小于 n 时,按该成员宽度对齐;
当最长宽度的数据成员的宽度大于或等于 n 时,按 n 对齐。

结构体STV中成员宽度最大为2,所以当n=4,8,16的时候还是按照宽度2来对齐的。

–EOF

5 Comments

我的2011年

2011年已经结束,元旦三天假期也已经过去,想想,还是零碎的总结下2011年。

2011年,工作上平均分配给了2个公司,于6月份离职SNSPLUS,进入另外一家公司。

2011年,我的本命年,因为没穿红内裤,损失不小。应该有4W人民币左右,不过还好的是家人朋友自己都身体健康、平平安安,就当舍财消灾了。

2011年,工作又回到C、C++上面来了,重新写C++让我觉得很高兴,年末碰到一个真正的技术上的大牛,虽然相处的时间不多,不过从他身上学会了很多,不光是技术、更多的是作为一个公司员工的心态。

2011年,感情依然没变化,和女朋友依旧是小吵不断,还好还是走过来了。

2011年,依旧月光、年光,每月依旧在固定的时间生活窘迫。

2011年,养了一只古牧,从四月份开始养着,到现在已经8个月了,它也有9月多大了。

2011年,上半年好玩,下半年很累。上半年,每天早上八点多近9点才慢悠悠的起床去上班,下半年,每天早上7点20就要起床然后挤公交。相比下半年真的很苦逼。

2011年,败入数码产品明显不如2010年,倍感欣慰。

2011年,身体貌似不如2010年了,需要锻炼,体重增加了5kg左右,鸭梨开始比山大了…

2011年,博客被墙,等待大半年不见解封的情况下,开了新的博客,没有导入以前的数据,重新开始。

2011年,该死的外国话还是外国话,没见到进步,这点很操蛋,一度怀疑自己是英语白痴。

2011年,第一次穿羽绒服,看来真的老了,都怕冷了。

2011年,还有很多很多………..

–EOF

5 Comments