Winsock Functions


shutdown Function

The shutdown function disables sends or receives on a socket.

Syntax

C++
int shutdown(
  __in  SOCKET s,
  __in  int how
);

Parameters

s [in]

A descriptor identifying a socket.

how [in]

A flag that describes what types of operation will no longer be allowed. Possible values for this flag are listed in the Winsock2.h header file.

ValueMeaning
SD_SEND
0

Shutdown send operations.

SD_RECEIVE
1

Shutdown receive operations.

SD_BOTH
2

Shutdown both send and receive operations.

 

Return Value

If no error occurs, shutdown returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.

Error codeMeaning
WSANOTINITIALISED

A successful WSAStartup call must occur before using this function.

WSAENETDOWN

The network subsystem has failed.

WSAEINVAL

The how parameter is not valid, or is not consistent with the socket type. For example, SD_SEND is used with a UNI_RECV socket type.

WSAEINPROGRESS

A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.

WSAENOTCONN

The socket is not connected (connection-oriented sockets only).

WSAENOTSOCK

Note  The descriptor is not a socket.

 

Remarks

The shutdown function is used on all types of sockets to disable reception, transmission, or both.

If the how parameter is SD_RECEIVE, subsequent calls to the recv function on the socket will be disallowed. This has no effect on the lower protocol layers. For TCP sockets, if there is still data queued on the socket waiting to be received, or data arrives subsequently, the connection is reset, since the data cannot be delivered to the user. For UDP sockets, incoming datagrams are accepted and queued. In no case will an ICMP error packet be generated.

If the how parameter is SD_SEND, subsequent calls to the send function are disallowed. For TCP sockets, a FIN will be sent after all data is sent and acknowledged by the receiver.

Setting how to SD_BOTH disables both sends and receives as described above.

The shutdown function does not close the socket. Any resources attached to the socket will not be freed until closesocket is invoked.

To assure that all data is sent and received on a connected socket before it is closed, an application should use shutdown to close connection before calling closesocket. One method to wait for notification that the remote end has sent all its data and initiated a graceful disconnect uses the WSAEventSelect function as follows :

  1. Call WSAEventSelect to register for FD_CLOSE notification.
  2. Call shutdown with how=SD_SEND.
  3. When FD_CLOSE received, call the recv or WSARecv until the function completes with success and indicates that zero bytes were received. If SOCKET_ERROR is returned, then the graceful disconnect is not possible.
  4. Call closesocket.

Another method to wait for notification that the remote end has sent all its data and initiated a graceful disconnect uses overlapped receive calls follows :

  1. Call shutdown with how=SD_SEND.
  2. Call recv or WSARecv until the function completes with success and indicates zero bytes were received. If SOCKET_ERROR is returned, then the graceful disconnect is not possible.
  3. Call closesocket.

Note  The shutdown function does not block regardless of the SO_LINGER setting on the socket.

For more information, see the section on Graceful Shutdown, Linger Options, and Socket Closure.

Once the shutdown function is called to disable send, receive, or both, there is no method to re-enable send or receive for the existing socket connection.

An application should not rely on being able to reuse a socket after it has been shut down. In particular, a Windows Sockets provider is not required to support the use of connect on a socket that has been shut down.

If an application wants to reuse a socket, then the DisconnectEx function should be called with the dwFlags parameter set to TF_REUSE_SOCKET to close a connection on a socket and prepare the socket handle to be reused. When the DisconnectEx request completes, the socket handle can be passed to the AcceptEx or ConnectEx function.

If an application wants to reuse a socket, the TransmitFile or TransmitPackets functions can be called with the dwFlags parameter set with TF_DISCONNECT and TF_REUSE_SOCKET to disconnect after all the data has been queued for transmission and prepare the socket handle to be reused. When the TransmitFile request completes, the socket handle can be passed to the function call previously used to establish the connection, such as AcceptEx or ConnectEx. When the TransmitPackets function completes, the socket handle can be passed to the AcceptEx function.

Note  The socket level disconnect is subject to the behavior of the underlying transport. For example, a TCP socket may be subject to the TCP TIME_WAIT state, causing the DisconnectEx, TransmitFile, or TransmitPackets call to be delayed.

Notes for ATM

There are important issues associated with connection teardown when using Asynchronous Transfer Mode (ATM) and Windows Sockets 2. For more information about these important considerations, see the section titled Notes for ATM in the Remarks section of the closesocket function reference.

Requirements

Minimum supported clientWindows 2000 Professional
Minimum supported serverWindows 2000 Server
HeaderWinsock2.h
LibraryWs2_32.lib
DLLWs2_32.dll

See Also

Winsock Reference
Winsock Functions
AcceptEx
connect
ConnectEx
DisconnectEx
socket
TransmitFile
TransmitPackets
WSAEventSelect

Send comments about this topic to Microsoft

Build date: 11/12/2009

Tags :


Community Content

Thomas Lee
Values for the how flag are incorrect
SD_RECEIVE 0
SD_SEND 1

Tags : contentbug

EPICS-CA
this should interrupt other threads currently blocking in send or recv for the specified socket


A responsible implementation would unblock other threads currently in blocking winsock function calls for the specified socket. For example, a dedicated thread blocking in recv should return immediately receiving WSAEINTR error status when shutdown SD_RECEIVE is called by another thread. Astonishingly, this isnt currently the case on any version of windows. There appears to be only one option on windows; we must rudely close the socket when shutting down multi-threaded socket codes.

/*************************************************************************\
* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/

#include <string.h>
#include <assert.h>
#include <stdio.h>

#include "osiSock.h"
#include "osiWireFormat.h"
#include "epicsThread.h"
#include "epicsSignal.h"
#include "epicsUnitTest.h"
#include "testMain.h"

union address {
struct sockaddr_in ia;
struct sockaddr sa;
};

class circuit {
public:
circuit ( SOCKET );
void recvTest ();
void shutdown ();
void close ();
bool recvWakeupDetected () const;
bool sendWakeupDetected () const;
virtual const char * pName () = 0;
protected:
SOCKET sock;
epicsThreadId id;
bool recvWakeup;
bool sendWakeup;
protected:
virtual ~circuit() {}
};

class serverCircuit : public circuit {
public:
serverCircuit ( SOCKET );
private:
const char * pName ();
};

class clientCircuit : public circuit {
public:
clientCircuit ( const address & );
private:
const char * pName ();
};

class server {
public:
server ( const address & );
void start ();
void daemon ();
protected:
SOCKET sock;
epicsThreadId id;
bool exit;
};

circuit::circuit ( SOCKET sockIn ) :
sock ( sockIn ),
id ( 0 ),
recvWakeup ( false ),
sendWakeup ( false )
{
assert ( this->sock != INVALID_SOCKET );
}

bool circuit::recvWakeupDetected () const
{
return this->recvWakeup;
}

bool circuit::sendWakeupDetected () const
{
return this->sendWakeup;
}

void circuit::shutdown ()
{
int status = ::shutdown ( this->sock, SHUT_RDWR );
assert ( status == 0 );
}

void circuit::close ()
{
epicsSocketDestroy ( this->sock );
}

void circuit::recvTest ()
{
char buf [1];
while ( true ) {
int status = recv ( this->sock,
buf, (int) sizeof ( buf ), 0 );
if ( status == 0 ) {
testDiag ( "%s was disconnected", this->pName () );
this->recvWakeup = true;
break;
}
else if ( status > 0 ) {
testDiag ( "client received %i characters", status );
}
else {
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
testDiag ( "%s socket recv() error was \"%s\"\n",
this->pName (), sockErrBuf );
this->recvWakeup = true;
break;
}
}
}

extern "C" void socketRecvTest ( void * pParm )
{
circuit * pCir = reinterpret_cast < circuit * > ( pParm );
pCir->recvTest ();
}

clientCircuit::clientCircuit ( const address & addrIn ) :
circuit ( epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ) )
{
address tmpAddr = addrIn;
int status = ::connect (
this->sock, & tmpAddr.sa, sizeof ( tmpAddr ) );
assert ( status == 0 );

circuit * pCir = this;
this->id = epicsThreadCreate (
"client circuit", epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
socketRecvTest, pCir );
assert ( this->id );
}


const char * clientCircuit::pName ()
{
return "client circuit";
}

extern "C" void serverDaemon ( void * pParam ) {
server * pSrv = reinterpret_cast < server * > ( pParam );
pSrv->daemon ();
}

server::server ( const address & addrIn ) :
sock ( epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ),
id ( 0 ), exit ( false )
{
assert ( this->sock != INVALID_SOCKET );

// setup server side
address tmpAddr = addrIn;
int status = bind ( this->sock,
& tmpAddr.sa, sizeof ( tmpAddr ) );
assert ( status == 0 );
status = listen ( this->sock, 10 );
assert ( status == 0 );
}

void server::start ()
{
this->id = epicsThreadCreate (
"server daemon", epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
serverDaemon, this );
assert ( this->id );
}

void server::daemon ()
{
while ( ! this->exit ) {
// accept client side
address addr;
osiSocklen_t addressSize = sizeof ( addr );
SOCKET ns = accept ( this->sock,
& addr.sa, & addressSize );
assert ( ns != INVALID_SOCKET );
circuit * pCir = new serverCircuit ( ns );
assert ( pCir );
}
}

serverCircuit::serverCircuit ( SOCKET sockIn ) :
circuit ( sockIn )
{
circuit * pCir = this;
epicsThreadId threadId = epicsThreadCreate (
"server circuit", epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
socketRecvTest, pCir );
assert ( threadId );
}

const char * serverCircuit::pName ()
{
return "server circuit";
}

static const char *mechName(int mech)
{
static const struct {
int mech;
const char *name;
} mechs[] = {
{-1, "Unknown shutdown mechanism" },
{esscimqi_socketCloseRequired, "esscimqi_socketCloseRequired" },
{esscimqi_socketBothShutdownRequired, "esscimqi_socketBothShutdownRequired" }
};

for (unsigned i=0; i < (sizeof(mechs) / sizeof(mechs[0])); ++i) {
if (mech == mechs[i].mech)
return mechs[i].name;
}
return "Unknown shutdown mechanism value";
}

MAIN(blockingSockTest)
{
testPlan(1);

address addr;
memset ( (char *) & addr, 0, sizeof ( addr ) );
addr.ia.sin_family = AF_INET;
addr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK );
addr.ia.sin_port = htons ( 5064 ); // CA

server srv ( addr );
srv.start ();
clientCircuit client ( addr );

epicsThreadSleep ( 1.0 );
assert ( ! client.recvWakeupDetected () );

client.shutdown ();
epicsThreadSleep ( 1.0 );
int mech = -1;
if ( client.recvWakeupDetected () ) {
mech = esscimqi_socketBothShutdownRequired;
}
else {
client.close ();
epicsThreadSleep ( 1.0 );
if ( client.recvWakeupDetected () ) {
mech = esscimqi_socketCloseRequired;
}
}
testDiag("This OS behaves like \"%s\".", mechName(mech));

int query = epicsSocketSystemCallInterruptMechanismQuery ();
if (! testOk(mech == query, "Socket shutdown mechanism") )
testDiag("epicsSocketSystemCallInterruptMechanismQuery returned \"%s\"",
mechName(query));

return testDone();
}





Page view tracker