Soul客户端IM -网络处理篇

网络层编写有点网编经验的同学都能做出来,但正在开发时却遇到几个需要注意的地方。

1.在发送数据时, intcnt = (int)write(self->_socketFD, data, size); 并不会立刻发送,会有一定时间的延时,经调研是tcp默认开启了nagle算法。

此算法让连接里同一时间只容许存在一个未被确认的数据段,当客户端给服务端发消息时,发消息A,此时服务端收到A后会返回个ack,而此时客户端又发了个消息B,如此时A的ack没有收到时,客户端会先把B存到发送缓存里,等收到ack或超过一定时长后再发送B。

同时服务端也并不会立刻返回ack,而是会在一定时长内等待发送其他应答消息,和ack一起发送。

这么做的目的是充分利用网络带宽,TCP总是希望尽可能的发送足够大的数据.


但是在IM这种实时性比较高的业务场景中,此算法略显鸡肋,不如禁掉。

不过我选择的是另外一种方法:刷新发送缓存。

self->file = fdopen (self->_socketFD, "w+");

    intcnt = (int)write(self->_socketFD, data, size);

    fflush(self->file);

这样能实现和禁掉nagle算法相同的功能,并且主动权在开发者手里,可主动调用。

2.读取数据(int)cnt = read(self->_socketFD,buffer,500);

有时无法一次性读到cnt = 500,猜想是因为tcp的流量控制,某一时刻接收缓存区空间不够,接收窗口告知发送窗口空间不够,发送方无法一次发送全部数据。

所以要多次read,以读到指定的length。

伪代码如:                

int needsize = 500;

int alreadyGetSize = 0;

while(1) 

  int cnt = (int) read(self->_socketFD, buffer,needsize);

   alreadyGetSize += cnt;

if (alreadyGetSize == needsize ) {

printf("获取完毕");

}


3. connect超时机制,做到这里时,因为connect默认是阻塞式的,完全不处理大概阻塞个几分钟都有可能。而我们所能容忍的是5秒,如何让connect在阻塞时结束是关键。

而GCDAsyncsocket做法是,额外开一个线程定时器,如在5s内connect成功则关掉定时器,如5s到达时则close socket。

这么做是可以的,但我的设想是,如果能在一个线程里去完成超时任务,节省线程开销岂不是更好,而且GCDAsyncsocket 里 用gcd实现timer,block嵌套较深,复杂难懂。

既然connect是阻塞的,那么改成非阻塞就好了。

    //为设置超时时间,套接字阻塞->非阻塞

    if(fcntl(self->_socketFD,F_SETFL,fcntl(self->_socketFD,F_GETFL) |O_NONBLOCK)<0) {

        NSLog(@"FCNTL Error");

        return Connect_Failed;

    }

//建立三路握手

    intconnected =connect(self->_socketFD, (conststructsockaddr*)[databytes], (socklen_t)[datalength]);

然后用select函数监听套接字状态变化

    //设置connect阻塞时间

    tm.tv_sec = ConnectTimeOut;

    tm.tv_usec=0;

    FD_ZERO(&sockfd_set);

    FD_SET(self->_socketFD,&sockfd_set);

    selectVal =select(self->_socketFD+1,NULL, &sockfd_set,NULL, &tm);

    switch(selectVal) {

        case-1: {

            NSLog(@"Get Select Connect Failed");

            ret =Connect_Failed;

        }

        case0: {

            ret =Connect_Failed;

            NSLog(@"Time Out To Connect Failed");

        }

            break;

        default: {

            if(FD_ISSET(self->_socketFD,&sockfd_set)) {

                if(getsockopt(self->_socketFD,SOL_SOCKET,SO_ERROR, &error, (socklen_t*)&len) <0) {

                    ret =Connect_Failed;

                }

                NSLog(@"error=%d\n",error);

                if(error ==0) {

                    ret =1;

                }

                else{

                    ret =0;

                    errno= error;

                    NSLog(@"error=%d\n",error);

                }

            }

        }

            break;

    }

最后别忘记把套接字再改回阻塞式的

 fcntl(self->_socketFD, F_SETFL, fcntl(self->_socketFD,F_GETFL) &~ O_NONBLOCK);

这样在当前线程就可完成连接超时机制。

4.异常close 问题

在soul上线一段时间之后,会发现一个很奇怪的bug,我对对方发的消息全失败,却能收到对方的消息。

经调研发现,当客户端A异常关闭close时,实际上只关闭了socket的写操作,同时发送finA表示关闭了A到B的数据传送,B收到finA后回个ack代表收到,然后B再回A一个finB,代表B关闭了到A的数据传递,而假设因为网络异常原因,finA没有发送到B,或者B没有发送finB会如何呢? 很可能会出现上面描述的bug,反之亦然。


目前的解决方案是,当A发送close后,立刻重连,当服务端收到重连请求后,立刻根据用户Id查询此用户有无重复连接在保持中,如 有则主动断开,去进行新的连接。

难点在于后端需要为每个用户维持一个连接记录,建立一个连接则把这个连接标记,记录下来。

当下一个连接请求到来时,close掉连接记录里的所有连接,再去应答新连接。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342