您好我试图在PHP中学习和构建套接字脚本这是我的代码:
客户:
include_once('server.php');
$host = "localhost";
$port = 1025;
$message = "Hello Server";
// create socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0);
if ($socket === false) {
echo "socket_create() failed: reason: " . socket_strerror(socket_last_error($socket)) . "\n";
}
// connect to server
socket_bind($socket, $host);
if (socket_connect($socket, $host, $port) === false) {
echo "socket_connect() failed: reason: " . socket_strerror(socket_last_error($socket)) . "\n";
}
else { echo "Connected"; }
服务器:
// Variables
$host = "localhost";
$port = 1025;
// No timeout
set_time_limit(0);
// Create socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Can't create socket..");
if ($socket === false) {
echo "Unable to create socket. Error: {$errno} : {$errstr}\n";
die();
}
// Bind Socket
if (socket_bind($socket, $host) === false) {
echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($socket)) . "\n";
}
else { echo "Connected to {$host}:{$port}"; }
// Socket listen
if (socket_listen($socket, 5) === false) {
echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($socket)) . "\n";
}
if (socket_accept($socket) === false) {
echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($socket)) . "\n";
}
// Socket close
socket_close($socket);
但是我错过了一些我无法弄明白的东西,我不知道是什么,当我试图从客户端连接它只是加载什么都没有发生,这里有人能指出我正确的方向并告诉我是什么即时通讯做错了?
答案 0 :(得分:3)
在您的客户端上,您不希望socket_bind()
。这是为了打开其他系统连接的连接;简而言之,要成为一个服务器。
我们的客户端脚本:
<?php // client.php
$host = "127.0.0.1"; // connect _does_ do DNS lookups, unlike bind, which is mentioned below, but for simplicity of the example I'm explicitly naming the IP address.
$port = 1025;
$message = "Hello Server";
// create socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0);
if ($socket === false) {
// Using die() here because we really don't want to continue on while in an error condition.
die('socket_create() failed: reason: ' . socket_strerror(socket_last_error($socket));
}
// connect to server
if (socket_connect($socket, $host, $port) === false) {
die('socket_connect() failed: reason: ' . socket_strerror(socket_last_error($socket));
}
socket_write($socket, $message);
socket_close($socket);
在您的服务器中,您应该侦听0.0.0.0
(或特定的IP地址),而不是指定域名。 0.0.0.0
表示侦听所有IP地址,任何特定的IP地址都意味着只侦听为该地址配置的一个接口。
例如,如果您的服务器有2个NIC,一个分配给公共地址,例如1.2.3.4
,另一个分配给本地地址192.168.0.2
。您也可以通过TCP / IP免费获得环回127.0.0.1
。
如果要将服务器脚本的访问权限仅限于网络上的其他主机,则应将收听地址设置为192.169.0.2
。即使您的服务器可以通过1.2.3.4
的公共IP访问,该脚本也不会监听任何流量。
如果要将对服务器脚本的访问权限仅限于在该计算机上运行的其他进程,则需要使用127.0.0.1
。使用“localhost”不会让任何人离开,因为bind
不执行任何DNS查找(并且PHP的socket_bind()
只是基于BSD套接字的系统调用bind
的薄包装器)。它甚至没有查看hosts文件。因此,任何非IP字符串都将转换为整数,通常会导致它变为0,并且0将转换为0.0.0.0
,这将允许从所有接口访问,尽管您认为只是听取localhost流量。
当您接受连接时,它会创建一个新的套接字资源,我在下面的示例中将其命名为$clientSocket
。不要在新主机连接时创建的套接字和监听套接字之间混淆;它们非常相似,但有一个非常重要的区别:当你的监听套接字有新消息时,总是说有一个新的主机连接,所以你应该accept
。如果是客户端套接字,那么您将使用read
或recv
。 (我更喜欢recv
因为更精细的控制,但我在下面的例子中使用read
来更清楚地显示过程而不是通过引用来增加混淆...这也是我不是的原因要显示select
。)
我们的服务器脚本:
<?php //server.php
$listen_address = "0.0.0.0";
$port = 1025;
$maxBuffer = 1024; // bytes, not characters.
// No timeout
//set_time_limit(0); // Shouldn't be necessary in CLI.
// Create socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
die('Unable to create socket: ' . $socket_strerror(socket_last_error($socket)) . PHP_EOL);
}
// Bind Socket
if (socket_bind($socket, $listen_address, $port) === false) {
die('socket_bind() failed: ' . socket_strerror(socket_last_error($socket)) . PHP_EOL);
}
else {
echo "Connected to {$listen_address}:{$port}\n";
}
// Accept our client connection
// Typically, this would be where we'd put a loop with socket_select,
// but for our example, since we'll be exiting as soon as we have our
// first packet, we'll listen, accept, read, then close.
if (socket_listen($socket) === false) {
die('socket_listen() failed: ' . socket_strerror(socket_last_error($socket)) . PHP_EOL);
}
$clientSocket = socket_accept($socket);
if ($clientSocket === false) {
die('socket_accept() failed: ' . socket_strerror(socket_last_error($socket)) . PHP_EOL);
}
// Because the contents of a packet could be longer than our
// buffer size, it's advisable to peek at the socket to see
// if it would block before saying that our message is complete.
// In our example, we're receiving 12 bytes out of our maximum 1024,
// so I leave handling large packets as an exercise for the developer.
$message = socket_read($clientSocket, $maxBuffer);
var_dump($message);
// Socket close
socket_close($clientSocket);
socket_close($socket);
最后,这是如何处理上面的混乱。
请注意,我将同时在同一个TTY中运行服务器和客户端,我将服务器作为后台进程运行(使用&
修饰符),但我不会重定向它们的输出,因此两个脚本都会将它们的输出吐出到同一个终端。
myhost.mydomain$ php ./server.php &
[8] 76399
Connected to 0.0.0.0:1025
myhost.mydomain$ php ./client.php
string(12) "Hello Server"
[8]+ Done php ./server.php
答案 1 :(得分:1)
您的服务器实施是错误的。你的服务器应该有一个循环,它总是在监听连接。
服务器
# Create Socket
$socket = socket_create(AF_INET6, SOCK_STREAM, SOL_TCP);
# Bind a host and a port to the socket
socket_bind($socket, $host, $port);
# Listen for connection in the socket you just created
socket_listen($socket);
# This is the heart of the server, without it, it can not serve.
while(true)
{
# Accept Client connection if client socket connects
$client = socket_accept($socket);
# Logic @todo Reponse to client
print "Connected." // For testing purposes, see if client actually connected
# Close client connection
socket_close($client);
}
要从客户端连接到服务器,请使用socket_connect
,文档here。