Perl实现Websocket-76的TCP并发连接服务器端

直接上代码:

 

################################################################################
# Filename:
#   websocket.pl
# Description:
#   Perl mutithreaded TCP server of HTML5 Websocket-draft-76
# Test enviroment:
#   OS      - Windows XP
#   Perl    - ActivePerl 5.12.1
#   Browser - Chrome 8.0
################################################################################
#!/usr/bin/perl -w
use strict;
use warnings;
use 5.010;

use IO::Socket::INET;
use Digest::MD5 qw/md5/;
use Redis;
use POSIX ':sys_wait_h';

### main part ##################################################################
### system level options
my %VARS = (
  DEBUG => 1,
  LOG2FILE => 0
);

#common global variables
my @TIMER_TASK;   #its element is %{task_name, interval fun_ref}

### stdout to log file
if( $VARS{LOG2FILE} ){
  open LOG, ">>syslog.txt";
  select LOG;
}

### redis db initialization
my $r = Redis->new;
$r->ping or die now() . "[Server]: connect Redis failed";
$r->flushall;

my $redis_sock = $$r{sock};
$r->hset("sys:sock", "redis", $redis_sock);
say now() . " | Redis: " . $redis_sock->sockhost() . ":" . $redis_sock->sockport() .
    " <---> " . $redis_sock->peerhost() . ":" . $redis_sock->peerport();

### tcp server
my $PORT = 8000;
my $server = IO::Socket::INET->new( Proto     => 'tcp',
                                 LocalPort => $PORT,
                                 Listen    => 255,
                                 Reuse     => 1);
$server or die now() . "[Server]: Err = can't setup server";
say now() . " | Server: Listening at TCP:" . $server->sockport;

#zombie
my $zombies = 0;
$SIG{CHLD} = sub {$zombies++;};

while(my $client = $server->accept()){
  #zombie reaper
  while($zombies) {
    $zombies = 0;
    while ((my $zombie = waitpid( -1, WNOHANG)) != -1){}
  };
  $client->autoflush(1);
 
  #handshake
  sysread $client, my $handshake_req, 1024;
  my($handshake_resp,$page) = handshake($handshake_req);
  print $client $handshake_resp;
  
  close $client unless $client;
  my $client_info = $client->peerhost() . ":" . $client->peerport() . "$page";
  $r->hset("sys:sock", $client_info,$client);
  say now() . " | Client: connected from " . $client_info;
  
  #data framing
  $client->blocking(0);

  #fork child for new connection
  if(my $child = fork()){     
    $/ = "\xff";  #new line seperator
    my($req,@resp);
    while(<$client>){    
      /\x00(.*)\xff/;
      $req = $1;   
      say now() . " | ws://" . "$client_info >>> [$req]" if $VARS{DEBUG};

   
      @resp = request($req);
      foreach my $resp (@resp){
        say now() . " | ws://" . "$client_info <<< [$resp]" if $VARS{DEBUG};
        print $client "\x00$resp\xff";
      }    
    }

    return;    #go back to parent
  }
}

### sub routines ###############################################################
# Description:  Request format from webSocket
# Format:       command param1,param2, ... ,paramN
sub request{
  my @resp = ();
  $_ = shift;  
  my($cmd, @param) = split / /;
  $cmd = "req_$cmd";
  
  if(defined(&$cmd)){
    no strict 'refs';
    @resp = $cmd->(@param);
  }else{
    push @resp, "!$_";
  }
  return @resp;
}

sub req_echo{ return @_; }
sub req_random{
  $_ = shift;
  my $r;
  if(/float/){ #float
    $r = rand(100);
  }else{    #integer
    $_ = "int";
    $r = int(rand(100));
  }
  return ("random: $_ = $r");
}

sub req_showsock{
  my @socks = ();
  my %s = $r->hgetall("sys:sock");
  say "..............";
  foreach (keys %s){
    say ;
    #push @socks, $item;
  }
  return @socks;
}



sub now{
  my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
  $year += 1900;
  return sprintf("%.4d-%.2d-%.2d %2d:%.2d:%.2d",$year,$mon,$mday,$hour,$min,$sec);
}

sub handshake{
  my $req = shift;
  ($req, my $key3) = split /\r\n\r\n/, $req;  
  my @req = split /\r\n/, $req;
  
  my $field = shift @req;
  $field =~ /GET (.*) HTTP/;
  my $page = $1;
  
  $field = shift @req;
  $field = shift @req;
  $field = shift @req;
  $field =~ /Host: (.*)/;
  my $location = "ws://" . $1 . $page;
  
  $field = shift @req;
  $field =~ /Origin: (.*)/;
  my $origin = $1;
  
  $field = shift @req;
  $field =~ /Sec-WebSocket-Key1: (.*)/;
  $field = $1;
  my @key1= $field =~ /([0-9])/g;
  my $key1 = join('', @key1) + 0;
  my @space1 = $field =~ /\x20/g;
  my $space1 = @space1 + 0;
  my $part1 = $key1 / $space1;
  my $part1_hex = pack 'L>', $part1;
  
  $field = shift @req;
  $field =~ /Sec-WebSocket-Key2: (.*)/;
  $field = $1;
  my @key2 = $field =~ /([0-9])/g;
  my $key2 = join('', @key2) + 0;
  my @space2 = $field =~ /\x20/g;
  my $space2 = @space2 + 0;
  my $part2 = $key2 / $space2;
  my $part2_hex = pack 'L>', $part2;
  
  my $challenge = md5($part1_hex, $part2_hex, $key3);
  my $resp = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
            "Upgrade: WebSocket\r\n" . 
            "Connection: Upgrade\r\n" .
            "Sec-WebSocket-Origin: $origin\r\n" .
            "Sec-WebSocket-Location: $location\r\n\r\n" . $challenge;  
  return ($resp, $page);
}

sub hexcode{
  my $str = unpack 'H*', shift;
  $str =~ s/(.)(.)/\U$1$2 /g;
  return $str;
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值