Mercurial > ~darius > hgwebdir.cgi > paradise_server
diff src/sockio.c @ 8:0836fb919dfa
First entry of Paradise Server 2.9 patch 10 Beta
author | darius |
---|---|
date | Sat, 06 Dec 1997 04:37:05 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sockio.c Sat Dec 06 04:37:05 1997 +0000 @@ -0,0 +1,440 @@ +/*-------------------------------------------------------------------------- +NETREK II -- Paradise + +Permission to use, copy, modify, and distribute this software and its +documentation, or any derivative works thereof, for any NON-COMMERCIAL +purpose and without fee is hereby granted, provided that this copyright +notice appear in all copies. No representations are made about the +suitability of this software for any purpose. This software is provided +"as is" without express or implied warranty. + + Xtrek Copyright 1986 Chris Guthrie + Netrek (Xtrek II) Copyright 1989 Kevin P. Smith + Scott Silvey + Paradise II (Netrek II) Copyright 1993 Larry Denys + Kurt Olsen + Brandon Gillespie +--------------------------------------------------------------------------*/ + + +#include "config.h" + +#include <errno.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> +#include <netinet/in.h> + +#include "data.h" +#include "packets.h" +#include "shmem.h" + +#define BUFSIZE 16738 +static char buf[BUFSIZE]; /* Socket buffer */ +static char *bufptr = buf; +#define UDPBUFSIZE 960 /* (tweakable; should be under 1300) */ +static char udpbuf[UDPBUFSIZE]; /* UDP socket buffer */ +static char *udpbufptr = udpbuf; +#ifdef DOUBLE_UDP +static char scbuf[UDPBUFSIZE]; /* semi-critical UDP socket buffer */ +static char *scbufptr = scbuf; /* (only used for double UDP) */ +#endif +static long sequence; /* the holy sequence number */ + +#define FAT_THRESH 500 /* if more than this, don't add fat */ + +extern int udpMode; + + +extern int clientDead; + +int +buffersEmpty() +{ + return bufptr == buf && + (commMode != COMM_UDP || udpbufptr == buf); +} + +void +resetUDPbuffer() +{ + if (udpbufptr != udpbuf) + { + udpbufptr = udpbuf; /* clear out any old data */ + sequence--; /* we just killed a sequence packet */ + } +} + +void +resetUDPsequence() +{ + sequence = 1; +} + +/* + * If we're in UDP mode, add a sequence number to the transmission buffer. + * Returns the #of bytes inserted. + * + * This will add a sequence # to transmissions on either channel. However, the + * current implementation doesn't put sequences on TCP transmissions because + * mixed TCP packets and UDP packets rarely arrive in the order in which they + * were sent. + */ +int +addSequence(outbuf) + char *outbuf; +{ + struct sequence_spacket *ssp; + + if (commMode != COMM_UDP || udpMode == MODE_TCP) + return (0); + + packets_sent++; + + ssp = (struct sequence_spacket *) outbuf; + ssp->type = SP_SEQUENCE; + ssp->sequence = htons((unsigned short) sequence); + sequence++; + + return (sizeof(struct sequence_spacket)); +} + +/* Flush the socket buffer */ +void +flushSockBuf() +{ + int cc; + + if (clientDead) + return; + if (bufptr != buf) + { + if ((cc = gwrite(sock, buf, bufptr - buf)) != bufptr - buf) + { + fprintf(stderr, "std flush gwrite failed (%d, error %d)\n", + cc, errno); + clientDead = 1; + } + bufptr = buf /* + addSequence(buf) */ ; + } + /* + * This is where we try to add fat. There's no point in checking at the + * other places which call gwrite(), because they only call it when the + * buffer is already full. + */ + if (udpSock >= 0 + && udpMode == MODE_FAT + && (udpbufptr - udpbuf) < FAT_THRESH) + fatten(); + + if (udpSock >= 0 && udpbufptr != udpbuf) + { +#ifdef BROKEN + /* debugging only!! */ + if (sequence % 5 == 0) + { + /* act as if we did the gwrite(), but don't */ + udpbufptr = udpbuf + addSequence(udpbuf); + goto foo; + } +#endif + if ((cc = gwrite(udpSock, udpbuf, udpbufptr - udpbuf)) != udpbufptr - udpbuf) + { + fprintf(stderr, "UDP flush gwrite failed (%d, error %d)\n", + cc, errno); + /* clientDead=1; */ + UDPDIAG(("*** UDP disconnected for %s\n", me->p_name)); + printUdpInfo(); + closeUdpConn(); + commMode = COMM_TCP; + } +#ifdef DOUBLE_UDP + sendSC(); +#endif + udpbufptr = udpbuf + addSequence(udpbuf); + } +#ifdef BROKEN +foo: +#endif + if (udpMode == MODE_FAT) + fatMerge(); +} + +void +build_select_masks(readfds, writefds) + fd_set *readfds, *writefds; +{ + if (readfds) + { + FD_ZERO(readfds); + FD_SET(sock, readfds); + if (udpSock >= 0) + FD_SET(udpSock, readfds); + } + if (writefds) + { + FD_ZERO(writefds); + if (haveDeferredPackets()) + FD_SET(sock, writefds); + } +} + +int +socketPause() +{ + struct timeval timeout; + fd_set readfds, writefds; + + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + build_select_masks(&readfds, &writefds); + + return select(32, (fd_set *) & readfds, &writefds, 0, &timeout); +} + +int +socketWait() +{ + fd_set readfds, writefds; + + build_select_masks(&readfds, &writefds); + + return select(32, &readfds, &writefds, 0, (struct timeval *) 0); +} + +int +gwrite(fd, wbuf, bytes) + int fd; + char *wbuf; + int bytes; +{ + int orig = bytes; + int n; + char tempbuf[80]; + struct timeval to; + + + while (bytes) + { + n = write(fd, wbuf, bytes); + if (n < 0) + { + if (errno == ENOBUFS) + { + /* + * The man pages don't mention this as a possibility. Yet, it + * happens. I guess I just wait for 1/10 sec, and continue? + */ + /* + * I would use usleep() to do this, but this system ain't got it... + */ + /* note: changed from 100 ms to 20 ms. (HAK) */ + to.tv_sec = 0; + to.tv_usec = 20000; + select(0, NULL, NULL, NULL, &to); + continue; + } + if (errno == EINTR) /* interrupted by signal, restart */ + continue; + + if (fd == udpSock) + { + /* do we want Hiccup code here? */ + UDPDIAG(("Tried to write %d, 0x%lx, %d (error %d)\n", + fd, (unsigned long) wbuf, bytes, errno)); + printUdpInfo(); + logmessage("UDP gwrite failed:"); + } + sprintf(tempbuf, "Died in gwrite, n=%d, errno=%d <%s@%s>", + n, errno, me->p_login, me->p_full_hostname); + logmessage(tempbuf); + return (-1); + } + bytes -= n; + wbuf += n; + } + return (orig); +} + + +void +sendUDPbuffered(issc, packet, size) + int issc; /* is semi-critical */ + void *packet; + int size; +{ + if (udpbufptr - udpbuf + size >= UDPBUFSIZE) + { + int cc; + if ((cc = gwrite(udpSock, udpbuf, udpbufptr - udpbuf)) != + udpbufptr - udpbuf) + { + fprintf(stderr, "UDP gwrite failed (%d, error %d)\n", + cc, errno); + /* clientDead=1; */ + UDPDIAG(("*** UDP disconnected for %s\n", me->p_name)); + printUdpInfo(); + closeUdpConn(); + commMode = COMM_TCP; + } +#ifdef DOUBLE_UDP + sendSC(); /* send semi-critical info, if needed */ +#endif + udpbufptr = udpbuf + addSequence(udpbuf); + } + memcpy(udpbufptr, packet, size); + udpbufptr += size; + +#ifdef DOUBLE_UDP + if (issc && udpMode == MODE_DOUBLE) + { + memcpy(scbufptr, packet, size); + scbufptr += size; + V_UDPDIAG((" adding SC\n")); + } +#endif + if (issc && udpMode == MODE_FAT) + { + updateFat(packet); + } +} + + +void +sendTCPbuffered(packet, size) + void *packet; + int size; +{ + int cc; + /* these are critical packets; send them via TCP */ +#ifdef FEATURE_DIAG + /* check the packet & see if we're adding packet type 60 to the buffer */ + if (*((char *) packet) == SP_FEATURE) + { + fprintf(stderr, "Sending SP_FEATURE packet\n"); + } +#endif + if (bufptr - buf + size >= BUFSIZE) + { +#ifdef FEATURE_DIAG + if (*((char *) packet) == SP_FEATURE) + { + fprintf(stderr, "Sending TCP buffer, delaying write.\n"); + } +#endif + if ((cc = gwrite(sock, buf, bufptr - buf)) != bufptr - buf) + { + fprintf(stderr, "TCP gwrite failed (%d, error %d)\n", + cc, errno); + clientDead = 1; + } + bufptr = buf /* + addSequence(buf) */ ; + } +#ifdef FEATURE_DIAG + if (*((char *) packet) == SP_FEATURE) + { + fprintf(stderr, "Adding SP_FEATURE packet to buffer.\n"); + } +#endif + memcpy(bufptr, packet, size); + bufptr += size; +} + +/* Transmission of some packets can be delayed indefinitely */ + +struct deferred_packet +{ + void *data; + int size; + struct deferred_packet *next; +}; + +struct deferred_packet *df_head, *df_tail; + +int +haveDeferredPackets() +{ + return df_head != 0; +} + +/* Put a packet on the deferred queue. */ + +void +sendTCPdeferred(packet, size) + void *packet; + int size; +{ +#if 1 + /* I'm having problems with UDP connection packet */ + sendTCPbuffered(packet, size); +#else + struct deferred_packet *pkt; + pkt = (struct deferred_packet *) malloc(sizeof(*pkt)); + pkt->data = malloc(size); + pkt->size = size; + memcpy(pkt->data, packet, size); + pkt->next = 0; + + if (df_tail) + { + df_tail->next = pkt; + } + else + { + df_head = pkt; + } + df_tail = pkt; +#endif +} + +/* + * When the socket is ready for write, toss a packet through the pipe + * Hopefully it won't block... + */ + +void +flushDeferred() +{ + int rval; + + if (df_head == 0) + return; + + /* could block, oh well */ + rval = gwrite(sock, df_head->data, df_head->size); + + if (rval != df_head->size) + { + fprintf(stderr, "TCP gwrite (deferred) failed (%d, error %d)\n", + rval, errno); + clientDead = 1; + } + + { + struct deferred_packet *temp = df_head; + df_head = temp->next; + free(temp->data); + free(temp); + } + if (!df_head) + df_tail = 0; /* queue is empty */ +} + +/* sends all the deferred packets through the TCP buffer */ +void +undeferDeferred() +{ + while (df_head) + { + sendTCPbuffered(df_head->data, df_head->size); + + { + struct deferred_packet *temp = df_head; + df_head = temp->next; + free(temp->data); + free(temp); + } + } + df_tail = 0; +}