Perl Socket 编程

  • 什么是 Socket(套接字)

    套接字(Socket)是在不同进程之间创建虚拟双工连接的Berkeley UNIX机制。后来将其移植到每个已知的OS上,从而使跨不同OS软件运行的地理位置的系统之间能够进行通信。如果不使用套接字,系统之间的大多数网络通信将永远不会发生。 仔细看看;网络上的典型计算机系统会根据网络上运行的各种应用程序的需要来接收和发送信息。该信息被路由到系统,因为为其指定了唯一的IP地址。在系统上,此信息将提供给在不同端口上侦听的相关应用程序。例如,互联网浏览器在端口80上侦听从Web服务器接收的信息。我们还可以编写自定义应用程序,这些应用程序可以侦听和发送/接收特定端口号上的信息。
    现在,让我们总结一下,套接字是一个IP地址和一个端口,使连接能够通过网络发送和接收数据。为了解释上面提到的套接字概念,我们将以使用Perl的客户端-服务器编程为例。要完成客户端服务器架构,我们必须执行以下步骤-
  • 创建服务器

    • 使用 socket 调用创建套接字。
    • 使用 bind 调用将套接字绑定到端口地址。
    • 使用 listen 调用在端口地址监听套接字。
    • 使用 accept 调用接受客户端连接。
  • 创建客户

    • 用socket调用创建一个套接字。
    • 使用connect调用将(套接字)连接到服务器。
    下图显示了客户端和服务器相互通信使用的完整呼叫顺序-
  • 服务器端套接字调用

    socket() 调用
    socket() 调用是在建立网络连接创建套接字的第一个电话。该调用具有以下语法-
    
    socket( SOCKET, DOMAIN, TYPE, PROTOCOL );
    
    上面的调用创建了一个SOCKET,其他三个参数是整数,对于TCP/IP连接应具有以下值。
    • DOMAIN应该是PF_INET。在您的计算机上大概是2。
    • 对于TCP/IP连接,TYPE应该为SOCK_STREAM。
    • PROTOCOL应该是(getprotobyname('tcp'))[2]。通过套接字说的是特定协议,例如TCP。
    所以服务器发出的套接字函数调用将是这样的-
    
    use Socket     # This defines PF_INET and SOCK_STREAM
    
    socket(SOCKET,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2]);
    
    bind() 调用
    通过socket() 调用创建的套接字在将它们绑定到主机名和端口号之前是无用的。服务器使用以下bind() 函数指定它们将接受来自客户端的连接的端口。
    
    bind( SOCKET, ADDRESS );
    
    这里SOCKET是由socket() 调用返回的描述符,而ADDRESS是包含三个元素的套接字地址(对于TCP / IP)-
    • 地址族(对于TCP/IP,为AF_INET,在您的系统上可能为2)。
    • 端口号(例如21)。
    • 计算机的Internet地址(例如10.12.12.168)。
    由于bind() 由服务器使用,服务器不需要知道其自己的地址,因此参数列表如下所示:
    
    use Socket        # This defines PF_INET and SOCK_STREAM
    
    $port = 12345;    # The unique port used by the sever to listen requests
    $server_ip_address = "10.12.12.168";
    bind( SOCKET, pack_sockaddr_in($port, inet_aton($server_ip_address)))
       or die "Can't bind to port $port! \n";
    
    or或die子句非常重要,因为如果服务器在没有连接成功的情况下死亡,那么除非您使用带有setockopt() 函数的SO_REUSEADDR选项,否则该端口将无法立即重用。 这里使用pack_sockaddr_in()函数将端口和IP地址打包为二进制格式。
    listen()调用
    如果这是服务器程序,则需要在指定端口上发出对listen()的调用以进行监听,即等待传入的请求。该调用具有以下语法-
    
    listen( SOCKET, QUEUESIZE );
    
    上面的调用使用了socket()调用返回的SOCKET描述符,而QUEUESIZE是同时允许的最大未完成连接请求数。
    accept()调用
    如果这是服务器程序,则需要发出对access()函数的调用以接受传入的连接。该调用具有以下语法-
    
    accept( NEW_SOCKET, SOCKET );
    
    接受调用接收由socket()函数返回的SOCKET描述符,成功完成后,将返回新的套接字描述符NEW_SOCKET,以用于客户端与服务器之间的所有将来通信。如果access()调用失败,则它将返回FLASE,该FLASE在我们最初使用的Socket模块中定义。
    通常,accept()在无限循环中使用。一旦一个连接到达,服务器要么创建一个子进程来处理它,要么自己提供服务,然后返回以监听更多连接。
    
    while(1) {
       accept( NEW_SOCKET, SOCKT );
       .......
    }
    
    现在,与服务器有关的所有呼叫都结束了,让我们看一看客户端需要的调用。
  • 客户端套接字调用

    connect()调用
    如果要准备客户端程序,则首先将使用socket()调用创建一个套接字,然后必须使用connect()调用连接到服务器。您已经看过socket()调用语法,它将与服务器socket()调用类似,但这是connect()调用的语法-
    
    connect( SOCKET, ADDRESS );
    
    这里的SCOKET是由客户端发出的socket()调用返回的套接字描述符,而ADDRESS是类似于绑定调用的套接字地址,除了它包含远程服务器的IP地址。
    
    $port = 21;    # For example, the ftp port
    $server_ip_address = "10.12.12.168";
    connect( SOCKET, pack_sockaddr_in($port, inet_aton($server_ip_address)))
       or die "Can't connect to port $port! \n";
    
    如果成功连接到服务器,则可以开始使用SOCKET描述符将命令发送到服务器,否则客户端将通过显示错误消息来退出。
  • 客户端-服务器示例

    以下是一个Perl代码,用于使用Perl套接字实现简单的客户端-服务器程序。服务器在这里侦听传入的请求,一旦建立连接,它就简单地从服务器回复Smile。客户端读取该消息并在屏幕上打印。假设我们在同一台计算机上拥有服务器和客户端,让我们看看它是如何完成的。
    创建服务器的脚本
    
    #!/usr/bin/perl -w
    # Filename : server.pl
    
    use strict;
    use Socket;
    
    # use port 7890 as default
    my $port = shift || 7890;
    my $proto = getprotobyname('tcp');
    my $server = "localhost";  # Host IP running the server
    
    # create a socket, make it reusable
    socket(SOCKET, PF_INET, SOCK_STREAM, $proto)
       or die "Can't open socket $!\n";
    setsockopt(SOCKET, SOL_SOCKET, SO_REUSEADDR, 1)
       or die "Can't set socket option to SO_REUSEADDR $!\n";
    
    # bind to a port, then listen
    bind( SOCKET, pack_sockaddr_in($port, inet_aton($server)))
       or die "Can't bind to port $port! \n";
    
    listen(SOCKET, 5) or die "listen: $!";
    print "SERVER started on port $port\n";
    
    # accepting a connection
    my $client_addr;
    while ($client_addr = accept(NEW_SOCKET, SOCKET)) {
       # send them a message, close connection
       my $name = gethostbyaddr($client_addr, AF_INET );
       print NEW_SOCKET "Smile from the server";
       print "Connection recieved from $name\n";
       close NEW_SOCKET;
    }
    
    要以后台模式运行服务器,请在Unix提示符下发出以下命令-
    
    $perl sever.pl&
    
    创建客户端的脚本
    
    !/usr/bin/perl -w
    # Filename : client.pl
    
    use strict;
    use Socket;
    
    # initialize host and port
    my $host = shift || 'localhost';
    my $port = shift || 7890;
    my $server = "localhost";  # Host IP running the server
    
    # create the socket, connect to the port
    socket(SOCKET,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2])
       or die "Can't create a socket $!\n";
    connect( SOCKET, pack_sockaddr_in($port, inet_aton($server)))
       or die "Can't connect to port $port! \n";
    
    my $line;
    while ($line = <SOCKET>) {
       print "$line\n";
    }
    close SOCKET or die "close: $!";
    
    现在,让我们在命令提示符下启动客户端,该客户端将连接到服务器并读取服务器发送的消息,并在屏幕上显示如下内容:
    
    $perl client.pl
    Smile from the server
    
    –如果您使用点号提供实际IP地址,则建议在客户端和服务器中以相同格式提供IP地址,以免造成混淆。