⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 transmit.c

📁 开放源码的编译器open watcom 1.6.0版的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 *
 *   BSD sockets functionality for Waterloo TCP/IP
 *
 *   Version
 *
 *   0.5 : Dec 18, 1997 : G. Vanem - created
 */

#include "socket.h"

#if defined(USE_BSD_FUNC)

static int ip_transmit   (Socket *socket, const void *buf, int len);
static int udp_transmit  (Socket *socket, const void *buf, int len);
static int tcp_transmit  (Socket *socket, const void *buf, int len, int flags);
static int setup_udp_raw (Socket *socket, const struct sockaddr *to, int tolen);

static int transmit (const char *func, int s, const void *buf, int len,
                     int flags, const struct sockaddr *to, int tolen);

int sendto (int s, const void *buf, int len, int flags, const struct sockaddr *to, int tolen)
{
  return transmit ("sendto", s, buf, len, flags, to, tolen);
}

int send (int s, const void *buf, int len, int flags)
{
  return transmit ("send", s, buf, len, flags, NULL, 0);
}

int write_s (int s, const char *buf, int nbyte)
{
  return transmit ("write_s", s, buf, nbyte, 0, NULL, 0);
}

int writev_s (int s, const struct iovec *vector, size_t count)
{
  char   *buffer, *bp;
  size_t  i, to_copy, bytes = 0;

  /* Find the total number of bytes to write
   */
  for (i = 0; i < count; i++)
      bytes += vector[i].iov_len;

  if (bytes == 0)
     return (0);

  /* Allocate a temporary buffer to hold the data
   */
  buffer = alloca (bytes);
  if (!buffer)
  {
    SOCK_ERR (ENOMEM);
    return (-1);
  }

  to_copy = bytes;
  bp = buffer;

  /* Copy the data into buffer.
   */
  for (i = 0; i < count; ++i)
  {
    size_t copy = min (vector[i].iov_len, to_copy);

    memcpy (bp, vector[i].iov_base, copy);
    bp      += copy;
    to_copy -= copy;
    if (to_copy == 0)
       break;
  }
  return transmit ("writev_s", s, (const void*)buffer, bytes, 0, NULL, 0);
}

/*
 * Close socket if MSG_EOR specified in flags.
 */
static __inline void msg_eor_close (Socket *socket)
{
  switch (socket->so_type)
  {
    case SOCK_STREAM:
         socket->so_state |= SS_CANTSENDMORE;
         sock_close ((sock_type*)socket->tcp_sock);
         break;
    case SOCK_DGRAM:
         socket->so_state |= SS_CANTSENDMORE;
         sock_close ((sock_type*)socket->udp_sock);
         break;
    case SOCK_RAW:
         socket->so_state |= SS_CANTSENDMORE;
         break;
  }
}

/*
 * transmit() flags:
 *   MSG_DONTROUTE                                     (not supported)
 *   MSG_EOR       Close sending side after data sent
 *   MSG_TRUNC                                         (not supported)
 *   MSG_CTRUNC                                        (not supported)
 *   MSG_OOB                                           (not supported)
 *   MSG_WAITALL   Wait till room in tx-buffer         (not supported)
 */
static int transmit (const char *func, int s, const void *buf, int len,
                     int flags, const struct sockaddr *to, int tolen)
{
  Socket *socket = _socklist_find (s);
  int     rc;

  SOCK_DEBUGF ((socket, "\n%s:%d, len=%d", func, s, len));

  if (!socket)
  {
    if (_sock_dos_fd(s))
    {
      SOCK_DEBUGF ((NULL, ", ENOTSOCK"));
      SOCK_ERR (ENOTSOCK);
      return (-1);
    }
    SOCK_DEBUGF ((NULL, ", EBADF"));
    SOCK_ERR (EBADF);
    return (-1);
  }

  if (socket->so_type == SOCK_STREAM ||      /* TCP-socket or */
      (socket->so_state & SS_ISCONNECTED))   /* "connected" udp/raw */
  {
    /* Note: SOCK_RAW doesn't really need a local address/port, but
     * makes the code more similar for all socket-types.
     * Disadvantage is that SOCK_RAW ties up a local port and a bit
     * more memory.
     */

    if (!socket->local_addr)
    {
      SOCK_DEBUGF ((socket, ", no local_addr"));
      SOCK_ERR (ENOTCONN);
      return (-1);
    }

    if (!socket->remote_addr)
    {
      SOCK_DEBUGF ((socket, ", no remote_addr"));
      SOCK_ERR (ENOTCONN);
      return (-1);
    }

    if (socket->so_state & SS_CONN_REFUSED)
    {
      if (socket->so_error == ECONNRESET)  /* set in tcp_sockreset() */
      {
        SOCK_DEBUGF ((socket, ", ECONNRESET"));
        SOCK_ERR (ECONNRESET);
      }
      else
      {
        SOCK_DEBUGF ((socket, ", ECONNREFUSED"));
        SOCK_ERR (ECONNREFUSED);
      }
      return (-1);
    }
  }

  /* connectionless protocol setup
   */
  if (socket->so_type == SOCK_DGRAM || socket->so_type == SOCK_RAW)
  {
    if (!to || tolen < sizeof(*to))
    {
      SOCK_DEBUGF ((socket, ", no to-addr"));
      SOCK_ERR (EINVAL);
      return (-1);
    }
    if (setup_udp_raw(socket,to,tolen) < 0)
       return (-1);
  }

  VERIFY_RW (buf, len);


  /* Setup SIGINT handler now.
   */
  if (_sock_sig_setup() < 0)
  {
    SOCK_ERR (EINTR);
    return (-1);
  }

  switch (socket->so_type)
  {
    case SOCK_DGRAM:
         rc = udp_transmit (socket, buf, len);
         break;

    case SOCK_STREAM:
         rc = tcp_transmit (socket, buf, len, flags);
         break;

    case SOCK_RAW:
         rc = ip_transmit (socket, buf, len);
         break;

    default:
         SOCK_DEBUGF ((socket, ", EPROTONOSUPPORT"));
         SOCK_ERR (EPROTONOSUPPORT);
         rc = -1;
  }

  _sock_sig_restore();

  if (rc >= 0 && (flags & MSG_EOR))
     msg_eor_close (socket);

  return (rc);
}


/*
 *  Setup remote_addr for SOCK_RAW/SOCK_DGRAM (connectionless) protocols.
 *  Must reconnect socket if 'remote_addr' and 'to' address are different.
 *  I.e we're sending to another host/port than last time.
 */
static int setup_udp_raw (Socket *socket, const struct sockaddr *to, int tolen)
{
  struct sockaddr_in *peer = (struct sockaddr_in*) to;
  DWORD  keepalive = socket->keepalive;
  WORD   lport     = 0;
  char  *rdata     = NULL;
  int    rc;

  if (socket->so_state & SS_ISCONNECTED)
  {
    /* Don't reconnect if same peer address/port.
     */
    if (peer->sin_addr.s_addr == socket->remote_addr->sin_addr.s_addr &&
        peer->sin_port        == socket->remote_addr->sin_port)
       return (1);

    SOCK_DEBUGF ((socket, ", reconnecting"));

    free (socket->remote_addr);
    socket->remote_addr = NULL;

    /* Clear any effect of previous ICMP errors etc.
     */
    socket->so_state &= ~(SS_CONN_REFUSED|SS_CANTSENDMORE|SS_CANTRCVMORE);
    socket->so_error  = 0;

    if (socket->so_type == SOCK_DGRAM)
    {
      lport = socket->udp_sock->myport;
      rdata = (char*)socket->udp_sock->rdata;  /* preserve current data */
    }
  }

  /* For SOCK_DGRAM, udp_close() will be called when (re)opening socket.
   */
  _sock_enter_scope();
  rc = connect (socket->fd, to, tolen);
  _sock_leave_scope();

  if (rc < 0)
     return (-1);


  if (rdata)  /* Must be SOCK_DGRAM */
  {
    udp_Socket *udp = socket->udp_sock;

    free (udp->rdata);                /* free new rx-buffer set in connect() */
    udp->rdata       = (BYTE*) rdata; /* reuse previous data buffer */
    udp->maxrdatalen = DEFAULT_RCV_WIN;

    grab_localport (lport);   /* Restore freed localport */
  }

  /* restore keepalive timer changed in connect()
   */
  socket->keepalive = keepalive;
  return (1);
}

/*
 * Check for enough room in Tx-buffer for a non-blocking socket
 * to transmit without waiting. Only called for SOCK_DGRAM/SOCK_STREAM
 * sockets.
 *
 * If '*len > room', modify '*len' on output to 'room' (the size of
 * bytes left in tx-buf).
 */
static __inline int check_non_block_tx (Socket *socket, int *len)
{
  sock_type *sk;
  int        room;

  if (socket->so_type == SOCK_DGRAM)
       sk = (sock_type*) socket->udp_sock;
  else sk = (sock_type*) socket->tcp_sock;

  room = sock_tbleft (sk);
  if (*len <= room)
     return (0);     /* okay, enough room, '*len' unmodified */

#if 0
  SOCK_YIELD();      /* a small delay to clear up things */
  tcp_tick (sk);

  room = sock_tbleft (sk);
  if (*len <= room)
     return (0);
#endif

  /* Still no room, but cannot split up datagrams (only in ip-fragments)
   */
  if (socket->so_type == SOCK_DGRAM) 
     return (-1);

  /* stream: Tx room below (or equal) low-water mark is failure.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -