/* \~russian ожидает появления данных в приемнике сокета * \return = 0 - if socket have some dats in receiver * = -1 - if timedout with no data * = SEerror_code - on error * */ int tcp_lock_avail(tcp_socket_t *s, unsigned allow_states , scheduless_condition waitfor, void* waitarg) { bool_t nonblock = (waitfor == (scheduless_condition)0) && (waitarg != (void*)0); if (nonblock && tcp_queue_is_empty (s)) return 0; mutex_lock (&s->lock); while (tcp_queue_is_empty (s)) { int res = -1; if ( __glibc_unlikely(nonblock) ) ; else if( __glibc_unlikely( ((1 << s->state) & allow_states) == 0) ) res = SENOTCONN; else { if (mutex_wait_until (&s->lock, waitfor, waitarg)) continue; if (!mutex_is_my(&s->lock)) return res; } //else // res = -1; //SETIMEDOUT; mutex_unlock (&s->lock); return res; } return 0; }
/** alternative tcp_read_poll with condition test function waitfor * \arg waitfor - simple test function that must not affects mutex_xxx and schedule functionality * */ buf_t* tcp_read_buf_until (tcp_socket_t *s , scheduless_condition waitfor, void* waitarg) { buf_t *p; bool_t nonblock = (waitfor == (scheduless_condition)0) && (waitarg != (void*)0); if (nonblock && tcp_queue_is_empty (s)) return 0; int ok = tcp_lock_avail(s, TCP_STATES_TRANSFER, waitfor, waitarg); if (ok != 0) return (buf_t*)ok; p = tcp_queue_get (s); tcp_debug ("tcp_read: received %u bytes, wnd %u (%u).\n", p->tot_len, s->rcv_wnd, TCP_WND - s->rcv_wnd); mutex_unlock (&s->lock); if (p == 0) return 0; mutex_lock (&s->ip->lock); if (! (s->flags & TF_ACK_DELAY) && ! (s->flags & TF_ACK_NOW)) { tcp_ack (s); } mutex_unlock (&s->ip->lock); return p; }
/* TODO stream is very inefficient of transmiting when receive - * receiver forces transmiter to accasionaly flush out-buffer * */ static unsigned short tcp_stream_getchar (tcp_stream_t *u) { unsigned short c; tcp_socket_t* s = u->socket; if (!s) return -1; mutex_lock (&s->lock); /* Flush output buffer. */ if (u->outptr > u->outdata) { stream_flush (u); mutex_unlock (&s->lock); socket_flush (s); mutex_lock (&s->lock); } if (u->inbuf) mutex_unlock (&s->lock); else { /* Wait for data. */ while (tcp_queue_is_empty (s)) { if (!tcp_socket_is_state(s, TCP_STATES_TRANSFER)) { mutex_unlock (&s->lock); return -1; } mutex_wait (&s->lock); } u->inbuf = tcp_queue_get (s); u->inptr = u->inbuf->payload; mutex_unlock (&s->lock); /*debug_printf ("tstream input"); buf_print (u->inbuf);*/ mutex_lock (&s->ip->lock); if (! (s->flags & TF_ACK_DELAY) && ! (s->flags & TF_ACK_NOW)) { tcp_ack (s); } mutex_unlock (&s->ip->lock); } /* Get byte from buffer. */ c = *u->inptr++; if (u->inptr >= u->inbuf->payload + u->inbuf->len) { buf_t *old = u->inbuf; u->inbuf = old->next; if (u->inbuf) { u->inptr = u->inbuf->payload; old->next = 0; } buf_free (old); } return c; }
/* * Receive len>0 bytes. Return <0 on error. * When nonblock flag is zero, blocks until data get available (never returns 0). * When nonblock flag is nonzero, returns 0 if no data is available. */ int tcp_read_poll (tcp_socket_t *s, void *arg, unsigned short len, int nonblock) { buf_t *p, *q; char *buf; int n; tcp_debug ("tcp_read(s=%p, arg=%p, len=%u)\n", (void*) s, arg, len); if (len == 0) { return -1; } mutex_lock (&s->lock); while (tcp_queue_is_empty (s)) { if (s->state != SYN_SENT && s->state != SYN_RCVD && s->state != ESTABLISHED) { mutex_unlock (&s->lock); tcp_debug ("tcp_read() called in invalid state\n"); return -1; } if (nonblock) { mutex_unlock (&s->lock); return 0; } mutex_wait (&s->lock); } p = tcp_queue_get (s); tcp_debug ("tcp_read: received %u bytes, wnd %u (%u).\n", p->tot_len, s->rcv_wnd, TCP_WND - s->rcv_wnd); mutex_unlock (&s->lock); mutex_lock (&s->ip->lock); if (! (s->flags & TF_ACK_DELAY) && ! (s->flags & TF_ACK_NOW)) { tcp_ack (s); } mutex_unlock (&s->ip->lock); /* Copy all chunks. */ buf = arg; n = 0; for (q=p; q!=0 && n<len; q=q->next) { int bytes; if (q->len == 0) continue; bytes = (q->len < len-n) ? q->len : len-n; memcpy (buf, q->payload, bytes); n += bytes; buf += bytes; } buf_free (p); return n; }
static int tcp_stream_peekchar (tcp_stream_t *u) { tcp_socket_t* s = u->socket; if (! s) return -1; mutex_lock (&s->lock); /* Flush output buffer. */ if (u->outptr > u->outdata) { stream_flush (u); mutex_unlock (&s->lock); socket_flush (s); mutex_lock (&s->lock); } /* Any data available? */ if (u->inbuf) mutex_unlock (&s->lock); else { if (tcp_queue_is_empty (s)) { mutex_unlock (&s->lock); return -1; } u->inbuf = tcp_queue_get (s); u->inptr = u->inbuf->payload; mutex_unlock (&s->lock); mutex_lock (&s->ip->lock); if (! (s->flags & TF_ACK_DELAY) && ! (s->flags & TF_ACK_NOW)) { tcp_ack (s); } mutex_unlock (&s->ip->lock); } return *u->inptr; }
tcp_socket_t * tcp_accept (tcp_socket_t *s) { tcp_socket_t *ns; buf_t *p; ip_hdr_t *iph; tcp_hdr_t *h; unsigned long optdata; again: mutex_lock (&s->lock); for (;;) { if (s->state != LISTEN) { mutex_unlock (&s->lock); tcp_debug ("tcp_accept: called in invalid state\n"); return 0; } if (! tcp_queue_is_empty (s)) { p = tcp_queue_get (s); break; } mutex_wait (&s->lock); } mutex_unlock (&s->lock); /* Create a new PCB, and respond with a SYN|ACK. * If a new PCB could not be created (probably due to lack of memory), * we don't do anything, but rely on the sender will retransmit * the SYN at a time when we have more memory available. */ mutex_lock (&s->ip->lock); ns = tcp_alloc (s->ip); if (ns == 0) { tcp_debug ("tcp_accept: could not allocate PCB\n"); ++s->ip->tcp_in_discards; mutex_unlock (&s->ip->lock); buf_free (p); goto again; } h = (tcp_hdr_t*) p->payload; iph = ((ip_hdr_t*) p->payload) - 1; /* Set up the new PCB. */ memcpy (ns->local_ip, iph->dest, 4); ns->local_port = s->local_port; memcpy (ns->remote_ip, iph->src, 4); ns->remote_port = h->src; ns->state = SYN_RCVD; ns->rcv_nxt = s->ip->tcp_input_seqno + 1; ns->snd_wnd = h->wnd; ns->ssthresh = ns->snd_wnd; ns->snd_wl1 = s->ip->tcp_input_seqno; ns->ip = s->ip; /* Register the new PCB so that we can begin receiving * segments for it. */ tcp_list_add (&s->ip->tcp_sockets, ns); /* Parse any options in the SYN. */ tcp_parseopt (ns, h); /* Build an MSS option. */ optdata = HTONL (((unsigned long)2 << 24) | ((unsigned long)4 << 16) | (((unsigned long)ns->mss / 256) << 8) | (ns->mss & 255)); buf_free (p); /* Send a SYN|ACK together with the MSS option. */ tcp_enqueue (ns, 0, 0, TCP_SYN | TCP_ACK, (unsigned char*) &optdata, 4); tcp_output (ns); mutex_unlock (&s->ip->lock); return ns; }