📄 sockgen.c
字号:
/* * sockGen.c -- Posix Socket support module for general posix use * * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. * * $Id: sockGen.c,v 1.6 2003/04/11 18:00:12 bporter Exp $ *//******************************** Description *********************************//* * Posix Socket Module. This supports blocking and non-blocking buffered * socket I/O. */#if (!defined (WIN) || defined (LITTLEFOOT) || defined (WEBS))/********************************** Includes **********************************/#ifndef CE#include <errno.h>#include <fcntl.h>#include <string.h>#include <stdlib.h>#endif#ifdef UEMF #include "uemf.h"#else #include <socket.h> #include <types.h> #include <unistd.h> #include "emfInternal.h"#endif#ifdef VXWORKS #include <hostLib.h>#endif/************************************ Locals **********************************/extern socket_t **socketList; /* List of open sockets */extern int socketMax; /* Maximum size of socket */extern int socketHighestFd; /* Highest socket fd opened */static int socketOpenCount = 0; /* Number of task using sockets *//***************************** Forward Declarations ***************************/static void socketAccept(socket_t *sp);static int socketDoEvent(socket_t *sp);static int tryAlternateConnect(int sock, struct sockaddr *sockaddr);/*********************************** Code *************************************//* * Open socket module */int socketOpen(){#if (defined (CE) || defined (WIN)) WSADATA wsaData;#endif if (++socketOpenCount > 1) { return 0; }#if (defined (CE) || defined (WIN)) if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) { return -1; } if (wsaData.wVersion != MAKEWORD(1,1)) { WSACleanup(); return -1; }#endif socketList = NULL; socketMax = 0; socketHighestFd = -1; return 0;}/******************************************************************************//* * Close the socket module, by closing all open connections */void socketClose(){ int i; if (--socketOpenCount <= 0) { for (i = socketMax; i >= 0; i--) { if (socketList && socketList[i]) { socketCloseConnection(i); } } socketOpenCount = 0; }}/******************************************************************************//* * Open a client or server socket. Host is NULL if we want server capability. */int socketOpenConnection(char *host, int port, socketAccept_t accept, int flags){#if (!defined (NO_GETHOSTBYNAME) && !defined (VXWORKS)) struct hostent *hostent; /* Host database entry */#endif /* ! (NO_GETHOSTBYNAME || VXWORKS) */ socket_t *sp; struct sockaddr_in sockaddr; int sid, bcast, dgram, rc; if (port > SOCKET_PORT_MAX) { return -1; }/* * Allocate a socket structure */ if ((sid = socketAlloc(host, port, accept, flags)) < 0) { return -1; } sp = socketList[sid]; a_assert(sp);/* * Create the socket address structure */ memset((char *) &sockaddr, '\0', sizeof(struct sockaddr_in)); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons((short) (port & 0xFFFF)); if (host == NULL) { sockaddr.sin_addr.s_addr = INADDR_ANY; } else { sockaddr.sin_addr.s_addr = inet_addr(host); if (sockaddr.sin_addr.s_addr == INADDR_NONE) {/* * If the OS does not support gethostbyname functionality, the macro: * NO_GETHOSTBYNAME should be defined to skip the use of gethostbyname. * Unfortunatly there is no easy way to recover, the following code * simply uses the basicGetHost IP for the sockaddr. */#ifdef NO_GETHOSTBYNAME if (strcmp(host, basicGetHost()) == 0) { sockaddr.sin_addr.s_addr = inet_addr(basicGetAddress()); } if (sockaddr.sin_addr.s_addr == INADDR_NONE) { socketFree(sid); return -1; }#elif (defined (VXWORKS)) sockaddr.sin_addr.s_addr = (unsigned long) hostGetByName(host); if (sockaddr.sin_addr.s_addr == NULL) { errno = ENXIO; socketFree(sid); return -1; }#else hostent = gethostbyname(host); if (hostent != NULL) { memcpy((char *) &sockaddr.sin_addr, (char *) hostent->h_addr_list[0], (size_t) hostent->h_length); } else { char *asciiAddress; char_t *address; address = basicGetAddress(); asciiAddress = ballocUniToAsc(address, gstrlen(address)); sockaddr.sin_addr.s_addr = inet_addr(asciiAddress); bfree(B_L, asciiAddress); if (sockaddr.sin_addr.s_addr == INADDR_NONE) { errno = ENXIO; socketFree(sid); return -1; } }#endif /* (NO_GETHOSTBYNAME || VXWORKS) */ } } bcast = sp->flags & SOCKET_BROADCAST; if (bcast) { sp->flags |= SOCKET_DATAGRAM; } dgram = sp->flags & SOCKET_DATAGRAM;/* * Create the socket. Support for datagram sockets. Set the close on * exec flag so children don't inherit the socket. */ sp->sock = socket(AF_INET, dgram ? SOCK_DGRAM: SOCK_STREAM, 0); if (sp->sock < 0) { socketFree(sid); return -1; }#ifndef __NO_FCNTL fcntl(sp->sock, F_SETFD, FD_CLOEXEC);#endif socketHighestFd = max(socketHighestFd, sp->sock);/* * If broadcast, we need to turn on broadcast capability. */ if (bcast) { int broadcastFlag = 1; if (setsockopt(sp->sock, SOL_SOCKET, SO_BROADCAST, (char *) &broadcastFlag, sizeof(broadcastFlag)) < 0) { socketFree(sid); return -1; } }/* * Host is set if we are the client */ if (host) {/* * Connect to the remote server in blocking mode, then go into * non-blocking mode if desired. */ if (!dgram) { if (! (sp->flags & SOCKET_BLOCK)) {/* * sockGen.c is only used for Windows products when blocking * connects are expected. This applies to FieldUpgrader * agents and open source webserver connectws. Therefore the * asynchronous connect code here is not compiled. */#if (defined (WIN) || defined (CE)) && (!defined (LITTLEFOOT) && !defined (WEBS)) int flag; sp->flags |= SOCKET_ASYNC;/* * Set to non-blocking for an async connect */ flag = 1; if (ioctlsocket(sp->sock, FIONBIO, &flag) == SOCKET_ERROR) { socketFree(sid); return -1; }#else socketSetBlock(sid, 1);#endif /* #if (WIN || CE) && !(LITTLEFOOT || WEBS) */ } if ((rc = connect(sp->sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr))) < 0 && (rc = tryAlternateConnect(sp->sock, (struct sockaddr *) &sockaddr)) < 0) {#if (defined (WIN) || defined (CE)) if (socketGetError() != EWOULDBLOCK) { socketFree(sid); return -1; }#else socketFree(sid); return -1;#endif /* WIN || CE */ } } } else {/* * Bind to the socket endpoint and the call listen() to start listening */ rc = 1; setsockopt(sp->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&rc, sizeof(rc)); if (bind(sp->sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr)) < 0) { socketFree(sid); return -1; } if (! dgram) { if (listen(sp->sock, SOMAXCONN) < 0) { socketFree(sid); return -1; }#ifndef UEMF sp->fileHandle = emfCreateFileHandler(sp->sock, SOCKET_READABLE, (emfFileProc *) socketAccept, (void *) sp);#else sp->flags |= SOCKET_LISTENING;#endif } sp->handlerMask |= SOCKET_READABLE; }/* * Set the blocking mode */ if (flags & SOCKET_BLOCK) { socketSetBlock(sid, 1); } else { socketSetBlock(sid, 0); } return sid;}/******************************************************************************//* * If the connection failed, swap the first two bytes in the * sockaddr structure. This is a kludge due to a change in * VxWorks between versions 5.3 and 5.4, but we want the * product to run on either. */static int tryAlternateConnect(int sock, struct sockaddr *sockaddr){#ifdef VXWORKS char *ptr; ptr = (char *)sockaddr; *ptr = *(ptr+1); *(ptr+1) = 0; return connect(sock, sockaddr, sizeof(struct sockaddr));#else return -1;#endif /* VXWORKS */}/******************************************************************************//* * Close a socket */void socketCloseConnection(int sid){ socket_t *sp; if ((sp = socketPtr(sid)) == NULL) { return; } socketFree(sid);}/******************************************************************************//* * Accept a connection. Called as a callback on incoming connection. */static void socketAccept(socket_t *sp){ struct sockaddr_in addr; socket_t *nsp; size_t len; char *pString; int newSock, nid;#ifdef NW NETINET_DEFINE_CONTEXT;#endif a_assert(sp);/* * Accept the connection and prevent inheriting by children (F_SETFD) */ len = sizeof(struct sockaddr_in); if ((newSock = accept(sp->sock, (struct sockaddr *) &addr, (int *) &len)) < 0) { return; }#ifndef __NO_FCNTL fcntl(newSock, F_SETFD, FD_CLOEXEC);#endif socketHighestFd = max(socketHighestFd, newSock);/* * Create a socket structure and insert into the socket list */ nid = socketAlloc(sp->host, sp->port, sp->accept, sp->flags); nsp = socketList[nid]; a_assert(nsp); nsp->sock = newSock; nsp->flags &= ~SOCKET_LISTENING; if (nsp == NULL) { return; }/* * Set the blocking mode before calling the accept callback. */ socketSetBlock(nid, (nsp->flags & SOCKET_BLOCK) ? 1: 0);/* * Call the user accept callback. The user must call socketCreateHandler * to register for further events of interest. */ if (sp->accept != NULL) { pString = inet_ntoa(addr.sin_addr); if ((sp->accept)(nid, pString, ntohs(addr.sin_port), sp->sid) < 0) { socketFree(nid); }#ifdef VXWORKS free(pString);#endif }}/******************************************************************************//* * Get more input from the socket and return in buf. * Returns 0 for EOF, -1 for errors and otherwise the number of bytes read. */int socketGetInput(int sid, char *buf, int toRead, int *errCode){ struct sockaddr_in server; socket_t *sp; int len, bytesRead; a_assert(buf); a_assert(errCode); *errCode = 0; if ((sp = socketPtr(sid)) == NULL) { return -1; }/* * If we have previously seen an EOF condition, then just return */ if (sp->flags & SOCKET_EOF) { return 0; }#if ((defined (WIN) || defined (CE)) && (!defined (LITTLEFOOT) && !defined (WEBS))) if ( !(sp->flags & SOCKET_BLOCK) && ! socketWaitForEvent(sp, FD_CONNECT, errCode)) { return -1; }#endif/* * Read the data */ if (sp->flags & SOCKET_DATAGRAM) { len = sizeof(server); bytesRead = recvfrom(sp->sock, buf, toRead, 0, (struct sockaddr *) &server, &len); } else { bytesRead = recv(sp->sock, buf, toRead, 0); } /* * BUG 01865 -- CPU utilization hangs on Windows. The original code used * the 'errno' global variable, which is not set by the winsock functions * as it is under *nix platforms. We use the platform independent * socketGetError() function instead, which does handle Windows correctly. * Other, *nix compatible platforms should work as well, since on those * platforms, socketGetError() just returns the value of errno. * Thanks to Jonathan Burgoyne for the fix. */ if (bytesRead < 0) { *errCode = socketGetError(); if (*errCode == ECONNRESET) { sp->flags |= SOCKET_CONNRESET; return 0; } return -1; } return bytesRead;}/******************************************************************************//* * Process an event on the event queue */#ifndef UEMFstatic int socketEventProc(void *data, int mask){ socket_t *sp; ringq_t *rq; int sid; sid = (int) data; a_assert(sid >= 0 && sid < socketMax); a_assert(socketList[sid]); if ((sp = socketPtr(sid)) == NULL) { return 1; }/* * If now writable and flushing in the background, continue flushing */ if (mask & SOCKET_WRITABLE) { if (sp->flags & SOCKET_FLUSHING) { rq = &sp->outBuf; if (ringqLen(rq) > 0) { socketFlush(sp->sid); } else { sp->flags &= ~SOCKET_FLUSHING; } } }/* * Now invoke the users socket handler. NOTE: the handler may delete the * socket, so we must be very careful after calling the handler. */ if (sp->handler && (sp->handlerMask & mask)) { (sp->handler)(sid, mask & sp->handlerMask, sp->handler_data); } if (socketList && sid < socketMax && socketList[sid] == sp) { socketRegisterInterest(sp, sp->handlerMask); } return 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -