📄 tcpapi.c
字号:
/*
* FILENAME: tcpapi.c
*
* Copyright 2000 By InterNiche Technologies Inc. All rights reserved
*
* The mini-sockets API for the mini TCP layer.
*
* MODULE: MTCP
*
* ROUTINES: m_connect(), m_listen(), m_send(), m_recv(), m_close(). m_ioctl(),
* ROUTINES: m_socket(), tcp_pktalloc(), tcp_pktfree(), tcp_send(), tcp_recv(),
* ROUTINES: m_getpeername(),
*
* PORTABLE: yes
*/
#include "ipport.h"
#include "mtcp.h"
#include "minip.h" /* (yaxon add) */
void tcp_sleep(void *); /* (yaxon add) */
/* FUNCTION: m_socket()
*
* Allocates a socket structure for an active connect.
*
* PARAM1: none
*
* RETURNS: returns the M_SOCK if OK, else NULL.
*/
M_SOCK
m_socket()
{
M_SOCK so;
so = (M_SOCK)SOC_ALLOC(sizeof(struct msocket));
if (so)
{
LOCK_NET_RESOURCE(NET_RESID); /* do net resource protection */
putq(&msoq, so); /* put new socket in queue */
UNLOCK_NET_RESOURCE(NET_RESID);
}
return(so);
}
/* FUNCTION: m_connect()
*
* Starts an active connect. If socket has been set to non-blocking and
* no problems are detected it returns immediatly with EINPROGRESS.
*
* PARAM1: socket to start connect on
* PARAM2: structure with port and address to conenct to.
* PARAM3: pointer to callback routine for NB connects
*
* RETURNS: 0 if socket is connected, else BSD error code
*/
int
m_connect(M_SOCK so, struct sockaddr_in * sin, M_CALLBACK(name))
{
struct tcpcb * tp;
int e = 0;
LOCK_NET_RESOURCE(NET_RESID);
if(so->tp) /* socket already has a tcpcb? */
{
e = EISCONN;
goto rtn;
}
so->fhost = sin->sin_addr.s_addr;
so->fport = sin->sin_port;
if (so->lport == 0)
m_setlport(so);
tp = m_newtcpcb(so);
if(!tp)
{
e = ENOMEM;
goto rtn;
}
so->callback = name; /* set callback routine */
so->state |= SS_ISCONNECTING; /* set state bitmask */
tp->t_state = TCPS_SYN_SENT; /* TCP state (after syn send) */
tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
tp->iss = tcp_iss; /* setup initial SEQ number */
tcp_iss += (tcp_seq)(TCP_ISSINCR/2);
tcp_sendseqinit(tp);
TCP_STAT_INC(tcps_connattempt); /* Keep detailed (BSDish) stats */
m_template(tp); /* set up header template for tcp sends */
tcp_output(tp); /* send opening syn packet */
if(so->state & SS_NBIO) /* if non-blocking, return now */
{
/* Socket may have blocked to connect in tcp_output() and then been
* set to non-blocking in connect callback - e.g., FTP server does this.
*/
if(so->state & SS_ISCONNECTED)
goto rtn;
/* fall to here if it's really in progress */
e = so->error = EINPROGRESS;
goto rtn;
}
while(so->state & SS_ISCONNECTING)
{
tcp_sleep(so);
}
if(so->state & SS_ISCONNECTED)
e = 0;
else
e = so->error;
rtn:
UNLOCK_NET_RESOURCE(NET_RESID);
return e;
}
/* FUNCTION: m_listen()
*
* Start a listen on the passed port (and optional IP address). The listen
* is implemented by creating a parially filled in M_SOCK and tcpcb. The socket
* is returned to the caller for passing to later calls, like m_close(), but
* will never actually become a working connection. Any passive connects
* which succeed on this socket cause the callback routine to be called
* with a code of M_OPENED and passed a new, connected, socket.
*
*
* PARAM1: struct sockaddr_in * - local port/foreign IP addr
* PARAM2: callback routine
* PARAM3: int * error - OUT - error return
*
* RETURNS: listening socket if success, else returns INVALID_SOCKET and
* sets the passed error holder to one of the BSD error codes.
*/
M_SOCK
m_listen (struct sockaddr_in * sin, M_CALLBACK(name), int * error)
{
M_SOCK so;
/* create a socket and tp to support the listen */
so = m_socket();
if(!so)
{
*error = ENOMEM;
return INVALID_SOCKET;
}
LOCK_NET_RESOURCE(NET_RESID);
so->fhost = sin->sin_addr.s_addr;
so->lport = sin->sin_port;
if (so->lport == 0)
m_setlport(so);
so->callback = name;
so->tp = m_newtcpcb(so);
if(!so->tp)
{
m_delsocket(so);
*error = ENOMEM;
so = INVALID_SOCKET;
goto rtn;
}
so->tp->t_state = TCPS_LISTEN;
rtn:
UNLOCK_NET_RESOURCE(NET_RESID);
return so;
}
/* FUNCTION: tcp_send()
*
* Send a packet allocated via tcp_pktalloc(). User should have filled
* data to be sent at pkt->m_data and set length in pkt->m_len.
*
* An OK return means the data is queued for sending and is now the
* responsability of the stack. An error return means the pkt has
* NOT been queued or freed and is still owned by the caller.
*
* PARAM1: so - socket to send on. Must be open
* PAMAR2: pkt - filled in data packet to send.
*
* RETURNS: 0 if OK or BSD error code.
*/
int
tcp_send(M_SOCK so, PACKET pkt)
{
int err;
LOCK_NET_RESOURCE(NET_RESID);
/* make sure we are not overfilling the socket send buffer */
while((pkt->m_len + so->sendq.sb_cc) > mt_deftxwin)
{
if((so->state & SS_ISCONNECTED) == 0) /* not connected? */
err = ENOTCONN;
else if(so->state & SS_NBIO) /* If non-blocking return now */
err = EWOULDBLOCK;
else
{
tcp_sleep(&so->sendq); /* else wait for data ack */
continue;
}
goto rtn;
}
/* setup packet protocol data pointers to TCP data */
pkt->nb_prot = pkt->m_data;
pkt->nb_plen = pkt->m_len;
if(so->tp == NULL) /* guard against TCP close by fhost */
{
err = EPIPE; /* host killed connection */
}
else /* connection seems OK, send it */
{
put_soq(&so->sendq, pkt); /* place pkt in send que */
err = tcp_output(so->tp); /* call TCP send routine */
}
rtn:
UNLOCK_NET_RESOURCE(NET_RESID);
return err;
}
/* FUNCTION: tcp_recv()
*
* Return next received packet on passed socket. Caller is responsible
* for returning pkt to freeq via pk_free(). pkt->m_data points to data,
* pkt->m_len is length of data.
*
* PARAM1: socket to receive on
*
* RETURNS: pkt if one is ready, NULL if no packet is ready and socket
* is non-blocking.
*/
PACKET
tcp_recv(M_SOCK so)
{
PACKET pkt;
LOCK_NET_RESOURCE(NET_RESID); /* do net resource protection */
pkt = NULL;
if(so->rcvdq.p_head)
goto returnit;
/* non-blocking sockets return a null now */
if(so->state & SS_NBIO)
goto returnpkt;
/* wait till blocking socket gets data or disconnects */
while(so->rcvdq.p_head == NULL)
{
if((so->state & SS_ISCONNECTED) == 0)
goto returnpkt;
UNLOCK_NET_RESOURCE(NET_RESID);
tk_yield();
LOCK_NET_RESOURCE(NET_RESID);
}
returnit:
pkt = get_soq(&so->rcvdq);
returnpkt:
UNLOCK_NET_RESOURCE(NET_RESID);
return pkt;
}
/* FUNCTION: m_ioctl()
*
* Implement selected SO_ options from socket.h. This one routine
* maintains both the so->so_options (socket options) and tp->t_state
* (TCP ioclt) masks.
*
* PARAM1:
*
* RETURNS:
*/
int
m_ioctl(M_SOCK so, int option, void * data)
{
int e = 0;
LOCK_NET_RESOURCE(NET_RESID);
/* map iniche type NBIO to BSD type option */
if(option == SO_NBIO)
{
if((data == NULL) || /* treat null as a pointer to zero */
(*(int*)data != 0)) /* set the masks for non-blocking */
{
so->so_options |= SO_NBIO;
option = SO_NONBLOCK;
}
else /* set socket and tp masks for blocking */
{
so->so_options &= ~SO_NBIO;
option = SO_NBIO;
}
}
switch (option)
{
case SO_NONBLOCK:
so->state |= SS_NBIO;
break;
case SO_BIO:
so->state &= ~SS_NBIO;
break;
case SO_DEBUG: /* toggle debug option based on data as ptr to boolean */
if(*(int*)data == 0) /* (*data) is FALSE, clear debug bit */
so->so_options &= ~SO_DEBUG;
else
so->so_options |= SO_DEBUG;
break;
case SO_LINGER:
/* This mini ioctl only sets the linger option bit and has no
* mechanism to clear it.
*/
so->so_options |= SO_LINGER;
so->linger = *(int*)data; /* number of seconds */
break;
default:
e = EOPNOTSUPP;
/* FALLTHROUGH */
}
UNLOCK_NET_RESOURCE(NET_RESID);
return e;
}
/* FUNCTION: m_close()
*
* close the socket
*
* PARAM1:
*
* RETURNS:
*/
int
m_close(M_SOCK so)
{
struct tcpcb * tp;
M_SOCK tmp;
int e = 0;
LOCK_NET_RESOURCE(NET_RESID); /* do net resource protection */
/* search msoq to make sure sock exists */
for(tmp = (M_SOCK)msoq.q_head; tmp; tmp = tmp->next)
if(tmp == so)
break;
if(tmp == NULL) /* bogus or stale socket */
{
e = EINVAL;
goto rtn;
}
if (so->tp == NULL) /* tp already cleaned up */
{
m_delsocket(so);
goto rtn;
}
tp = so->tp; /* make a local copy of the tcpcb */
/* mark socket as closed so it can be deleted by tcp_slowtimo()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -