热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

LuaSocket学习笔记

———LUASocketLib和协程前言:这是一篇译文(TheLUASocketLibandtheCoroutines),有

——— LUA SocketLib 和 协程

前言:
这是一篇译文(The LUA SocketLib and the Coroutines),有删改,原文见下方链接。

简介

目标读者:会使用 LUA SocketLib;会用协程。

LUA SocketLib 不仅提供了 TCP-UDP/IP 的网络连接。还提供了诸如 TCP、UDP 的客户端和服务端,以及 FTP、HTTP 协议等高级对象。

本教程专注于 LUA SocketLib 提供的 socket 和 TCP/IP 服务器。一旦掌握了基本的操作,这个库里面的其他组件用起来就是小菜一碟。

协程是 LUA 5.1 的特性会在 TCP/IP 服务器的管理中使用到。

TCP/IP 和 Socket 知识回顾

首先, TCP/IP 协议的圣经在此:RFC793 。
TCP/IP 是许多通信协议的一个大集合,它基于两个原始的协议:TCP 和 IP。 TCP 在 OSI 模型的第四层(传输层),IP在第三层(网络层)。TCP 协议用于从应用向网络的数据传输,能够以可靠的方式处理从源到目标地址的字节流(stream)。一个 TCP 连接由一个五件套表示{协议, IP本地地址,本地端口,IP远程地址,远程端口},它在给定的整个网络中具有唯一性。
一个连接由 2 个 半套接字(half-sockets):源(客户端)一半套接字 和 终点(服务端)一半套接字。

在监听一个端口的时候, TCP 服务器创建半个套接字(服务器套接字)。接收到连接时,服务器对这半个套接字进行复制并与终点的半个套接字(客户端套接字)连接,建立通信套接字,实现信息数据流的传输。因此,服务器总是拥有一个可用的半个套接字。

建立 TCP/IP 服务器

通常建立一个TCP服务器的过程可以直接用 SocketLib 的函数实现。具体流程如下:

  1. 建立服务端套接字
  2. 把服务端套接字附加到端口
  3. 监听这个端口

SocketLib 是 Hyperion 的一部分,它可以像 LUA 提供的其他标准库一样使用。下面给出建立 TCP 服务器的代码:

function createTCPServer( host, port, backlog) // host 是个啥?local tcp_server,err = socket.tcp();if tcp_server == nil thenreturn nil,err;endtcp_server:setoption("reuseaddr",true);local res, err = tcp_server:bind(host,port);if res==nil thenreturn nil,err;endres,err = tcp_server:listen(backlog);if res == nil thenreturn nil,err;endreturn tcp_server;
end

socket.tcp() 创建一个 TCP 服务端对象。函数和对象的详解见本页末尾。
在创建对象之后是一些耳熟能详的步骤:setoption(), bind() 及 listen();

大多数 SocketLib 函数成功返回一个参数,失败返回两个参数。在失败的情况下,参数一返回 nil,参数二返回错误信息。

createTCPServer() 必须在 host 程序初始化的时候调用。在 Hyperion 中,我们直接把函数及其调用都放在一个 LUA 的初始化脚本中(run_mode="INIT")。

最后一件事:注意 createTCPServer() 函数中的 host 参数。如果你用 localhost 来设置它,那么这个 TCP 服务器只能接受本地主机(localhost)传进来的连接(例如客户端和服务器端在同一个电脑里运行)。所以,如果你希望能够连放在另一台计算机运行的客户端程序,你需要把host设置为:host = "*";。这是一个小技巧,但是相信我,如果你不知道的话可能会浪费很多时间!

创建一个 TCP 服务器是最简单的部分,让我们来处理有分量的那部分:传入连接的接收与管理。

接收(incoming)与发送(connecting)连接管理 -协程

处理传入连接的指导原则是把 TCP 服务器设置成一个死循环,在循环的开始处用 server:accept() 函数来等待连接。但是我们马上发现了问题:如果我们进入这个死循环,那么服务器程序就会冻结在那里,这是我们无法接受的。

对于这个尖锐的问题,其解决方案是把这个死循环转换为一个与程序主线程分离的执行路径。有两个方法:线程或者协程。

线程大家都比较了解,不再赘述。

协程与线程一样是一个分离的执行队列,但是不由 OS 管理,而是由主程序来控制协程的切换。主程序每隔一小段时间会遍历一遍目前存在的协程。

协程也被称作协作的多线程,意思是协程必须相互合作来使得多线程正确工作。如果一个协程因为某种原因停止合作,整个程序都会受到影响。如果一个应用程序卡死,那么很可能不是由于操作系统的问题,而是由于应用程序中不合理的协作关系。

为了能够正确进行协作,协程中无限循环的每一次循环都必须尽快结束。协程中最关键的函数是 accept() 。这个函数在默认情况下直到新的连接到来才会执行下一步。这种行为是高度非协作性的。因此我们需要通过 server:settimeout() 来设置一个等待的最大时间。在后面提供的 DEMO_LUA_TCP_Server_Coroutine.xml demo 中,这个时间被设置为 10ms。

故事到此还没有结束。协程需要告诉主程序什么时候能够出让空间给下一个协程。这个功能由 coroutine.yield() 函数提供。yield 是协程无限循环中的最后一个指令。通过这个函数,主程序会获知当前协程已经执行完毕,轮到执行下一个协程。另一个协程的运行通过 coroutine.resume() 函数来实现。

