?? httcp.c
字號:
/* HTTCP.c** TCP SPECIFIC CODE**** (c) COPYRIGHT MIT 1995.** Please first read the full copyright statement in the file COPYRIGH.** @(#) $Id: HTTCP.c,v 2.119 2000/08/02 10:48:12 kahan Exp $**** This code is in common between client and server sides.**** 16 Jan 92 TBL Fix strtol() undefined on CMU Mach.** 25 Jun 92 JFG Added DECNET option through TCP socket emulation.** 13 Sep 93 MD Added correct return of vmserrorno for HTInetStatus.** Added decoding of vms error message for MULTINET.** 31 May 94 HF Added cache on host id's; now use inet_ntoa() to** HTInetString and some other fixes. Added HTDoConnect** and HTDoAccept*//* Library include files */#include "wwwsys.h"#include "WWWUtil.h"#include "WWWCore.h"#include "HTReqMan.h"#include "HTNetMan.h"#include "HTTCP.h" /* Implemented here */#include "HTHstMan.h"/* VMS stuff */#ifdef VMS#ifndef MULTINET#define FD_SETSIZE 32#else /* Multinet */#define FD_SETSIZE 256#endif /* Multinet */#endif /* VMS *//* Macros and other defines *//* x ms penalty on a multi-homed host if IP-address is unreachable */#define TCP_DELAY 30000/* x ms penalty on a multi-homed host if IP-address is down for unknown reason */#define TCP_PENALTY 60000/* empirical study in socket call error codes yovavm@contact.com : added handling for WSAEINVAL error code (Windows) "When calling connect() in the second time, after the first call to connect() returned WSAEWOULDBLOCK, an error of WSAEINVAL is returned. It happens often on WinNT & Win95, and rarely on Win2K & Win98, where in most cases the second call to connect() returns WSAEISCON (10056). jose@w3.org : didn't add that test for Unix, as the connect() doc (Linux and Solaris) says it's not needed. */#ifdef _WINSOCKAPI_ /* windows */#define NETCALL_ERROR(ret) (ret == SOCKET_ERROR)#define NETCALL_DEADSOCKET(err) (err == WSAEBADF)#define NETCALL_WOULDBLOCK(err) (err == WSAEWOULDBLOCK)#define NETCALL_INVAL(err) (err == WSAEINVAL)#else /* _WINSOCKAPI_ unix */#define NETCALL_ERROR(ret) (ret < 0)#define NETCALL_DEADSOCKET(err) (err == EBADF)#if defined(EAGAIN) && defined(EALREADY)#define NETCALL_WOULDBLOCK(err) (err == EINPROGRESS || \ err == EALREADY || \ err == EAGAIN)#else /* (EAGAIN && EALREADY) */#ifdef EALREADY#define NETCALL_WOULDBLOCK(err) (err == EINPROGRESS || err == EALREADY)#else /* EALREADY */#ifdef EAGAIN#define NETCALL_WOULDBLOCK(err) (err == EINPROGRESS || err == EAGAIN)#else /* EAGAIN */#define NETCALL_WOULDBLOCK(err) (err == EINPROGRESS)#endif /* !EAGAIN */#endif /* !EALREADY */#endif /* !(EAGAIN && EALREADY) */#endif /* !_WINSOCKAPI_ done */#if defined(__svr4__) || defined (_WINSOCKAPI_)#define HT_HOSTUNREACHABLE(e) ((e)==ECONNREFUSED || (e)==ETIMEDOUT || \ (e)==ENETUNREACH || (e)==EHOSTUNREACH || \ (e)==EHOSTDOWN)#else#define HT_HOSTUNREACHABLE(e) ((e)==ECONNREFUSED || (e)==ETIMEDOUT || \ (e)==ENETUNREACH || (e)==EHOSTUNREACH || \ (e)==EHOSTDOWN || (e)==EINVAL)#endif/* ------------------------------------------------------------------------- *//* CONNECTION ESTABLISHMENT MANAGEMENT *//* ------------------------------------------------------------------------- *//* _makeSocket - create a socket, if !preemptive, set FIONBIO** returns sockfd or INVSOC if error*/PRIVATE int _makeSocket (HTHost * host, HTRequest * request, int preemptive){ int status = 1; SOCKET sockfd = INVSOC;#ifdef DECNET if ((sockfd=socket(AF_DECnet, SOCK_STREAM, 0))==INVSOC)#else if ((sockfd=socket(AF_INET, SOCK_STREAM,IPPROTO_TCP))==INVSOC)#endif { HTRequest_addSystemError(request, ERR_FATAL, socerrno, NO, "socket"); return INVSOC; } HTTRACE(PROT_TRACE, "Socket...... Created %d\n" _ sockfd); /* Increase the number of sockets by one */ HTNet_increaseSocket(); /* ** If we have compiled without Nagle's algorithm then try and turn ** it off now */#if defined(HT_NO_NAGLE) && defined(HAVE_SETSOCKOPT) && defined(TCP_NODELAY) { int disable = 1; status = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &disable, sizeof(int)); if (status == -1) { HTTRACE(PROT_TRACE, "Socket...... Could not disable Nagle's algorithm - error %d\n" _ sockfd); } else { HTTRACE(PROT_TRACE, "Socket...... Turned off Nagle's algorithm\n"); } }#endif /* If non-blocking protocol then change socket status ** I use fcntl() so that I can ask the status before I set it. ** See W. Richard Stevens (Advan. Prog. in UNIX environment, p.364) ** Be CAREFULL with the old `O_NDELAY' - it will not work as read() ** returns 0 when blocking and NOT -1. FNDELAY is ONLY for BSD and ** does NOT work on SVR4 systems. O_NONBLOCK is POSIX. */ if (!preemptive) {#ifdef _WINSOCKAPI_ { /* begin windows scope */ long levents = FD_READ | FD_WRITE | FD_ACCEPT | FD_CONNECT | FD_CLOSE ; int rv = 0 ; u_long one = 1; status = ioctlsocket(sockfd, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0; } /* end scope */#else /* _WINSOCKAPI_ */#if defined(VMS) { int enable = 1; status = IOCTL(sockfd, FIONBIO, &enable); }#else /* VMS */ if((status = fcntl(sockfd, F_GETFL, 0)) != -1) {#ifdef O_NONBLOCK status |= O_NONBLOCK; /* POSIX */#else /* O_NONBLOCK */#ifdef F_NDELAY status |= F_NDELAY; /* BSD */#endif /* F_NDELAY */#endif /* !O_NONBLOCK */ status = fcntl(sockfd, F_SETFL, status); }#endif /* !VMS */#endif /* !_WINSOCKAPI_ */ HTTRACE(PROT_TRACE, "Socket...... %slocking socket\n" _ status == -1 ? "B" : "Non-b"); } else HTTRACE(PROT_TRACE, "Socket...... Blocking socket\n"); return sockfd;}/*** for this host/channel*/PRIVATE BOOL createChannelAndTransportStreams (HTHost * host, SOCKET sockfd, HTTransport * trans){ if (host && sockfd!=INVSOC && trans) { HTHost_setChannel(host, HTChannel_new(sockfd, NULL, YES)); HTHost_getInput(host, trans, NULL, 0); HTHost_getOutput(host, trans, NULL, 0); return YES; } return NO;}/* HTDoConnect()** Note: Any port indication in URL, e.g., as `host:port' overwrites** the default port value.**** returns HT_ERROR Error has occured or interrupted** HT_OK if connected** HT_WOULD_BLOCK if operation would have blocked*/PUBLIC int HTDoConnect (HTNet * net){ HTHost * host = HTNet_host(net); HTRequest * request = HTNet_request(net); char * hostname = HTHost_name(host); int preemptive = net->preemptive; int status = HT_OK; /* Jump into the state machine */ while (1) { switch (host->tcpstate) { case TCP_BEGIN: { /* ** Add the net object to the host object found above. If the ** host is idle then we can start the request right away, ** otherwise we must wait until it is free. */ if ((status = HTHost_addNet(host, net)) == HT_PENDING) HTTRACE(PROT_TRACE, "HTDoConnect. Pending...\n"); /* ** If we are pending then return here, otherwise go to next state ** which is setting up a channel */ host->tcpstate = TCP_CHANNEL; HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_CHANNEL.\n" _ host); if (status == HT_PENDING) return HT_PENDING; } break; case TCP_CHANNEL: /* ** The next state depends on whether we have a connection ** or not - if so then we can jump directly to connect() to ** test it - otherwise we must around DNS to get the name ** Resolved */ if (HTHost_channel(host) == NULL) { host->tcpstate = TCP_DNS; HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_DNS.\n" _ host); } else { /* ** There is now one more using the channel */ HTChannel_upSemaphore(host->channel); /* ** We are now all set and can jump to connected mode */ host->tcpstate = TCP_CONNECTED; HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_CONNECTED.\n" _ host); } hostname = HTHost_name(host); break; case TCP_DNS: if ((status = HTParseInet(host, hostname, request)) < 0) { HTTRACE(PROT_TRACE, "HTDoConnect. Can't locate `%s\'\n" _ hostname); HTRequest_addError(request, ERR_FATAL, NO, HTERR_NO_REMOTE_HOST, (void *) hostname, strlen(hostname), "HTDoConnect"); host->tcpstate = TCP_DNS_ERROR; HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_ERROR.\n" _ host); break; } if (!HTHost_retry(host) && status > 1) /* If multiple homes */ HTHost_setRetry(host, status); host->tcpstate = TCP_NEED_SOCKET; HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_NEED_SOCKET.\n" _ host); break; case TCP_NEED_SOCKET: { SOCKET sockfd; /* Create a new socket */ if ((sockfd = _makeSocket(host, request, preemptive)) == INVSOC) { host->tcpstate = TCP_ERROR; break; } /* Create channnel and streams */ createChannelAndTransportStreams (host, sockfd, net->transport); /* If multi-homed host then start timer on connection */ if (HTHost_retry(host)) host->connecttime = HTGetTimeInMillis(); /* Progress notification */ { HTAlertCallback *cbf = HTAlert_find(HT_PROG_CONNECT); if (cbf) (*cbf)(request, HT_PROG_CONNECT, HT_MSG_NULL, NULL, hostname, NULL); } host->tcpstate = TCP_NEED_CONNECT; HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_NEED_CONNECT.\n" _ host); break; } case TCP_NEED_CONNECT:#ifdef _WINSOCKAPI_ /* 2000/08/02 Jens Meggers (jens@meggers.com): ** In HTDoConnect(), the connect command is done before the ** WSAAsyncSelect() that is called when ** HTHost_register(host, net, HTEvent_CONNECT); is executed. ** Although that is in line with the WinSock2 and Microsoft ** documentation, it does _not_ work all the time. I have done ** extensive tests on Win2000 and Win 4.0 SP5. In very rare cases, ** the connect is finished between the connect() command itself and ** the WSAAsyncSelect(). In this unlikely case, WinSock does not ** (always) send the FD_CONNECT message. As a result, when using ** the Async mode, the event loop hangs because there is no ** timeout procedure registered for FD_CONNECT. ** JK: what happens if status returns an error? Do we have to ** unregister the HTEvent_CONNECT event then? */ HTHost_register(host, net, HTEvent_CONNECT);#endif /* _WINSOCKAPI_ */ status = connect(HTChannel_socket(host->channel), (struct sockaddr *) &host->sock_addr, sizeof(host->sock_addr)); /* * According to the Sun man page for connect: * EINPROGRESS The socket is non-blocking and the con- * nection cannot be completed immediately. * It is possible to select(2) for comple- * tion by selecting the socket for writ- * ing. * According to the Motorola SVR4 man page for connect: * EAGAIN The socket is non-blocking and the con- * nection cannot be completed immediately. * It is possible to select for completion * by selecting the socket for writing. * However, this is only possible if the * socket STREAMS module is the topmost * module on the protocol stack with a * write service procedure. This will be * the normal case. */ if (NETCALL_ERROR(status)) { if (NETCALL_WOULDBLOCK(socerrno)) {
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -