911 lines
20 KiB
C
911 lines
20 KiB
C
/*
|
||
* win/dpSock.c --
|
||
*
|
||
* This file implements the few windows-specific routines for the
|
||
* socket code.
|
||
*
|
||
* Copyright (c) 1995-1996 Cornell University.
|
||
*
|
||
* See the file "license.terms" for information on usage and redistribution
|
||
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||
*
|
||
*/
|
||
|
||
#include "generic/dpInt.h"
|
||
#include "generic/dpPort.h"
|
||
|
||
#ifndef _TCL76
|
||
|
||
SocketInfo *dpSocketList;
|
||
int initd = FALSE;
|
||
HWND hwnd;
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* DppSetupSocketEvents --
|
||
*
|
||
* Initializes a new socket structure and the events
|
||
* we are interested in handling.
|
||
*
|
||
* Results:
|
||
* None
|
||
*
|
||
* Side effects:
|
||
* This puts the socket in non-blocking mode
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
DppSetupSocketEvents(statePtr, sock, async, server)
|
||
SocketState *statePtr;
|
||
DpSocket sock;
|
||
int async;
|
||
int server;
|
||
{
|
||
SocketInfo *infoPtr;
|
||
|
||
infoPtr = NewSocketInfo(sock);
|
||
statePtr->sockInfo = infoPtr;
|
||
|
||
/*
|
||
* Set up the select mask for read/write events. If the connect
|
||
* attempt has not completed, include connect events.
|
||
*/
|
||
|
||
if (server) {
|
||
infoPtr->selectEvents = FD_ACCEPT;
|
||
infoPtr->watchEvents |= FD_ACCEPT;
|
||
} else {
|
||
infoPtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE;
|
||
infoPtr->watchEvents |= FD_READ | FD_WRITE | FD_CLOSE;
|
||
if (async) {
|
||
infoPtr->flags |= SOCKET_ASYNC_CONNECT;
|
||
infoPtr->selectEvents |= FD_CONNECT;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Register for interest in events in the select mask. Note that
|
||
* automatically places the socket into non-blocking mode.
|
||
*/
|
||
|
||
(void) WSAAsyncSelect(infoPtr->socket, hwnd,
|
||
SOCKET_MESSAGE, infoPtr->selectEvents);
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* SocketSetupProc --
|
||
*
|
||
* Scans for a ready socket.
|
||
*
|
||
* Results:
|
||
* None
|
||
*
|
||
* Side effects:
|
||
* Might set the event loop to poll.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
SocketSetupProc(data, flags)
|
||
ClientData data; /* Not used. */
|
||
int flags; /* Event flags as passed to Tcl_DoOneEvent. */
|
||
{
|
||
SocketInfo *infoPtr;
|
||
Tcl_Time blockTime = { 0, 0 };
|
||
|
||
if (!(flags & TCL_FILE_EVENTS)) {
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* Check to see if there is a ready socket. If so, poll.
|
||
*/
|
||
|
||
for (infoPtr = dpSocketList; infoPtr != NULL;
|
||
infoPtr = infoPtr->nextPtr) {
|
||
if (infoPtr->readyEvents & infoPtr->watchEvents) {
|
||
Tcl_SetMaxBlockTime(&blockTime);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* NewSocketInfo --
|
||
*
|
||
* Creates and initialized a new socket structure.
|
||
*
|
||
* Results:
|
||
* None
|
||
*
|
||
* Side effects:
|
||
* Memory is allocated.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
SocketInfo *
|
||
NewSocketInfo(socket)
|
||
SOCKET socket;
|
||
{
|
||
SocketInfo *infoPtr;
|
||
|
||
infoPtr = (SocketInfo *) ckalloc(sizeof(SocketInfo));
|
||
infoPtr->socket = socket;
|
||
infoPtr->flags = 0;
|
||
infoPtr->watchEvents = 0;
|
||
infoPtr->readyEvents = 0;
|
||
infoPtr->selectEvents = 0;
|
||
infoPtr->lastError = 0;
|
||
infoPtr->nextPtr = dpSocketList;
|
||
dpSocketList = infoPtr;
|
||
return infoPtr;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* SocketCheckProc --
|
||
*
|
||
* Finds a socket with an event to process and queues
|
||
* an event if one is found.
|
||
*
|
||
* Results:
|
||
* None
|
||
*
|
||
* Side effects:
|
||
* An event handler might execute later.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
SocketCheckProc(data, flags)
|
||
ClientData data; /* Not used. */
|
||
int flags; /* Event flags as passed to Tcl_DoOneEvent. */
|
||
{
|
||
SocketInfo *infoPtr;
|
||
SocketEvent *evPtr;
|
||
|
||
if (!(flags & TCL_FILE_EVENTS)) {
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* Queue events for any ready sockets that don't already have events
|
||
* queued (caused by persistent states that won't generate WinSock
|
||
* events).
|
||
*/
|
||
|
||
for (infoPtr = dpSocketList; infoPtr != NULL;
|
||
infoPtr = infoPtr->nextPtr) {
|
||
if ((infoPtr->readyEvents & infoPtr->watchEvents)
|
||
&& !(infoPtr->flags & SOCKET_PENDING)) {
|
||
infoPtr->flags |= SOCKET_PENDING;
|
||
evPtr = (SocketEvent *) ckalloc(sizeof(SocketEvent));
|
||
evPtr->header.proc = SocketEventProc;
|
||
evPtr->socket = infoPtr->socket;
|
||
Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* WaitForSocketEvent --
|
||
*
|
||
* Waits for one of the specified events to occur on the
|
||
* socket.
|
||
*
|
||
* Results:
|
||
* None
|
||
*
|
||
* Side effects:
|
||
* Blocks until event happens.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
WaitForSocketEvent(infoPtr, events, errorCodePtr)
|
||
SocketInfo *infoPtr; /* Information about this socket. */
|
||
int events; /* Events to look for. */
|
||
int *errorCodePtr; /* Where to store errors? */
|
||
{
|
||
MSG msg;
|
||
int result = 1;
|
||
int oldMode;
|
||
|
||
/*
|
||
* Be sure to disable event servicing so we are truly modal.
|
||
*/
|
||
|
||
oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE);
|
||
|
||
while (!(infoPtr->readyEvents & events)) {
|
||
if (infoPtr->flags & SOCKET_ASYNC) {
|
||
if (!PeekMessage(&msg, hwnd, SOCKET_MESSAGE,
|
||
SOCKET_MESSAGE, PM_REMOVE)) {
|
||
*errorCodePtr = EWOULDBLOCK;
|
||
result = 0;
|
||
break;
|
||
}
|
||
} else {
|
||
/*
|
||
* Look for a socket event. Note that we will be getting
|
||
* events for all of Tcl's sockets, not just the one we wanted.
|
||
*/
|
||
|
||
result = GetMessage(&msg, hwnd, SOCKET_MESSAGE,
|
||
SOCKET_MESSAGE);
|
||
if (result == -1) {
|
||
TclWinConvertError(GetLastError());
|
||
*errorCodePtr = Tcl_GetErrno();
|
||
result = 0;
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* I don't think we can get a WM_QUIT during a tight modal
|
||
* loop, but just in case...
|
||
*/
|
||
|
||
if (result == 0) {
|
||
panic("WaitForSocketEvent: Got WM_QUIT during modal loop!");
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Dispatch the message and then check for an error on the socket.
|
||
*/
|
||
|
||
infoPtr->lastError = 0;
|
||
DispatchMessage(&msg);
|
||
if (infoPtr->lastError) {
|
||
*errorCodePtr = infoPtr->lastError;
|
||
result = 0;
|
||
break;
|
||
}
|
||
}
|
||
|
||
(void) Tcl_SetServiceMode(oldMode);
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* SocketEventProc --
|
||
*
|
||
* Translates the Windows socket event into a Tcl
|
||
* channel event and notifies the channel.
|
||
*
|
||
* Results:
|
||
* None
|
||
*
|
||
* Side effects:
|
||
* Channel handler will execute.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
int
|
||
SocketEventProc(evPtr, flags)
|
||
Tcl_Event *evPtr; /* Event to service. */
|
||
int flags; /* Flags that indicate what events to
|
||
* handle, such as TCL_FILE_EVENTS. */
|
||
{
|
||
SocketInfo *infoPtr;
|
||
SocketEvent *eventPtr = (SocketEvent *) evPtr;
|
||
int mask = 0;
|
||
u_long nBytes;
|
||
int status, events;
|
||
|
||
if (!(flags & TCL_FILE_EVENTS)) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Find the specified socket on the socket list.
|
||
*/
|
||
|
||
for (infoPtr = dpSocketList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
|
||
if (infoPtr->socket == eventPtr->socket) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Discard events that have gone stale.
|
||
*/
|
||
|
||
if (!infoPtr) {
|
||
return 1;
|
||
}
|
||
|
||
infoPtr->flags &= ~SOCKET_PENDING;
|
||
|
||
/*
|
||
* Handle connection requests directly.
|
||
*
|
||
*/
|
||
|
||
if (infoPtr->readyEvents & FD_ACCEPT) {
|
||
mask |= TCL_READABLE;
|
||
Tcl_NotifyChannel(infoPtr->channel, mask);
|
||
return 1;
|
||
}
|
||
|
||
|
||
/*
|
||
* Mask off unwanted events and compute the read/write mask so
|
||
* we can notify the channel.
|
||
*/
|
||
events = infoPtr->readyEvents & infoPtr->watchEvents;
|
||
|
||
if (events & FD_CLOSE) {
|
||
/*
|
||
* If the socket was closed and the channel is still interested
|
||
* in read events, then we need to ensure that we keep polling
|
||
* for this event until someone does something with the channel.
|
||
* Note that we do this before calling Tcl_NotifyChannel so we don't
|
||
* have to watch out for the channel being deleted out from under
|
||
* us. This may cause a redundant trip through the event loop, but
|
||
* it's simpler than trying to do unwind protection.
|
||
*/
|
||
|
||
Tcl_Time blockTime = { 0, 0 };
|
||
Tcl_SetMaxBlockTime(&blockTime);
|
||
mask |= TCL_READABLE;
|
||
} else if (events & FD_READ) {
|
||
/*
|
||
* We must check to see if data is really available, since someone
|
||
* could have consumed the data in the meantime.
|
||
*/
|
||
|
||
status = ioctlsocket(infoPtr->socket, FIONREAD, &nBytes);
|
||
if (status != SOCKET_ERROR && nBytes > 0) {
|
||
mask |= TCL_READABLE;
|
||
} else {
|
||
/*
|
||
* We are in a strange state, probably because someone
|
||
* besides Tcl is reading from this socket. Try to
|
||
* recover by clearing the read event.
|
||
*/
|
||
|
||
infoPtr->readyEvents &= ~(FD_READ);
|
||
|
||
/*
|
||
* Re-issue WSAAsyncSelect() since we are gobbling up an
|
||
* event, without letting the reader do any I/O to re-enable
|
||
* the notification.
|
||
*/
|
||
|
||
(void) WSAAsyncSelect(infoPtr->socket, hwnd,
|
||
SOCKET_MESSAGE, infoPtr->selectEvents);
|
||
}
|
||
}
|
||
if (events & FD_WRITE) {
|
||
mask |= TCL_WRITABLE;
|
||
}
|
||
|
||
if (mask) {
|
||
infoPtr->readyEvents &= ~FD_WRITE;
|
||
Tcl_NotifyChannel(infoPtr->channel, mask);
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* SocketExitHandler --
|
||
*
|
||
* Cleans up the entire DP socket subsystem.
|
||
*
|
||
* Results:
|
||
* None
|
||
*
|
||
* Side effects:
|
||
* Sockets are no longer viable.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
void
|
||
SocketExitHandler(clientData)
|
||
ClientData clientData; /* Not used. */
|
||
{
|
||
DestroyWindow(hwnd);
|
||
UnregisterClass("DpSocket", TclWinGetTclInstance());
|
||
Tcl_DeleteEventSource(SocketSetupProc, SocketCheckProc, NULL);
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* SocketProc --
|
||
*
|
||
* This is the Windows callback which recv's
|
||
* notification of socket events. It marks the
|
||
* socket and tells Tcl to service events.
|
||
*
|
||
* Results:
|
||
* None
|
||
*
|
||
* Side effects:
|
||
* Event handling will commence.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
LRESULT CALLBACK
|
||
SocketProc(hwnd, message, wParam, lParam)
|
||
HWND hwnd;
|
||
UINT message;
|
||
WPARAM wParam;
|
||
LPARAM lParam;
|
||
{
|
||
int event, error;
|
||
SOCKET socket;
|
||
SocketInfo *infoPtr;
|
||
|
||
if (message != SOCKET_MESSAGE) {
|
||
return DefWindowProc(hwnd, message, wParam, lParam);
|
||
}
|
||
|
||
event = WSAGETSELECTEVENT(lParam);
|
||
error = WSAGETSELECTERROR(lParam);
|
||
socket = (SOCKET) wParam;
|
||
|
||
/*
|
||
* Find the specified socket on the socket list and update its
|
||
* eventState flag.
|
||
*/
|
||
|
||
for (infoPtr = dpSocketList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
|
||
if (infoPtr->socket == socket) {
|
||
/*
|
||
* Update the socket state.
|
||
*/
|
||
|
||
if (event & FD_CLOSE) {
|
||
infoPtr->readyEvents &= ~(FD_WRITE|FD_ACCEPT);
|
||
}
|
||
if (event & FD_CONNECT) {
|
||
/*
|
||
* The socket is now connected, so clear the async connect
|
||
* flag.
|
||
*/
|
||
|
||
infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
|
||
|
||
/*
|
||
* Remember any error that occurred so we can report
|
||
* connection failures.
|
||
*/
|
||
|
||
if (error != ERROR_SUCCESS) {
|
||
TclWinConvertWSAError(error);
|
||
infoPtr->lastError = Tcl_GetErrno();
|
||
}
|
||
|
||
}
|
||
infoPtr->readyEvents |= event;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Flush the Tcl event queue before returning to the event loop.
|
||
*/
|
||
|
||
Tcl_ServiceAll();
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* InitDpSockets --
|
||
*
|
||
* Initializes DP's socket subsystem.
|
||
*
|
||
* Results:
|
||
* None
|
||
*
|
||
* Side effects:
|
||
* A hidden window is created and a window class
|
||
* is registered.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
void
|
||
InitDpSockets()
|
||
{
|
||
WNDCLASS class;
|
||
|
||
Tcl_CreateExitHandler(SocketExitHandler, (ClientData) NULL);
|
||
|
||
/*
|
||
* Create the async notification window with a new class. We
|
||
* must create a new class to avoid a Windows 95 bug that causes
|
||
* us to get the wrong message number for socket events if the
|
||
* message window is a subclass of a static control.
|
||
*/
|
||
|
||
class.style = 0;
|
||
class.cbClsExtra = 0;
|
||
class.cbWndExtra = 0;
|
||
class.hInstance = TclWinGetTclInstance();
|
||
class.hbrBackground = NULL;
|
||
class.lpszMenuName = NULL;
|
||
class.lpszClassName = "DpSocket";
|
||
class.lpfnWndProc = SocketProc;
|
||
class.hIcon = NULL;
|
||
class.hCursor = NULL;
|
||
|
||
if (RegisterClass(&class)) {
|
||
hwnd = CreateWindow("DpSocket", "DpSocket", WS_TILED, 0, 0,
|
||
0, 0, NULL, NULL, class.hInstance, NULL);
|
||
} else {
|
||
hwnd = NULL;
|
||
}
|
||
if (hwnd == NULL) {
|
||
TclWinConvertError(GetLastError());
|
||
return;
|
||
}
|
||
Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL);
|
||
|
||
return;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* SockWatch --
|
||
*
|
||
* Sets up which events to watch for on this socket.
|
||
*
|
||
* Results:
|
||
* None
|
||
*
|
||
* Side effects:
|
||
* Events on this socket are now watched for.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
void
|
||
SockWatch (instanceData, mask)
|
||
ClientData instanceData;
|
||
int mask;
|
||
{
|
||
SocketState *statePtr = (SocketState *) instanceData;
|
||
SocketInfo *infoPtr = (SocketInfo *) instanceData;
|
||
|
||
/*
|
||
* Update the watch events mask.
|
||
*/
|
||
|
||
infoPtr->watchEvents = 0;
|
||
if (mask & TCL_READABLE) {
|
||
infoPtr->watchEvents |= (FD_READ|FD_CLOSE|FD_ACCEPT);
|
||
}
|
||
if (mask & TCL_WRITABLE) {
|
||
infoPtr->watchEvents |= (FD_WRITE);
|
||
}
|
||
|
||
/*
|
||
* If there are any conditions already set, then tell
|
||
* the notifier to poll rather than block.
|
||
*/
|
||
|
||
if (infoPtr->readyEvents & infoPtr->watchEvents) {
|
||
Tcl_Time blockTime = { 0, 0 };
|
||
Tcl_SetMaxBlockTime(&blockTime);
|
||
}
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* SockBlockMode --
|
||
*
|
||
* Sets the socket to blocking or non-blocking.
|
||
*
|
||
* Results:
|
||
* Zero always.
|
||
*
|
||
* Side effects:
|
||
* None
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
SockBlockMode (instanceData, mode)
|
||
ClientData instanceData; /* Pointer to SocketState struct */
|
||
int mode; /* TCL_MODE_BLOCKING or TCL_MODE_NONBLOCKING */
|
||
{
|
||
SocketState *statePtr = (SocketState *)instanceData;
|
||
SocketInfo *infoPtr = statePtr->sockInfo;
|
||
|
||
if (mode == TCL_MODE_NONBLOCKING) {
|
||
infoPtr->flags |= SOCKET_ASYNC;
|
||
} else {
|
||
infoPtr->flags &= ~(SOCKET_ASYNC);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* SockClose --
|
||
*
|
||
* This function is called by the Tcl channel driver when the
|
||
* caller want to close the socket. It releases the instanceData
|
||
* and closes the socket. All queued output will have been flushed
|
||
* to the device before this function is called.
|
||
*
|
||
* Results:
|
||
* Zero for success, otherwise a nonzero POSIX error code and,
|
||
* if interp is not NULL, an error message in interp->result
|
||
*
|
||
* Side effects:
|
||
* None
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
SockClose (instanceData, interp)
|
||
ClientData instanceData; /* (in) Pointer to tcpState struct */
|
||
Tcl_Interp *interp; /* (in) For error reporting */
|
||
{
|
||
SocketState *statePtr = (SocketState *)instanceData;
|
||
SocketInfo *infoPtr = statePtr->sockInfo;
|
||
SocketInfo **nextPtrPtr;
|
||
int result;
|
||
|
||
result = closesocket(statePtr->sock);
|
||
if ((result != 0) && (interp != NULL)) {
|
||
DppGetErrno();
|
||
Tcl_SetResult(interp, Tcl_PosixError(interp), TCL_STATIC);
|
||
}
|
||
|
||
for (nextPtrPtr = &dpSocketList; (*nextPtrPtr) != NULL;
|
||
nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
|
||
if ((*nextPtrPtr) == infoPtr) {
|
||
(*nextPtrPtr) = infoPtr->nextPtr;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* IPM only
|
||
*/
|
||
|
||
if (statePtr->flags & SOCKET_IPM) {
|
||
Tcl_DStringFree(&statePtr->groupList);
|
||
}
|
||
|
||
ckfree((char *) infoPtr);
|
||
ckfree((char *) statePtr);
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* SockGetFile --
|
||
*
|
||
* Called from Tcl_GetChannelFile to retrieve the handle
|
||
* from inside a socket based channel.
|
||
*
|
||
* Results:
|
||
* TCL_OK
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
int
|
||
SockGetFile(instanceData, direction, handlePtr)
|
||
ClientData instanceData;
|
||
int direction;
|
||
FileHandle *handlePtr;
|
||
{
|
||
SocketState *statePtr = (SocketState *)instanceData;
|
||
|
||
*handlePtr = statePtr->sockFile;
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* UdpIpmOutput --
|
||
*
|
||
* This function is called by the Tcl channel driver whenever
|
||
* the user wants to send output to a UDP/IPM socket.
|
||
* The function writes toWrite bytes from buf to the socket.
|
||
*
|
||
* Results:
|
||
* A nonnegative integer indicating how many bytes were written
|
||
* to the socket. The return value is normally the same as toWrite,
|
||
* but may be less in some cases such as if the output operation
|
||
* is interrupted by a signal.
|
||
*
|
||
* Side effects:
|
||
* None
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
int
|
||
UdpIpmOutput(instanceData, buf, toWrite, errorCodePtr)
|
||
ClientData instanceData; /* (in) Pointer to udpState struct */
|
||
char *buf; /* (in) Buffer to write */
|
||
int toWrite; /* (in) Number of bytes to write */
|
||
int *errorCodePtr; /* (out) POSIX error code (if any) */
|
||
{
|
||
SocketState *statePtr = (SocketState *) instanceData;
|
||
SocketInfo *infoPtr = statePtr->sockInfo;
|
||
int result;
|
||
int error;
|
||
|
||
while (1) {
|
||
#ifdef UDPDEBUG
|
||
{
|
||
char msg[1024];
|
||
memcpy(msg, buf, toWrite);
|
||
msg[toWrite] = '\0';
|
||
printf("Sending UDP data: %s\n", msg);
|
||
}
|
||
#endif
|
||
result = sendto(statePtr->sock, buf, toWrite, 0,
|
||
(struct sockaddr *) &statePtr->sockaddr,
|
||
sizeof(statePtr->sockaddr));
|
||
if (result != SOCKET_ERROR) {
|
||
/*
|
||
* Since Windows won't generate a new write event until we hit
|
||
* an overflow condition, we need to force the event loop to
|
||
* poll until the condition changes.
|
||
*/
|
||
|
||
if (infoPtr->watchEvents & FD_WRITE) {
|
||
Tcl_Time blockTime = { 0, 0 };
|
||
Tcl_SetMaxBlockTime(&blockTime);
|
||
}
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* Check for error condition or overflow. In the event of overflow, we
|
||
* need to clear the FD_WRITE flag so we can detect the next writable
|
||
* event. Note that Windows only sends a new writable event after a
|
||
* send fails with WSAEWOULDBLOCK.
|
||
*/
|
||
|
||
error = WSAGetLastError();
|
||
if (error == WSAEWOULDBLOCK) {
|
||
infoPtr->readyEvents &= ~(FD_WRITE);
|
||
if (infoPtr->flags & SOCKET_ASYNC) {
|
||
*errorCodePtr = EWOULDBLOCK;
|
||
return -1;
|
||
}
|
||
} else {
|
||
TclWinConvertWSAError(error);
|
||
*errorCodePtr = Tcl_GetErrno();
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* In the blocking case, wait until the file becomes writable
|
||
* or closed and try again.
|
||
*/
|
||
|
||
if (!WaitForSocketEvent(infoPtr, FD_WRITE|FD_CLOSE, errorCodePtr)) {
|
||
return -1;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
#endif
|
||
|
||
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* DppSetBlock --
|
||
*
|
||
* Put the socket into a blocking or non-blocking state.
|
||
* Note that the way Microsoft phrases it, turning off
|
||
* blocking requires a "1" (enabling non-blocking mode...)
|
||
* <sigh>
|
||
*
|
||
* Results:
|
||
* None
|
||
*
|
||
* Side effects:
|
||
* None
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
int
|
||
DppSetBlock (sock, block)
|
||
DpSocket sock;
|
||
int block;
|
||
{
|
||
int result;
|
||
u_long val = 0;
|
||
|
||
if (block) {
|
||
/* Set blocking mode */
|
||
result = ioctlsocket(sock, FIONBIO, &val);
|
||
} else {
|
||
/* Set non-blocking mode */
|
||
val = 1;
|
||
result = ioctlsocket(sock, FIONBIO, &val);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* DppCloseSocket --
|
||
*
|
||
* Closes the Windows socket.
|
||
*
|
||
* Results:
|
||
* None
|
||
*
|
||
* Side effects:
|
||
* None
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
int DppCloseSocket(sock)
|
||
DpSocket sock;
|
||
{
|
||
return closesocket(sock);
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* DppGetErrno --
|
||
*
|
||
* Returns the value of the errno variable for the last error
|
||
* that occurred.
|
||
*
|
||
* Results:
|
||
* POSIX error number.
|
||
*
|
||
* Side effects:
|
||
* Changes the Tcl global variable errno.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
int
|
||
DppGetErrno ()
|
||
{
|
||
int err;
|
||
int posix;
|
||
|
||
err = WSAGetLastError();
|
||
TclWinConvertWSAError(err);
|
||
posix = Tcl_GetErrno();
|
||
return posix;
|
||
}
|