Skip to content
Advertisement

Merged Socket->recv with perl on Linux

Sorry for the bad English, it is not my mother tongue.

I am new to perl programming and I’m facing a tedious problem for some hours now.

I have coded a simple Client-Server using IO::Socket::INET. It works flawlessly on Windows, but is broken on Linux.

On Linux, the first recv get both of server’s communication and therefor, the second one is waiting endlessly for a communication.

Running the command “perl -version” gives me this result on Windows:

This is perl 5, version 20, subversion 2 (v5.20.2) built for MSWin32-x64-multi-t hread (with 1 registered patch, see perl -V for more detail)

And on Linux :

This is perl 5, version 20, subversion 2 (v5.20.2) built for x86_64-linux-gnu-thread-multi (with 42 registered patches, see perl -V for more detail)

Here is and exemple of a server:

use IO::Socket;

my $socket= IO::Socket::INET->new( Proto => "tcp",
                                   LocalPort => 2559,
                                   Listen => SOMAXCONN,
                                   Reuse => 1);

while(1)
{
    print "Waiting for a clientn";
    my $client = $socket->accept();
    $client->send("Hello, please connect yourself");
    $client->send("Username:");
    $client->recv(my $username, 1024);
    $client->send("Password:");
    $client->recv(my $cipheredpassword, 1024);
    $client->send("Thank you, Goodbye.");
    $client->close();
    print "Connection closedn";
}

And here is an exemple of a client :

use IO::Socket;
use Digest::MD5 qw(md5_hex);

my $username = "";
my $password = "";

my $server = IO::Socket::INET->new( Proto => "tcp",
                                    PeerAddr => "localhost",
                                    PeerPort => 2559);

# Pick up both $firstServerMessage and 
# $serverAskUsernameMessage on Linux
$server->recv(my $firstServerMessage, 1024); 
print "$firstServerMessagen";

# Hangs on Linux
$server->recv(my $serverAskUsernameMessage, 1024);

while($username eq "")
{
    print "$serverAskUsernameMessagen";
    chomp($username = <STDIN>); 
}

$server->send($username);
$server->recv(my $serverAskPasswordMessage, 1024);

while($password eq "")
{
    print "$serverAskPasswordMessagen";
    chomp($password = <STDIN>);
}

my $hashedPassword = md5_hex($password);
$server->send($hashedPassword);

$server->recv(my $lastServerMessage, 1024);
print $lastServerMessage;

I know that the easy solution to this problem would be to avoid having multiple ->recv in a row, but I’m also curious to know why it is not working on Linux.

I have tried to use ->flush and ->autoflush(1), without success.

Your help and knowledge would be appreciated, L.C.

Advertisement

Answer

This problem has nothing to do with the choice of operating system, or indeed the language. The problem relates to the fact of how you are using TCP.

A TCP stream is just that – a stream of bytes. It does not matter to TCP how you write those bytes – you could send in fifty 1-byte chunks, or one 50-byte, or anything inbetween. The TCP stream simply represents the bytes, without message boundaries. It’s much the same as file IO – a file on disk doesn’t remember the distinction between write calls, only the sum total of bytes that were transferred.

So in your server when you do

$client->send("Hello, please connect yourself");
$client->send("Username:");

It could all get merged in one segment over the wire, and will arrive in one go at the other end. This is why any TCP-based protocol provides some form of framing – be it linefeeds, or message headers that declare the size of the body, or whatever. Some way that the receiver can pull the stream apart again.

For example, you might decide to use "n" as a message boundary. You can then send using

$client->send("Hello, please connect yourselfn");
$client->send("Username:n");

and the receiver can use the regular readline to read these lines back out again.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement