view 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 source

/*--------------------------------------------------------------------------
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;
}