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;
+}