Сокеты
Класс Socket
В основе межсетевых взаимодействий по протоколам TCP и UDP лежат сокеты. В .NET сокеты представлены классом System.NET.Sockets.Socket , который предоставляет низкоуровневый интерфейс для приема и отправки сообщений по сети.
Рассмотрим основные свойства данного класса:
AddressFamily : возвращает все адреса, используемые сокетом. Данное свойство представляет одно из значений, определенных в одноименном перечислении AddressFamily . Перечисление содержит 18 различных значений, наиболее используемые:
InterNetwork : адрес по протоколу IPv4
InterNetworkV6 : адрес по протоколу IPv6
Ipx : адрес IPX или SPX
NetBios : адрес NetBios
Available : возвращает объем данных, которые доступны для чтения
Connected : возвращает true, если сокет подключен к удаленному хосту
LocalEndPoint : возвращает локальную точку, по которой запущен сокет и по которой он принимает данные
ProtocolType : возвращает одно из значений перечисления ProtocolType , представляющее используемый сокетом протокол. Есть следующие возможные значения:
IPSecAuthenticationHeader (Заголовок IPv6 AH)
IPSecEncapsulatingSecurityPayload (Заголовок IPv6 ESP)
IPv6DestinationOptions (Заголовок IPv6 Destination Options)
IPv6FragmentHeader (Заголовок IPv6 Fragment)
IPv6HopByHopOptions (Заголовок IPv6 Hop by Hop Options)
IPv6NoNextHeader (Заголовок IPv6 No next)
IPv6RoutingHeader (Заголовок IPv6 Routing)
Unknown (неизвестный протокол)
Unspecified (неуказанный протокол)
Каждое значение представляет соответствующий протокол, но наиболее используемыми являются Tcp и Udp.
RemoteEndPoint : возвращает адрес удаленного хоста, к которому подключен сокет
SocketType : возвращает тип сокета. Представляет одно из значений из перечисления SocketType :
Dgram : сокет будет получать и отправлять дейтаграммы по протоколу Udp. Данный тип сокета работает в связке с типом протокола — Udp и значением AddressFamily.InterNetwork
Raw : сокет имеет доступ к нижележащему протоколу транспортного уровня и может использовать для передачи сообщений такие протоколы, как ICMP и IGMP
Rdm : сокет может взаимодействовать с удаленными хостами без установки постоянного подключения. В случае, если отправленные сокетом сообщения невозможно доставить, то сокет получит об этом уведомление
Seqpacket : обеспечивает надежную двустороннюю передачу данных с установкой постоянного подключения
Stream : обеспечивает надежную двустороннюю передачу данных с установкой постоянного подключения. Для связи используется протокол TCP, поэтому этот тип сокета используется в паре с типом протокола Tcp и значением AddressFamily.InterNetwork
Unknown : адрес NetBios
Для создания объекта сокета можно использовать один из его конструкторов. Например, сокет, использующий протокол Tcp:
Или сокет, использующий протокол Udp:
Таким образом, при создании сокета мы можем указывать разные комбинации протоколов, типов сокета, значений из перечисления AddressFamily. Однако в то же время не все комбинации являются корректными. Так, для работы через протокол Tcp, нам надо обязательно указать параметры: AddressFamily.InterNetwork, SocketType.Stream и ProtocolType.Tcp. Для Udp набор параметров будет другим: AddressFamily.InterNetwork, SocketType.Dgram и ProtocolType.Udp. Для других протоколов набор значений будет отличаться. Поэтому использование сокетов может потребовать некоторого знания принципов работы отдельных протоколов. Хотя в отношении Tcp и Udp все относительно просто.
Общий принцип работы сокетов
При работе с сокетами вне зависимости от выбранных протоколов мы будем опираться на методы класса Socket:
Accept() : создает новый объект Socket для обработки входящего подключения
Bind() : связывает объект Socket с локальной конечной точкой
Close() : закрывает сокет
Connect() : устанавливает соединение с удаленным хостом
Listen() : начинает прослушивание входящих запросов
Poll() : определяет состояние сокета
Receive() : получает данные
Send() : отправляет данные
Shutdown() : блокирует на сокете прием и отправку данных
В зависимости от применяемого протокола (TCP, UDP и т.д.) общий принцип работы с сокетами будет немного различаться.
При применении протокола, который требует установление соединения, например, TCP, сервер должен вызвать метод Bind для установки точки для прослушивания входящих подключений и затем запустить прослушивание подключений с помощью метода Listen. Далее с помощью метода Accept можно получить входящие запросы на подключение в виде объекта Socket, который используется для взаимодействия с удаленным узла. У полученного объекта Socket вызываются методы Send и Receive соответственно для отправки и получения данных. Если необходимо подключиться к серверу, то вызывается метод Connect. Для обмена данными с сервером также применяются методы Send или Receive.
Если применяется протокол, для которого не требуется установление соединения, например, UDP, то после вызова метода Bind не надо вызывать метод Listen. И в этом случае для приема данных используется метод ReceiveFrom, а для отправки данных — метод SendTo.
accept function (winsock2.h)
The accept function permits an incoming connection attempt on a socket.
Syntax
Parameters
A descriptor that identifies a socket that has been placed in a listening state with the listen function. The connection is actually made with the socket that is returned by accept.
An optional pointer to a buffer that receives the address of the connecting entity, as known to the communications layer. The exact format of the addr parameter is determined by the address family that was established when the socket from the sockaddr structure was created.
An optional pointer to an integer that contains the length of structure pointed to by the addr parameter.
Return value
If no error occurs, accept returns a value of type SOCKET that is a descriptor for the new socket. This returned value is a handle for the socket on which the actual connection is made.
Otherwise, a value of INVALID_SOCKET is returned, and a specific error code can be retrieved by calling WSAGetLastError.
The integer referred to by addrlen initially contains the amount of space pointed to by addr. On return it will contain the actual length in bytes of the address returned.
Error code | Meaning |
---|---|
WSANOTINITIALISED | A successful WSAStartup call must occur before using this function. |
WSAECONNRESET | An incoming connection was indicated, but was subsequently terminated by the remote peer prior to accepting the call. |
WSAEFAULT | The addrlen parameter is too small or addr is not a valid part of the user address space. |
WSAEINTR | A blocking Windows Sockets 1.1 call was canceled through WSACancelBlockingCall. |
WSAEINVAL | The listen function was not invoked prior to accept. |
WSAEINPROGRESS | A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function. |
WSAEMFILE | The queue is nonempty upon entry to accept and there are no descriptors available. |
WSAENETDOWN | The network subsystem has failed. |
WSAENOBUFS | No buffer space is available. |
WSAENOTSOCK | The descriptor is not a socket. |
WSAEOPNOTSUPP | The referenced socket is not a type that supports connection-oriented service. |
WSAEWOULDBLOCK | The socket is marked as nonblocking and no connections are present to be accepted. |
Remarks
The accept function extracts the first connection on the queue of pending connections on socket s. It then creates and returns a handle to the new socket. The newly created socket is the socket that will handle the actual connection; it has the same properties as socket s, including the asynchronous events registered with the WSAAsyncSelect or WSAEventSelect functions.
The accept function can block the caller until a connection is present if no pending connections are present on the queue, and the socket is marked as blocking. If the socket is marked as nonblocking and no pending connections are present on the queue, accept returns an error as described in the following. After the successful completion of accept returns a new socket handle, the accepted socket cannot be used to accept more connections. The original socket remains open and listens for new connection requests.
The parameter addr is a result parameter that is filled in with the address of the connecting entity, as known to the communications layer. The exact format of the addr parameter is determined by the address family in which the communication is occurring. The addrlen is a value-result parameter; it should initially contain the amount of space pointed to by addr; on return it will contain the actual length (in bytes) of the address returned.
The accept function is used with connection-oriented socket types such as SOCK_STREAM. If addr and/or addrlen are equal to NULL, then no information about the remote address of the accepted socket is returned.
Example Code
For another example that uses the accept function, see Getting Started With Winsock.
Notes for ATM
The following are important issues associated with connection setup, and must be considered when using Asynchronous Transfer Mode (ATM) with Windows Sockets 2:
- The accept and WSAAccept functions do not necessarily set the remote address and address length parameters. Therefore, when using ATM, the caller should use the WSAAccept function and place ATM_CALLING_PARTY_NUMBER_IE in the ProviderSpecific member of the QoS structure, which itself is included in the lpSQOS parameter of the callback function used in accordance with WSAAccept.
- When using the accept function, realize that the function may return before connection establishment has traversed the entire distance between sender and receiver. This is because the accept function returns as soon as it receives a CONNECT ACK message; in ATM, a CONNECT ACK message is returned by the next switch in the path as soon as a CONNECT message is processed (rather than the CONNECT ACK being sent by the end node to which the connection is ultimately established). As such, applications should realize that if data is sent immediately following receipt of a CONNECT ACK message, data loss is possible, since the connection may not have been established all the way between sender and receiver.
WindowsВ 8.1 and Windows ServerВ 2012В R2: This function is supported for Windows Store apps on WindowsВ 8.1, Windows ServerВ 2012В R2, and later.
socket_accept
(PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8)
socket_accept — Принимает соединение на сокете
Описание
После того, как сокет socket был создан при помощи функции socket_create() , привязан к имени при помощи функции socket_bind() , и ему было указано слушать соединения при помощи функции socket_listen() , эта функция будет принимать входящие соединения на этом сокете. Как только произошло удачное соединение, возвращается экземпляр Socket, который может быть использован для связи. Если в очереди сокета есть несколько соединений, будет использовано первое из них. Если нет ожидающих соединений, то функция socket_accept() будет блокировать выполнение скрипта до тех пор, пока не появится соединение. Если сокет socket был сделан неблокирующим при помощи функции socket_set_blocking() или socket_set_nonblock() , будет возвращено false .
Экземпляр Socket, полученный при помощи функции socket_accept() не может быть использован для принятия новых соединений. Однако изначальный слушающий сокет socket , остаётся открытым и может быть использован повторно.
Список параметров
Экземпляр Socket, созданный при помощи функции socket_create() .
Возвращаемые значения
В случае успешного выполнения возвращает экземпляр Socket или false в случае возникновения ошибки. Код ошибки может быть получен при помощи вызова функции socket_last_error() . Этот код ошибки может быть передан функции socket_strerror() для получения текстового описания ошибки.
Список изменений
Версия | Описание |
---|---|
8.0.0 | В случае успешного выполнения функция возвращает экземпляр Socket; ранее возвращался ресурс ( resource ). |
Смотрите также
- socket_connect() — Начинает соединение с сокетом
- socket_listen() — Прослушивает входящие соединения на сокете
- socket_create() — Создаёт сокет (конечную точку для обмена информацией)
- socket_bind() — Привязывает имя к сокету
- socket_strerror() — Возвращает строку, описывающую ошибку сокета
User Contributed Notes 7 notes
If you want to have multiple clients on a server you will have to use non blocking.
= array();
$socket = socket_create ( AF_INET , SOCK_STREAM , SOL_TCP );
socket_bind ( $socket , ‘127.0.0.1’ , $port );
socket_listen ( $socket );
socket_set_nonblock ( $socket );
while( true )
<
if(( $newc = socket_accept ( $socket )) !== false )
<
echo «Client $newc has connected\n» ;
$clients [] = $newc ;
>
>
If you want to make a daemon process that forks on each client connection, you’ll find out that there’s a bug in PHP. The child processes send SIGCHLD to their parent when they exit but the parent can’t handle signals while it’s waiting for socket_accept (blocking).
Here is a piece of code that makes a non-blocking forking server.
#!/usr/bin/php -q
/**
* Listens for requests and forks on each connection
*/
error_reporting ( E_ALL );
set_time_limit ( 0 );
ob_implicit_flush ();
declare( ticks = 1 );
/* nobody/nogroup, change to your host’s uid/gid of the non-priv user */
change_identity ( 65534 , 65534 );
/* handle signals */
pcntl_signal ( SIGTERM , ‘sig_handler’ );
pcntl_signal ( SIGINT , ‘sig_handler’ );
pcntl_signal ( SIGCHLD , ‘sig_handler’ );
/* change this to your own host / port */
server_loop ( «127.0.0.1» , 1234 );
/**
* Change the identity to a non-priv user
*/
function change_identity ( $uid , $gid )
<
if( ! posix_setgid ( $gid ) )
<
print «Unable to setgid to » . $gid . «!\n» ;
exit;
>
if( ! posix_setuid ( $uid ) )
<
print «Unable to setuid to » . $uid . «!\n» ;
exit;
>
>
/**
* Creates a server socket and listens for incoming client connections
* @param string $address The address to listen on
* @param int $port The port to listen on
*/
function server_loop ( $address , $port )
<
GLOBAL $__server_listening ;
if(( $sock = socket_create ( AF_INET , SOCK_STREAM , 0 )) 0 )
<
echo «failed to create socket: » . socket_strerror ( $sock ). «\n» ;
exit();
>
if(( $ret = socket_bind ( $sock , $address , $port )) 0 )
<
echo «failed to bind socket: » . socket_strerror ( $ret ). «\n» ;
exit();
>
if( ( $ret = socket_listen ( $sock , 0 ) ) 0 )
<
echo «failed to listen to socket: » . socket_strerror ( $ret ). «\n» ;
exit();
>
echo «waiting for clients to connect\n» ;
while ( $__server_listening )
<
$connection = @ socket_accept ( $sock );
if ( $connection === false )
<
usleep ( 100 );
>elseif ( $connection > 0 )
<
handle_client ( $sock , $connection );
>else
<
echo «error: » . socket_strerror ( $connection );
die;
>
>
>
/**
* Signal handler
*/
function sig_handler ( $sig )
<
switch( $sig )
<
case SIGTERM :
case SIGINT :
exit();
break;
case SIGCHLD :
pcntl_waitpid (- 1 , $status );
break;
>
>
/**
* Handle a new client connection
*/
function handle_client ( $ssock , $csock )
<
GLOBAL $__server_listening ;
if ( $pid == — 1 )
<
/* fork failed */
echo «fork failure!\n» ;
die;
>elseif ( $pid == 0 )
<
/* child process */
$__server_listening = false ;
socket_close ( $ssock );
interact ( $csock );
socket_close ( $csock );
>else
<
socket_close ( $csock );
>
>
function interact ( $socket )
<
/* TALK TO YOUR CLIENT */
>
/**
* Become a daemon by forking and closing the parent
*/
function become_daemon ()
<
$pid = pcntl_fork ();
if ( $pid == — 1 )
<
/* fork failed */
echo «fork failure!\n» ;
exit();
>elseif ( $pid )
<
/* close the parent */
exit();
>else
<
/* child becomes our daemon */
posix_setsid ();
chdir ( ‘/’ );
umask ( 0 );
return posix_getpid ();