Myron DaChump has asked for the wisdom of the Perl Monks concerning the following question:

This a rather convoluted problem so your patience is appreciated as I attempt to make sure I have made all the issues and circumstances clear.

My counter-strike server is transmitting a udp stream to another host (call this the splitter program) which subsquently splits the stream and forwards it to two different ports where to "receivers" catch the ultimate stream.

Simply put host 10.0.0.1 is transmitting a udp stream to 10.0.0.2 at port 10000. 10.0.0.2 then splits it (not the issue) and forwards it to 10.0.0.2:10001 and 10.0.0.2:10002.

So I wrote the following to for the "splitter", which receives the server's stream just fine, it is the forwarding problem that is the trouble. The splitter has no problems forwarding the data (I believe it forwards it to the right port, I think the problem is in the final destination script, see even further below for that code) and I include the relevant portion of the "splitter" here:

my $socket = IO::Socket::INET->new( LocalPort => $LISTEN_PORT, Type => SOCK_DGRAM, Proto => 'udp') || die "ERROR: can't open socket\n"; while ($socket->recv(my $in, 1024) ) { my $sock = IO::Socket::INET->new( Proto => 'udp', PeerAddr =>'127.0.0.1:50000'); print $sock $in;

I believe this to be true and correct. If it is not, please explain why.

The "reciever" performs the following and perhaps this is not a problem with the socket at all:

$s_socket = IO::Socket::INET->new( Proto=>"udp", LocalAddr=>"$s_ip", LocalPort=>"$s_port" ) or die ("\nCan't setup UDP socket on $ip$s_port: $!\n"); print "opened OK\n";

Then it checks to see what port AND ip the data stream is originating from...

$s_socket->recv($s_output, 1024); $s_peerhost = $s_socket->peerhost; $s_peerport = $s_socket->peerport; $s_addr = "$s_peerhost:$s_peerport";

and then later....

# Get the server info, if we know the server, otherwise ignore + the data if (!$g_servers{$s_addr}) { $g_servers{$s_addr} = &getServer($s_peerhost, $s_peerport); if (!$g_servers{$s_addr}) { &printEvent(997, "UNRECOGNISED SERVER: " . $s_output); next; } }

Now this may a be a bit confusing out of context, so I should mention that the script takes the the listen port and IP from the command line and determines if the server is a valid "source" from a mysql database. I would point out that without my "splitter" in the way, this code fragment above works fine when you specify the cs server's IP and port instead of the splitters IP and port.

The error output looks something like this....

Fri Mar 30 19:32:35 2001: 127.0.0.1:44018 - E997: UNRECOGNISED S +ERVER: ˙˙˙˙log L 03/30/2001 - 19:35:10: "eatitPUNK<269><346699><TERRO +RIST>" killed "livestock<370><23593><CT>" with "mp5navy"

So to be even more specific the counter-strike server sent the udp stream to 10.0.0.2:12345 and the splitter forwarded it to localhost via 127.0.0.1:12346. The final receipient does indeed see the stream, it just throws the above error though saying the packet orginates from an unspecified host. If I set the value for the port shown in the error in the database it reads the stream and does what it is supposed to do for a while, but then spontaneously or perhaps not the port is incremented, how I dunno.

So I am sorry for blathering on indefinitely. You time and patience are appreciated. Many thanks.

Myron DaChump chump@regulatorsonline.com

Replies are listed 'Best First'.
(tye)Re: IO:Socket::NET problem & Counter-Strike
by tye (Sage) on Mar 31, 2001 at 09:40 UTC

    I only took a quick look but I think this is the problem...

    1) It looks like your splitter creates a new socket each time it wants to forward a packet. This is extremely wasteful.

    2) The splitter doesn't specify what local port to send from so each time you create this new socket it will get a new local port number.

    3) Your check is against the sender's (splitter's) source port so, yes, that port number will be constantly changing and you'll get the error you describe.

    So the fix is to move the new() method call outside of the loop in your splitter.

    Just in case there is some deeper confusion involved in this mistake Also, a bit of a tutorial on some much-misunderstood aspect of TCP/IP is probably in order...

    Every UDP packet (or TCP connect) has four address elements associated with it: Source IP address, source port number, destination IP address, and destination port number. When talking about a socket we replace "source" and "destination" with "local" and "remote".

    A server usually specifies its local port number and nothing else. The local IP address is determined by the address the client uses to contact the server (having a server specify the local IP address is about the most common TCP/IP programming mistake there is). A client usually specifies the the remote IP address and remote port number and lets the operating system fill in appropriate local IP address (depends on the network interface that will be used to reach the server, determined from the server's IP address) and local port number (pretty much a random port number that is currently not in use).

    So you don't want to create a new UDP socket for each packet that you send out. In fact, you can just use one socket for all three purposes in the splitter: receiving packets, sending copies to the first destination, and sending copies to the second destination (this wouldn't work for TCP since each socket has to be connected to a single desination). Just specify the destination address when you send each packet out.

            - tye (but my friends call me "Tye")
      So If I understand you correctly....I should change the code from this....

      while ($socket->recv(my $in, 1024) ) { my $sock = IO::Socket::INET->new( Proto => 'udp', PeerAddr =>'127.0.0.1:50000'); print $sock $in; }

      To this....

      my $sock = IO::Socket::INET->new( Proto => 'udp', LocalAddr =>'127.0.0.1:50000'); while ($socket->recv(my $in, 1024) ) { print $sock $in; }

      But do I need to specify the PeerAddr as well in the new statement? Also it would appear it won't reuse the socket, after one iteration, it says cannot write to socket.

      I admit I am not too smart and I looked at the Module docs, but the examples there don't specify a Peer and Local in the same command, am I able to?

      Thanks again Tye, I appreciate you taking the time to help me!

      -Myron DaChump

        Well, I don't see how it could write to the socket the first time since you don't give a destination address/port. Also, I strongly discourage specifying a local IP address and I don't recommend specifying a local port number. Certainly if you specify a local port number then you should be checking whether the new() fails!

                - tye (but my friends call me "Tye")
      WOOT! Finally figured it out! Thanks for your help!

      -Myron DaChump