📄 transmit.c
字号:
/*
*
* 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 + -