?? 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 UEMF
static 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 + -