PHP to C# - Sockets, or whatever, I dunno
Hi,
Sorry for the confusing title, as will become clear shortly this is completely unfamiliar territory for me, so I have really no clue what I'm doing.
Anyway, I am trying to interact with a game server for Call of Duty: Black Ops, this is usually called 'remote console'. I had no idea where to start so I googled around a bit and found a complete web-based remote console implementation (I did not try it but I am assuming it works). Unfortunately, it is in PHP, about which I know absolutely nothing.
The most important file in the PHP implementation, I think, is this:
php Code:
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Remote console connection
*
* @author EpicLegion
* @package rcon
* @subpackage lib
*/
class Rcon {
/**
* Socket res
*
* @var resource
*/
protected $socket = NULL;
/**
* Password
*
* @var string
*/
protected $password = '';
/**
* Host (IP)
*
* @var string
*/
protected $host = '';
/**
* Serve port
* @var int
*/
protected $port = 3074;
/**
* Constructor
*
* @param string $host
* @param int $port
* @param string $password
*/
public function __construct($host = '', $port = 3074, $password = '')
{
$this->host = $host;
$this->port = $port;
$this->password = $password;
}
/**
* Send command
*
* @param string $command
* @param bool $return_response
* @throws Exception
* @return mixed
*/
public function command($command, $return_response = TRUE)
{
// No connection
if(!$this->socket OR !is_resource($this->socket))
{
throw new Exception('You must connect to remote console before issuing commands');
}
// Send command
fwrite($this->socket, "\xff\xff\xff\xff\x00".$this->password.' '.$command."\x00");
// Wait for response?
if($return_response)
{
// Retrieve reponse
$response = $this->get_response();
// Invalid password?
if(trim($response) == 'Invalid password.')
{
throw new Exception('Invalid remote console password.');
}
return $response;
}
}
/**
* Connect to rcon
*
* @throws Exception
*/
public function connect()
{
// Errors
$errno = 0;
$errstr = '';
// Open socket
$this->socket = fsockopen('udp://'.$this->host, $this->port, $errno, $errstr, 5);
// Invalid?
if(!$this->socket)
{
throw new Exception('Cannot connect to remote console');
}
}
/**
* Disconnect
*/
public function disconnect()
{
fclose($this->socket);
}
/**
* Read response
*
* @return string
*/
public function get_response()
{
// Var containing response
$response = '';
// Set socket timeout
stream_set_timeout($this->socket, 0, 7e5);
// Lol how rare
do
{
// Read 8kb
$stream_read = fread($this->socket, 8192);
// End of response?
if(strpos($stream_read, "\x00") !== FALSE)
{
// Cut
$stream_read = substr($stream_read, 0, strpos($stream_read, "\x00"));
}
// Get socket info
$stream_info = stream_get_meta_data($this->socket);
// Append
$response .= substr(trim($stream_read, "\x0a"), 11);
}
while(!$stream_info['timed_out']);
// Return
return $response;
}
/**
* Get socket resource
*
* @return resource
*/
public function get_socket()
{
return $this->socket;
}
/**
* Set RCON password
*
* @param string $password
*/
public function set_password($password = '')
{
$this->password = $password;
}
/**
* Set server connection details
*
* @param string $host
* @param int $port
*/
public function set_server_info($host = '', $port = 3074)
{
$this->host = $host;
$this->port = $port;
}
}
I can logically split this file into three parts:
- 'Properties' like Host, Port and Password, including setters / getters / constructor, etc. I can manage this obviously.
- Connecting and sending commands to the host, as well as receiving a response. This is what I'm having trouble with.
- Parsing the response into readable chunks. I am not here yet (though I might need some help with that too), I'd rather get item 2 working first.
So, I looked around a bit and found the UdpClient class. The command to connect in the PHP source uses "udp:// ..." so I thought that would be a good place to start.
I wrote this class as a translation of the PHP code, at least the connecting and sending a command part:
csharp Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace BlackOpsRconTest
{
public class Rcon
{
public Rcon(string host, string password)
{
this.Host = host;
this.Port = 3074; // I don't think this can ever change for this game
this.Password = password;
client = new UdpClient();
}
public string Host { get; set; }
public int Port { get; set; }
public string Password { get; set; }
private readonly UdpClient client;
private static bool messageReceived = false;
public void Connect()
{
client.Connect(this.Host, this.Port);
}
public void Disconnect()
{
client.Close();
}
public void SendCommand(string command)
{
// preCommand is supposed to be the '\xff\xff\xff\xff\x00' stuff...
string preCommand = new String(Convert.ToChar(0xff), 4);
preCommand += Convert.ToChar(0x00);
// get the bytes to send
// this spells \xff\xff\xff\xff\x00<password> <command>\x00
// as in the PHP source...
var commandBytes = Encoding.ASCII.GetBytes(String.Format("{0}{1} {2}{3}",
preCommand,
this.Password,
command,
Convert.ToChar(0x00)));
// Send it!
client.Send(commandBytes, commandBytes.Length);
// Now listen for response.
IPEndPoint e = new IPEndPoint(IPAddress.Any, this.Port);
UdpState s = new UdpState();
s.e = e;
s.u = client;
Console.WriteLine("listening for messages");
client.BeginReceive(new AsyncCallback(ReceiveCallback), s);
// Do some work while we wait for a message. For this example,
// we'll just sleep
while (!messageReceived)
{
Thread.Sleep(100);
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
UdpClient u = (UdpClient)((UdpState)(ar.AsyncState)).u;
IPEndPoint e = (IPEndPoint)((UdpState)(ar.AsyncState)).e;
Byte[] receiveBytes = u.EndReceive(ar, ref e);
string receiveString = Encoding.ASCII.GetString(receiveBytes);
Console.WriteLine("Received: {0}", receiveString);
messageReceived = true;
}
struct UdpState
{
public UdpClient u;
public IPEndPoint e;
}
}
}
The sending part is mostly 'made up', I assumed that the \xff and \x00 stuff was hex so I used Convert.ToChar to convert those hex numbers to characters and insert them before the actual command to send, just like the PHP source code does (at least: i think it does that, I may be completely wrong here as I have no clue how PHP works).
The receiving part is straight from MSDN.
I am trying to use it as such:
csharp Code:
Rcon r = new Rcon("173.199.111.167", "mypassword");
r.Connect();
r.SendCommand("teamstatus");
"teamstatus" is supposed to be a valid command that should result in the server returning the players and their status (name, id, score, ping, etc).
When I try to run this, nothing happens. The console displays "listening for messages" but nothing after that. I waited a few minutes but no response ever came.
What am I doing wrong? As I said, I am completely new to this (both the PHP and to 'socket' stuff in general) so I really have no clue what I'm doing. At this point basically I'm trying to hack together a translation and I'm just looking to get A response, no matter what.
Thanks!
Re: PHP to C# - Sockets, or whatever, I dunno
Why not post this in the PHP section? Penagate is fluent with both languages (php and C#), I believe he will be of help.