Skip to content

Instantly share code, notes, and snippets.

@keyganker
Last active September 26, 2023 06:26
Show Gist options
  • Save keyganker/a57d9004c05ad3b32bdcf8922367c951 to your computer and use it in GitHub Desktop.
Save keyganker/a57d9004c05ad3b32bdcf8922367c951 to your computer and use it in GitHub Desktop.
simple tcp proxy implement for experiment #php
/**
* @param $lPort 本地端口
* @param $rHost 远程主机
* @param $rPort 远程端口
*/
function actionProxy2($lPort, $rHost, $rPort)
{
$recvTimeoutSec = 5;
$localServer = stream_socket_server("tcp://0.0.0.0:$lPort", $lErrno, $lErrstr);
if ($localServer === FALSE)
{
die('TSocket: Could not connect to 0.0.0.0:'.$lPort.' ('.$lErrstr.' ['.$lErrno.'])');
}
else
{
echo "Local connect succ...\n";
}
//stream_set_blocking($rHandle, false);
stream_set_blocking($localServer, false);
$handles = ['local' => $localServer];
$localToRemoteMap = [];//本地连接和远程连接的mapping
$localConns = [];//本地连接
while (true)
{
$read = $handles;
$readable = @stream_select($read, $_w = NULL, $_e = NULL, 5);
if ($readable > 0)
{
foreach ($read as $item)
{
echo "reading...\n";
$key = array_search($item, $handles);
if ($key === 'local')
{
echo "Accept new connection\n";
$currentLocalConn = stream_socket_accept($localServer);
$rHandle = stream_socket_client("tcp://$rHost:$rPort", $errno, $errstr, $recvTimeoutSec);
if ($rHandle === FALSE)
{
echo 'TSocket: Could not connect to '.$rHost.':'.$rPort.' ('.$errstr.' ['.$errno.'])' . "\n";
@stream_socket_shutdown($currentLocalConn, STREAM_SHUT_RDWR);
continue;
}
$handles[$currentLocalConn] = $currentLocalConn;
$handles[$rHandle] = $rHandle;
$localToRemoteMap[$currentLocalConn] = $rHandle;
$localConns[$currentLocalConn] = $currentLocalConn;
}
elseif (isset($localToRemoteMap[$item]))
{
$data = @stream_socket_recvfrom($item, 1500);
if (strlen($data) == 0)
{
closeConn($item, $localToRemoteMap[$item], $localToRemoteMap, $localConns, $handles);
}
else
{
echo "Receive data from local and send to remote: $data\n";
@stream_socket_sendto($localToRemoteMap[$item], $data);
//@fwrite($proxyMapping[$item], $data);
}
}
elseif (($localConnId = array_search($item, $localToRemoteMap)) !== false)
{
$data = @stream_socket_recvfrom($item, 1500);
if (strlen($data) == 0)
{
$this->closeConn($localConns[$localConnId], $item, $localToRemoteMap, $localConns, $handles);
}
else
{
echo "Receive data from remote and send to local: $data\n";
//@stream_socket_sendto($index, $data);
@fwrite($localConns[$localConnId], $data);
}
}
else
{
echo "Error!\n";
@stream_socket_shutdown($item, STREAM_SHUT_RDWR);
unset($handles[$item]);
}
}
}
else
{
echo "No readable\n";
}
}
}
function closeConn($local, $remote, &$localToRemoteMap, &$localConns, &$handles)
{
@stream_socket_shutdown($remote, STREAM_SHUT_RDWR);//close remote
@stream_socket_shutdown($local, STREAM_SHUT_RDWR);//close mapping local
if(isset($localToRemoteMap[$local])) unset($localToRemoteMap[$local]);//remove local to remote mapping
if(isset($localConns[$local])) unset($localConns[$local]);//remove local from local conns
if(isset($handles[$remote])) unset($handles[$remote]);//remove remote from event loop
if(isset($handles[$local])) unset($handles[$local]);//remove local from event loop
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment