socket长连接所用到的八大技术

[select 出错! errno:22(EINVAL)](https://blog.csdn.net/jia12216/article/details/108049826)。

我在做一个APP,连续做了7天终于搞定的一个异步socket通信的问题。由于公司机密不可能把代码贴出来。

不过socket相关的原形在我那篇socket通信中有介绍。

用到8大技术:

第一大技术是:多线程,超简单,一句而已(dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{   . . . . . .});。但是有多线程引起的崩溃问题,只有用共享锁解决了和socket集成定时器的功能来处理超时问题才得到解决。所以多线程起来容易,使用不好就是一大堆崩溃问题。多线程要慎用,能不用尽量就尽量不用。必定程序是顺序执行的,多线程是为了解决不阻碍主线程才必须用,若只是为了提高运行效率,就要多考虑考虑了,因为用不好效率没有提高,他还老嗝屁。

第二大技术:socket和select。作为异步socket他们本来就是一对。用 server_sock_fd = socket(AF_INET, SOCK_STREAM, 0);设置为异步socket,用int i = connect(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in));建立异步socket,它通常在3秒内建立连接,然后用long ret = select(max_fd + 1, &read_fd_set, NULL, NULL, &tv);来监听文件描述符(专业名称叫 fd)对应socket连接的可读标志,若服务端有消息发送过来,文件描述符可读,select函数会立即结束侦听,若判断为可读(if (FD_ISSET(server_sock_fd, &read_fd_set)))就读取缓冲区的数据(byte_num = recv(server_sock_fd,recv_msg,BUFFER_SIZE,0);),若读取的字节数为0就说明socket已经断链,需要关闭本地FD,立刻发起连接;若读的字节数为负数就是接受消息错误。当然socket一旦建立,它的一般都可读,除非出现网卡缓冲区满了写不进去才处于非可读状态,所以需要通过socket长连接写数据时需要用判断它是否可写ret = select(server_sock_fd + 1, &read_fd_set, &client_fd_set, NULL, &tv);if (FD_ISSET(server_sock_fd, &read_fd_set))。利用select函数的超时功能当定时器来使用,解决消息超时问题,不另分为一类技术了。详细内容见我的文章《使用底层socket实现客户端和服务端通信,发送请求》:http://blog.csdn.net/jia12216/article/details/47025923

第三技术:管道。那有人问既然SOCKET大部分处理可写状态,那为何不想发送消息不直接发送就可以了?因为长连接可能断链,虽然它可以立即发起连接,重新建立连接,那若你正在它连接时发送消息怎么办?还有若网卡缓冲区写满了,暂时发送不出去消息怎么办。所以用select来侦听并且判断可写标志才是王道。与服务器之间建立了长连接后,这个长连接线程大部分时间处于侦听fd的select函数(long ret = select(max_fd + 1, &read_fd_set, NULL, NULL, &tv);)处。当外部有消息需要发送时,如何立刻发送呢?最傻的方法时采用轮询的方法,就把超时时间(tv)设置的小一点来达到及时发送的目的。CPU调度的情况有三种:线程结束,调用sleep(usleep)函数,调用调用wait函数挂起线程。所以不用担心在一个函数内部非以上三种情况时被其它非系统线程抢走CPU了。当然众所周知一个线程中的循环越快耗电越快,对主机的性能影响越大(毕竟一个应用只使用一个CPU,这个线程用到CPU时间越多,其它线程得到CPU的时间就越少,而且CPU处于休眠状态的时间更少)。这就是轮询方法和处理及时性之间的均衡问题。用管道技术就能很好的解决这个问题,脱离这个轮询方法的桎梏,既解决了设置超时时间的足够长,又能解决消息的实时发送。只需要把管道的读标志加入FD的读集合,FD_SET(readPipeFd, &read_fd_set);当需要立即发送消息时,立刻向管道里写数据就可以,那么select函数(long ret = select(max_fd + 1, &read_fd_set, NULL, NULL, &tv);)立刻结束等待,先别读管道消息,然后监听是否可以写ret = select(server_sock_fd + 1, &read_fd_set, &client_fd_set, NULL, &tv);然后把管道消息读出来,把消息发送出去就可以。

第四大技术:block。BLOCK能够实现发送消息和响应消息的异步问题,并且可以实现找到以前对应的消息和响应消息。最典型的时大所周知的AFNETWORK第三方库。它能解决发送函数找到自己对应的响应消息。BLOCK方面的例子后续我会写文章进一步说明。代理方面的典型例子见我的文章《自己实现异步发送请求和图片》:http://blog.csdn.net/jia12216/article/details/47043935。

第五大技术:全局共享锁,NSLock。由于用到多线程并且用到字典,所以必然要用到全局共享锁。不然主线程正访问或拷贝一个字典,另一个线程把他的引用取消了,那么你等着程序挂吧!ARC机制是当一个对象没有一个指针指向它时,它就可能被系能释放,当系统正释放的过程中你引用它,它暴跳如雷,把你的应用干掉,并且崩溃栈没有任何信息留下,这就是你惹恼系统老大的后果,不给你任何交代干掉你的应用。所以避免这种内存释放的问题,最好的是若只是一个基本变量如:INT直接定义为对应基本变量类型就可以,别玩对象指针玩上瘾了,被杀到就不知道。当然的你的数据量很大很关键非要用字典等对象时,你只有靠NSLock这个来把大锁先锁住它,等你用完了在放开它。

第六大技术:单例。这个面向对象编程经常考试的问题。实现长连接需抽象出一定的BLOCK接口函数和全局变量。你若都定义为静态全局变量和静态全局函数,那样封装性不好,谁想怎么玩怎么玩,很不好。还是单例好,全局只有一个实例,并且把全局的变量都通过全局函数调用,避免暴露全局变量。

第七大技术:状态机。既然是长连接,长连接就有初始化,连接中,连接成功,连接异常,连接超时等状态,相应也需要状态机记录它的状态。状态机就是用全局变量记录连接在不同情况的枚举值,在不同的场景下根据变量不同的枚举值来做出对应的处理。

第八大技术:消息队列。长连接的消息队列分为发送消息队列,待处理消息队列(消息已经发送出去需要等待相应的消息),响应消息队列(一次性把响应消息收过来,等待处理消息函数统一处理)。我是利用select函数的超时功能来当定时器来使用,每个消息对应一个字典,发送消息和待处理消息有发送出去时的时间记录,响应消息与收到消息的时间理论。当前时间减去他们的时间大于超时时间就进行消息超时处理。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值