// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (C) Microsoft Corporation. All Rights Reserved. // // Module: // sender.cpp // // Abstract: // Implement send-side functionality. The sender uses a combination of // WSASend and WSAEventSelect to support sending of data on the socket. // // Entry Points: // Sender - send contents of data buffer to receiver program. // #include "atmevent.h" #define WSAEVENTSELECT_EVENT 0 #define WSASEND_EVENT 1 #define TOTAL_EVENTS 2 // encapsulate socket, events, buffers, and various flags and counters that // control how/when data is sent into a structure typedef struct _SEND_INFO { SOCKET sd; WSAEVENT hEvents[2]; // WSASend and WSAEventSelect events WSAOVERLAPPED ovr; BOOL bSendEnabled; BOOL bSendComplete; int nTotalSends; WSABUF sndbuf; } SEND_INFO; static BOOL CreateSendingSocket(SEND_INFO *, OPTIONS *); static BOOL fd_connect_func(DWORD, WSANETWORKEVENTS *, SEND_INFO *, OPTIONS *); static BOOL fd_close_func(DWORD, WSANETWORKEVENTS *, SEND_INFO *, OPTIONS *); static BOOL PostSend(SEND_INFO *, OPTIONS *); static BOOL CompleteSend(SEND_INFO *, OPTIONS *); // Abstract: // Main function to establish connection to a server and then // send data to server. WSASend and WSAEventSelect are used to send data // and monitor socket activity, respectively. // // Due to the nature of this application - just sending data in a loop - // FD_WRITE is not suitable whereas using WSASend with overlapped I/O is. // The reason is because FD_WRITE will only be delivered after the inital // connection has been established and only after sends block would // WSAEWOULDBLOCK and buffer space becomes available once again. Thus // WSAWaitForMultipleEvents waits on the event associated with overlapped // structure passed to WSASend AND the event associated with WSAEventSelect. // VOID Sender( OPTIONS *pOptions ) { WSANETWORKEVENTS NetworkEvents = {0}; BOOL bProcessEventsDone = FALSE; SEND_INFO SendInfo = {0}; int i = 0; printf("Sender\n"); SendInfo.sd = INVALID_SOCKET; for (i = 0; i < TOTAL_EVENTS; i++) SendInfo.hEvents[i] = WSA_INVALID_EVENT; for (i=0; ibuf; SendInfo.sndbuf.len = pOptions->nBufSize; SendInfo.nTotalSends = 0; SendInfo.bSendEnabled = TRUE; if (!CreateSendingSocket(&SendInfo, pOptions)) goto CLEANUP; while (!bProcessEventsDone) { DWORD dwEvent; dwEvent = WSAWaitForMultipleEvents(TOTAL_EVENTS, SendInfo.hEvents, FALSE, WSA_INFINITE, FALSE); switch (dwEvent) { case WSA_WAIT_FAILED: printf("WSAEventSelect: %d\n", WSAGetLastError()); break; case WAIT_IO_COMPLETION: case WSA_WAIT_TIMEOUT: break; default: dwEvent -= WSA_WAIT_EVENT_0; if (WSAEVENTSELECT_EVENT == dwEvent) { // lets see what network activity trigged this event if (SOCKET_ERROR == WSAEnumNetworkEvents(SendInfo.sd, SendInfo.hEvents[WSAEVENTSELECT_EVENT], &NetworkEvents)) { printf("WSAEnumNetworkEvent: failed w/%d lNetworkEvent %X\n", WSAGetLastError(), NetworkEvents.lNetworkEvents); NetworkEvents.lNetworkEvents = 0; } else { if (FD_CONNECT & NetworkEvents.lNetworkEvents) bProcessEventsDone |= fd_connect_func(dwEvent, &NetworkEvents, &SendInfo, pOptions); if (FD_CLOSE & NetworkEvents.lNetworkEvents) bProcessEventsDone |= fd_close_func(dwEvent, &NetworkEvents, &SendInfo, pOptions); } } else { bProcessEventsDone = CompleteSend(&SendInfo, pOptions); } } // allow pauses between notifications if (pOptions->dwSleep) Sleep(pOptions->dwSleep); } // while (!bProcessEventsDone) CLEANUP: if (INVALID_SOCKET != SendInfo.sd) { closesocket(SendInfo.sd); SendInfo.sd = INVALID_SOCKET; } for (i=0; isd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &pOptions->protocolInfo, 0, dwSocketFlags); if (INVALID_SOCKET == pSendInfo->sd) { printf("socket failed w/%d", WSAGetLastError()); return FALSE; } // set up the address structure of the destination which is needed on connect // and/or sendto operations. ZeroMemory(&sockaddr, sizeof(sockaddr)); nAddrLen = sizeof(sockaddr); nRet = WSAStringToAddress(pOptions->szRemoteInterface, AF_ATM, &pOptions->protocolInfo, (LPSOCKADDR)&sockaddr, &nAddrLen); if (SOCKET_ERROR == nRet) { printf("WSAAddressToString: failed w/%d\n", WSAGetLastError()); return FALSE; } // fill in remainder of ATM address structure not set using WSAStringToAddress sockaddr.satm_family = AF_ATM; sockaddr.satm_number.AddressType = ATM_NSAP; sockaddr.satm_number.NumofDigits = ATM_ADDR_SIZE; sockaddr.satm_blli.Layer2Protocol = SAP_FIELD_ANY; sockaddr.satm_blli.Layer3Protocol = SAP_FIELD_ABSENT; sockaddr.satm_bhli.HighLayerInfoType = SAP_FIELD_ABSENT; lNetworkEvents = (FD_CONNECT | FD_CLOSE); if (SOCKET_ERROR == WSAEventSelect(pSendInfo->sd, pSendInfo->hEvents[WSAEVENTSELECT_EVENT], lNetworkEvents)) { printf("WSAEventSelect: failed w/%d\n", WSAGetLastError()); return FALSE; } pQos = NULL; nRet = WSAConnect(pSendInfo->sd, (LPSOCKADDR)&sockaddr, sizeof(sockaddr), NULL, NULL, pQos, NULL); if (SOCKET_ERROR == nRet) { DWORD dwErr = WSAGetLastError(); if (WSAEWOULDBLOCK != dwErr) { printf("WSAConnect: failed w/%d\n", dwErr); return FALSE; } } return TRUE; } // Abstract: // FD_CONNECT handler. This handler is invoked when an asynchronous connect // completes, successfully or otherwise. After the socket connects // additional notifications will be enabled using WSAEventSelect. // // static BOOL fd_connect_func( DWORD dwEvent, WSANETWORKEVENTS *NetworkEvents, SEND_INFO *pSendInfo, OPTIONS *pOptions ) { BOOL bProcessEventsDone = FALSE; int nRet = 0; printf(" FD_CONNECT: dwEvent=%d error code =%d\n", dwEvent, NetworkEvents->iErrorCode[FD_CONNECT_BIT]); if (NetworkEvents->iErrorCode[FD_CONNECT_BIT] != 0) bProcessEventsDone = TRUE; else { nRet = WSAEventSelect(pSendInfo->sd, pSendInfo->hEvents[WSAEVENTSELECT_EVENT], (FD_CLOSE)); if (SOCKET_ERROR == nRet) { printf("WSAEventSelect: failed w/%d\n", WSAGetLastError()); bProcessEventsDone = FALSE; } pSendInfo->bSendEnabled = TRUE; // post initial send bProcessEventsDone = PostSend(pSendInfo, pOptions); } return bProcessEventsDone; } // Abstract: // FD_CLOSE handler. If server closes before we have sent all the data, then // we are done. Winsock2 events are manual reset events and FD_CLOSE has no // re-enabling function, therefore must reset the event here, otherwise we would // get additional FD_CLOSE notifications. // static BOOL fd_close_func( DWORD dwEvent, WSANETWORKEVENTS *NetworkEvents, SEND_INFO *pSendInfo, OPTIONS *pOptions ) { BOOL bProcessEventsDone = TRUE; printf(" FD_CLOSE: dwEvent=%d error code =%d\n", dwEvent, NetworkEvents->iErrorCode[FD_CLOSE_BIT]); WSAResetEvent(pSendInfo->hEvents[WSAEVENTSELECT_EVENT]); if (pOptions->dwSleepClose) Sleep(pOptions->dwSleepClose); if (INVALID_SOCKET != pSendInfo->sd) { closesocket(pSendInfo->sd); pSendInfo->sd = INVALID_SOCKET; } return bProcessEventsDone; } // Abstract: // Post an overlapped send and indicate a send is in progress if it doesn't // complete immediately. If the WSASend completes immediately bump the // counters and indicate the send completed. The send flag is needed to // what needs to be done in CompleteSend() - wait for overlapped results // or post another send. // static BOOL PostSend( SEND_INFO *pSendInfo, OPTIONS *pOptions ) { int nRet = 0; DWORD cbBytesXfer = 0; BOOL bProcessEventsDone = FALSE; pOptions = NULL; printf(" PostSend\n"); if (pSendInfo->bSendEnabled) { pSendInfo->bSendComplete = FALSE; nRet = WSASend(pSendInfo->sd, &pSendInfo->sndbuf, 1, &cbBytesXfer, 0, &pSendInfo->ovr, NULL); if (0 == nRet) { // the send completed immediately pSendInfo->bSendComplete = TRUE; pSendInfo->sndbuf.len -= cbBytesXfer; pSendInfo->sndbuf.buf += cbBytesXfer; } else if (SOCKET_ERROR == nRet) { DWORD dwErr = WSAGetLastError(); if (WSA_IO_PENDING != dwErr) { printf("WSASend failed w/%d\n", dwErr); bProcessEventsDone = TRUE; } } } return bProcessEventsDone; } // Abstract: // Handle the completion of a WSASend. This may involve posting another // overlapped send or closing the socket. // static BOOL CompleteSend( SEND_INFO *pSendInfo, OPTIONS *pOptions ) { BOOL bProcessEventsDone = FALSE; DWORD dwFlags = 0; DWORD cbBytesXfer = 0; printf(" CompleteSend\n"); if (!pSendInfo->bSendComplete) { // handle getting overlapped results if (!WSAGetOverlappedResult(pSendInfo->sd, &pSendInfo->ovr, &cbBytesXfer, TRUE, &dwFlags)) { printf("WSAGetOverlappedResult = %d\n", WSAGetLastError()); bProcessEventsDone = TRUE; } else { pSendInfo->bSendComplete = TRUE; pSendInfo->sndbuf.len -= cbBytesXfer; pSendInfo->sndbuf.buf += cbBytesXfer; } } if (pSendInfo->sndbuf.len > 0) { PostSend(pSendInfo, pOptions); bProcessEventsDone = FALSE; } else { // we have sent the entire buffer, check repeat count to // see if buffer needs to be sent again. if (pOptions->nRepeat) pSendInfo->nTotalSends++; else pSendInfo->nTotalSends = -1; pSendInfo->sndbuf.buf = pOptions->buf; pSendInfo->sndbuf.len = pOptions->nBufSize; if (pSendInfo->nTotalSends <= pOptions->nRepeat-1) { PostSend(pSendInfo, pOptions); bProcessEventsDone = FALSE; } else { printf(" repeat count complete - closing\n"); if (pOptions->dwSleepClose) Sleep(pOptions->dwSleepClose); if (INVALID_SOCKET != pSendInfo->sd) { closesocket(pSendInfo->sd); pSendInfo->sd = INVALID_SOCKET; } bProcessEventsDone = TRUE; WSAResetEvent(pSendInfo->hEvents[WSASEND_EVENT]); } } return bProcessEventsDone; }