现在,让我们以更具体的方式来看一看。先来看看等待传入连接的核心实现部分。

function runTCPServer()local stop_server = 0;local num_cnx = 0;while( stop_server == 0) dolocal err = "";local tcp_client = 0;g_tcp_server:settimeout(0.01);tcp_client,err = g_tcp_server:accept();if tcp_client ~= nil theng_client_msg, err = tcp_client:receive('*|'); if( g_client_msg ~= nil) thenif (g_client_msg == "STOP") thenstop_server = 1;elsetcp_client:send("Message :"..g_client_msg);endendif tcp_client ~= nil thentcp_client:close();endendcoroutine.yield();end
end

这个协程的创建是在初始化部分通过 coroutine.create() 函数实现。它的参数是等待处理的协程。这个函数会返回一个新建协程的句柄。

g_tcp_co = coroutine.create(runTCPServer);

通过这个句柄,我们可以按固定间隔来执行对应的协程(例如每帧调用一次),调用 coroutine.resume() 函数即可。

coroutine.resume( g_tcp_co);

一旦有连接传进来,TCP 服务器通过 server:receive() 函数接收到数据。参数中 *| 表示接收到的数据以 LF\n 结尾。这里 TCP 服务器以反射模式工作:通过 server:send() 把接收到的数据原路返回给客户端。

为了测试 TCP 服务端,这里有一个TCP 客户端可供使用:

这个客户端用起来很简单。只要输入服务器名称(主机名称或者IP地址,例如:127.0.0.1)、服务器的端口号和需要传送的消息。这个客户端会自动添加消息结尾。然后点击发送就OK啦,服务器的反馈信息会在底部区域显示。


参考链接

The LUA SocketLib and the Coroutines
Tutorial:Networking with UDP
LUA 网络编程

转:https://www.cnblogs.com/tangyikejun/p/4703380.html



推荐阅读
  • javascript分页类支持页码格式
    前端时间因为项目需要,要对一个产品下所有的附属图片进行分页显示,没考虑ajax一张张请求,所以干脆一次性全部把图片out,然 ... [详细]
  • Python多线程编程技巧与实战应用详解 ... [详细]
  • PHP 5.5.31 和 PHP 5.6.17 安全更新发布
    PHP 5.5.31 和 PHP 5.6.17 已正式发布,主要包含多个安全修复。强烈建议所有用户尽快升级至最新版本以确保系统安全。 ... [详细]
  • 本文将深入探讨 iOS 中的 Grand Central Dispatch (GCD),并介绍如何利用 GCD 进行高效多线程编程。如果你对线程的基本概念还不熟悉,建议先阅读相关基础资料。 ... [详细]
  • 一、Tomcat安装后本身提供了一个server,端口配置默认是8080,对应目录为:..\Tomcat8.0\webapps二、Tomcat8.0配置多个端口,其实也就是给T ... [详细]
  • Spring Data JdbcTemplate 入门指南
    本文将介绍如何使用 Spring JdbcTemplate 进行数据库操作,包括查询和插入数据。我们将通过一个学生表的示例来演示具体步骤。 ... [详细]
  • 本文详细解析了ASP.NET 2.0中的Callback机制,不仅介绍了基本的使用方法,还深入探讨了其背后的实现原理。通过对比Atlas框架,帮助读者更好地理解和应用这一机制。 ... [详细]
  • 机器学习算法:SVM(支持向量机)
    SVM算法(SupportVectorMachine,支持向量机)的核心思想有2点:1、如果数据线性可分,那么基于最大间隔的方式来确定超平面,以确保全局最优, ... [详细]
  • Spring 切面配置中的切点表达式详解
    本文介绍了如何在Spring框架中使用AspectJ风格的切面配置,详细解释了切点表达式的语法和常见示例,帮助开发者更好地理解和应用Spring AOP。 ... [详细]
  • IOS Run loop详解
    为什么80%的码农都做不了架构师?转自http:blog.csdn.netztp800201articledetails9240913感谢作者分享Objecti ... [详细]
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • 在多线程环境中,IpcChannel的性能表现并未如预期般优于Tcp和Http通道。实际测试结果显示,在IIS6中通过Remoting创建的Ipc通道,其速度比Tcp通道慢了约20倍。本文详细分析了这一现象的原因,并提出了针对性的优化建议,以提升IpcChannel在高并发场景下的性能表现。 ... [详细]
  • 在PHP中如何正确调用JavaScript变量及定义PHP变量的方法详解 ... [详细]
  • Java Socket 关键参数详解与优化建议
    Java Socket 的 API 虽然被广泛使用,但其关键参数的用途却鲜为人知。本文详细解析了 Java Socket 中的重要参数,如 backlog 参数,它用于控制服务器等待连接请求的队列长度。此外,还探讨了其他参数如 SO_TIMEOUT、SO_REUSEADDR 等的配置方法及其对性能的影响,并提供了优化建议,帮助开发者提升网络通信的稳定性和效率。 ... [详细]
  • 深入解析Android 4.4中的Fence机制及其应用
    在Android 4.4中,Fence机制是处理缓冲区交换和同步问题的关键技术。该机制广泛应用于生产者-消费者模式中,确保了不同组件之间高效、安全的数据传输。通过深入解析Fence机制的工作原理和应用场景,本文探讨了其在系统性能优化和资源管理中的重要作用。 ... [详细]
author-avatar
o0大大脸么么小小鱼0o
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有