static coroutine void connector(void) { tcpsock s = tcpconnect(iplocal("127.0.0.1", 5555, 0), -1); assert(s); char buf[1]; tcprecv(s, buf, 1, -1); assert(0); }
coroutine void client(int port) { ipaddr addr = ipremote("127.0.0.1", port, 0, -1); tcpsock cs = tcpconnect(addr, -1); assert(cs); char ipstr[16] = {0}; ipaddrstr(addr, ipstr); assert(errno == 0); assert(strcmp(ipstr, "127.0.0.1") == 0); int fd = tcpdetach(cs); assert(fd != -1); cs = tcpattach(fd, 0); assert(cs); msleep(now() + 100); char buf[16]; size_t sz = tcprecv(cs, buf, 3, -1); assert(sz == 3 && buf[0] == 'A' && buf[1] == 'B' && buf[2] == 'C'); sz = tcpsend(cs, "123\n45\n6789", 11, -1); assert(sz == 11 && errno == 0); tcpflush(cs, -1); assert(errno == 0); tcpclose(cs); }
size_t net_peer_recv(net_peer_t* peer, void* buf, size_t len, int64_t deadline) { switch(peer->type) { case NET_PROTO_UNIX: return unixrecv((unixsock)peer->socket, buf, len, deadline); case NET_PROTO_TCP: return tcprecv((tcpsock)peer->socket, buf, len, deadline); case NET_PROTO_UDP: return udprecv((udpsock)peer->socket, 0, buf, len, deadline); } return 0; }
void client(void) { tcpsock cs = tcpconnect("127.0.0.1:5555"); assert(cs); char buf[16]; ssize_t sz = tcprecv(cs, buf, 3); assert(buf[0] == 'A' && buf[1] == 'B' && buf[2] == 'C'); tcpsend(cs, "123\n45\n6789", 11); int rc = tcpflush(cs); assert(rc == 0); tcpclose(cs); }
int main(int argc, char *argv[]) { if(argc != 3) { printf("usage: c10k <parallel-connections> <roundtrip-count>\n"); return 1; } long conns = atol(argv[1]); long roundtrips = atol(argv[2]); assert(conns >= 1); tcpsock ls = tcplisten(iplocal("127.0.0.1", 5555, 0), 10); assert(ls); int i; for(i = 0; i != conns - 1; ++i) { go(connector()); tcpsock as = tcpaccept(ls, -1); assert(as); } go(sender(roundtrips)); tcpsock as = tcpaccept(ls, -1); assert(as); int64_t start = now(); char buf[1]; size_t nbytes; for(i = 0; i != roundtrips; ++i) { nbytes = tcpsend(as, "A", 1, -1); assert(errno == 0); assert(nbytes == 1); tcpflush(as, -1); assert(errno == 0); nbytes = tcprecv(as, buf, 1, -1); assert(errno == 0); assert(nbytes == 1); assert(buf[0] == 'A'); } int64_t stop = now(); long duration = (long)(stop - start); long us = (duration * 1000) / roundtrips; printf("done %ld roundtrips in %f seconds\n", roundtrips, ((float)duration) / 1000); printf("duration of a single roundtrip: %ld us\n", us); printf("roundtrips per second: %ld\n", (long)(roundtrips * 1000 / duration)); return 0; }
int main(void) { go(daemon()); msleep(now() + 500); tcpmuxsock ls = tcpmuxlisten(5557, "foo", -1); assert(ls); go(doconnect()); tcpsock s = tcpmuxaccept(ls, -1); assert(s); char buf[3]; tcprecv(s, buf, sizeof(buf), -1); assert(errno == 0); assert(buf[0] == 'a' && buf[1] == 'b' && buf[2] == 'c'); tcpclose(s); tcpmuxclose(ls); return 0; }
static coroutine void sender(long roundtrips) { tcpsock s = tcpconnect(iplocal("127.0.0.1", 5555, 0), -1); assert(s); char buf[1]; int i; size_t nbytes; for(i = 0; i != roundtrips; ++i) { nbytes = tcprecv(s, buf, 1, -1); assert(errno == 0); assert(nbytes == 1); assert(buf[0] == 'A'); nbytes = tcpsend(s, "A", 1, -1); assert(errno == 0); assert(nbytes == 1); tcpflush(s, -1); assert(errno == 0); } }
int main() { char buf[16]; tcpsock ls = tcplisten(iplocal(NULL, 5555, 0), 10); assert(ls); int fd = tcpdetach(ls); assert(fd != -1); ls = tcpattach(fd, 1); assert(ls); assert(tcpport(ls) == 5555); go(client(5555)); tcpsock as = tcpaccept(ls, -1); /* Test deadline. */ int64_t deadline = now() + 30; size_t sz = tcprecv(as, buf, sizeof(buf), deadline); assert(sz == 0 && errno == ETIMEDOUT); int64_t diff = now() - deadline; assert(diff > -20 && diff < 20); sz = tcpsend(as, "ABC", 3, -1); assert(sz == 3 && errno == 0); tcpflush(as, -1); assert(errno == 0); sz = tcprecvuntil(as, buf, sizeof(buf), "\n", 1, -1); assert(sz == 4); assert(buf[0] == '1' && buf[1] == '2' && buf[2] == '3' && buf[3] == '\n'); sz = tcprecvuntil(as, buf, sizeof(buf), "\n", 1, -1); assert(sz == 3); assert(buf[0] == '4' && buf[1] == '5' && buf[2] == '\n'); sz = tcprecvuntil(as, buf, 3, "\n", 1, -1); assert(sz == 3); assert(buf[0] == '6' && buf[1] == '7' && buf[2] == '8'); tcpclose(as); tcpclose(ls); return 0; }
size_t tcprecvuntil(tcpsock s, void *buf, size_t len, const char *delims, size_t delimcount, int64_t deadline) { if(s->type != MILL_TCPCONN) mill_panic("trying to receive from an unconnected socket"); char *pos = (char*)buf; size_t i; for(i = 0; i != len; ++i, ++pos) { size_t res = tcprecv(s, pos, 1, deadline); if(res == 1) { size_t j; for(j = 0; j != delimcount; ++j) if(*pos == delims[j]) return i + 1; } if (errno != 0) return i + res; } errno = ENOBUFS; return len; }
/* Gets one CRLF-delimited line from the socket. Trims all leading and trailing whitesepace. Replaces any remaining whitespace sequences by single space. */ static int wsock_getline(wsock s, char *buf, size_t len, int64_t deadline) { size_t sz = tcprecvuntil(s->u, buf, len, "\r", 1, deadline); if(errno != 0) return -1; sz--; char c; tcprecv(s->u, &c, 1, deadline); if(errno != 0) return -1; if(c != '\n') { errno = EPROTO; return -1; } size_t i; for(i = 0; i != sz; ++i) { if(buf[i] < 32 || buf[i] > 127) { errno = EPROTO; return -1; } } i = 0; while(i != sz && isspace(buf[i])) ++i; size_t pos = 0; while(i != sz) { if(isspace(buf[i])) { while(i != sz && isspace(buf[i])) ++i; --i; } buf[pos++] = buf[i++]; } if(pos && isspace(buf[pos - 1])) --pos; return pos; }
PROCESS_THREAD(mqtt_socket_process, ev, data) { PROCESS_BEGIN(); SOCKETMSG msg; static struct etimer mqtt_socket_timer; int recv; mqtt_tcp_socket_init(); app_mqtt_init(&g_stMQTTBroker); mqtt_tcp_connect(MQTT_SERVER_ADDR, MQTT_SERVER_PORT); while(1) { PROCESS_WAIT_EVENT(); if(ev == resolv_event_found) { char *p_hostname = (char *)data; if(strcmp(p_hostname, MQTT_SERVER_ADDR)) continue; uip_ipaddr_t addr; uip_ipaddr_t *p_addr = &addr; uip_ip4addr_t remote_ip_addr; resolv_lookup(p_hostname, &p_addr); uip_ipaddr_copy(&remote_ip_addr, p_addr); char ip_str[20]= {0}; sprintf(ip_str,"%d.%d.%d.%d", remote_ip_addr.u8[0], remote_ip_addr.u8[1], remote_ip_addr.u8[2], remote_ip_addr.u8[3]); mqtt_tcp_connect(ip_str, MQTT_SERVER_PORT); } else if(ev == PROCESS_EVENT_EXIT) { mqtt_disconnect(&g_stMQTTBroker); tcpclose(g_mqtt_socket_para.fd); break; } else if(ev == PROCESS_EVENT_MSG) { msg = *(SOCKETMSG *)data; if(msg.status == SOCKET_CONNECTED) { printf("MQTT tcp socket(%d) connected\n", msg.socket); g_mqtt_socket_para.connect_status = SOCKET_CONNECTED; tcp_set_send_buffer_size(g_mqtt_socket_para.fd, MQTT_SOCKET_SEND_BUF_SIZE); mqtt_login_server(&g_stMQTTBroker); } else if(msg.status == SOCKET_CLOSED) { if(g_mqtt_socket_para.connect_status == SOCKET_CREATE) printf("Socked(%d) create fail and retry...\n", msg.socket); else printf("MQTT tcp socket(%d) closed\n", msg.socket); g_mqtt_socket_para.fd = -1; g_mqtt_socket_para.connect_status = SOCKET_CLOSED; etimer_set(&mqtt_socket_timer, 3*CLOCK_SECOND); PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&mqtt_socket_timer)); mqtt_tcp_connect(MQTT_SERVER_ADDR, MQTT_SERVER_PORT); } else if(msg.status == SOCKET_SENDACK) { } else if(msg.status == SOCKET_NEWDATA) { g_mqtt_socket_para.connect_status = SOCKET_NEWDATA; recv = tcprecv(g_mqtt_socket_para.fd, g_mqtt_socket_para.recv_buf, MQTT_SOCKET_RECV_BUF_SIZE); if(recv<=0) { printf("tcprecv error! ret:%d\n", recv); continue; } app_parse_mqttmsg(g_mqtt_socket_para.recv_buf); } } } PROCESS_END(); }
size_t wsockrecv(wsock s, void *msg, size_t len, int64_t deadline) { if(s->flags & WSOCK_LISTENING) {errno = EOPNOTSUPP; return 0;} if(s->flags & WSOCK_BROKEN) {errno = ECONNABORTED; return 0;} size_t res = 0; while(1) { uint8_t hdr1[2]; tcprecv(s->u, hdr1, 2, deadline); if(errno != 0) {s->flags |= WSOCK_BROKEN; return 0;} if(hdr1[0] & 0x70) { s->flags &= WSOCK_BROKEN; errno = EPROTO; return 0;} int opcode = hdr1[0] & 0x0f; if(opcode == 8) { if(!(s->flags & WSOCK_DONE)) { /* TODO: Close frames from client should be masked. */ tcpsend(s->u, "\x88\x00", 2, deadline); tcpflush(s->u, deadline); s->flags |= (WSOCK_BROKEN & WSOCK_DONE); } errno = ECONNRESET; return 0; } if(opcode == 9) { /* TODO: Account for pings and pongs with payload. */ if(!(s->flags & WSOCK_DONE)) { tcpsend(s->u, "\x8A\x00", 2, deadline); if(errno != 0) {s->flags &= WSOCK_BROKEN; return 0;} tcpflush(s->u, deadline); if(errno != 0) {s->flags &= WSOCK_BROKEN; return 0;} } continue; } if(opcode == 10) { /* TODO: Account for pings and pongs with payload. */ /* TODO: Do we want to make exiting the function here optional? */ errno = EAGAIN; return 0; } if(!!(s->flags & WSOCK_CLIENT) ^ !(hdr1[1] & 0x80)) { s->flags &= WSOCK_BROKEN; errno = EPROTO; return 0;} size_t sz = hdr1[1] & 0x7f; if(sz == 126) { uint8_t hdr2[2]; tcprecv(s->u, hdr2, 2, deadline); if(errno != 0) {s->flags |= WSOCK_BROKEN; return 0;} sz = wsock_gets(hdr2); } else if(sz == 127) { uint8_t hdr2[8]; tcprecv(s->u, hdr2, 8, deadline); if(errno != 0) {s->flags |= WSOCK_BROKEN; return 0;} sz = wsock_getll(hdr2); } uint8_t mask[4]; if(!(s->flags & WSOCK_CLIENT)) { tcprecv(s->u, mask, 4, deadline); if(errno != 0) {s->flags |= WSOCK_BROKEN; return 0;} } size_t toread = sz < len ? sz : len; if(toread > 0) { tcprecv(s->u, msg, toread, deadline); if(errno != 0) {s->flags |= WSOCK_BROKEN; return 0;} } if(!(s->flags & WSOCK_CLIENT)) { size_t i; for(i = 0; i != toread; ++i) ((uint8_t*)msg)[i] ^= mask[i % 4]; } if(sz > toread) { tcprecv(s->u, NULL, sz - toread, deadline); if(errno != 0) {s->flags |= WSOCK_BROKEN; return 0;} } res += sz; if(hdr1[0] & 0x80) break; msg = ((uint8_t*)msg) + sz; len -= sz; } return res; }
/*---------------------------------------------------------------------------*/ PROCESS_THREAD(http_req_process, ev, data) { static struct etimer httptimeout; SOCKETMSG msg; PROCESS_BEGIN(); while(1) { //wait for TCP connected or uip_timeout. PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_MSG || ev == PROCESS_EVENT_TIMER || ev == PROCESS_EVENT_EXIT); if(ev == PROCESS_EVENT_MSG) { msg = *(SOCKETMSG *)data; if(msg.status == SOCKET_CONNECTED) { //send out the http request. if(httpreqdata.cmdlen) tcpsend(httpreqdata.httpsock, httpreqdata.httpcmd, httpreqdata.cmdlen); } else if(msg.status == SOCKET_SENDACK) { //set timeot for http response. etimer_set(&httptimeout, 10 * CLOCK_CONF_SECOND); } else if(msg.status == SOCKET_NEWDATA) { //Get http response, parse response and close socket. etimer_stop(&httptimeout); httpreqdata.rsplen = tcprecv(httpreqdata.httpsock, httpreqdata.httprsp, HTTPRSP_MAX); tcpclose(httpreqdata.httpsock); httpreqdata.httpsock = -1; httpreqdata.httpstatus = HTTP_IDLE; if(httpreqdata.rsplen > 0 && httpreqdata.callbackfn) { httpreqdata.httprsp[httpreqdata.rsplen] = 0; httprsp_parse(httpreqdata.httprsp, httpreqdata.rsplen); } } else if(msg.status == SOCKET_CLOSED) { //socket closed, if it is unnormal case, notify upper layer. if(httpreqdata.httpstatus != HTTP_IDLE) { if(httpreqdata.callbackfn) { httpmsg.msgtype = HTTPREQ_CONN_ERROR; httpmsg.rsp = NULL; httpreqdata.callbackfn(&httpmsg); } httpreqdata.httpsock = -1; httpreqdata.httpstatus = HTTP_IDLE; } } } else if(ev == PROCESS_EVENT_TIMER) { //http response timeout, close socket and notify upper layer. tcpclose(httpreqdata.httpsock); httpreqdata.httpsock = -1; httpreqdata.httpstatus = HTTP_IDLE; if(httpreqdata.callbackfn) { httpmsg.msgtype = HTTPREQ_RSP_TIMEOUT; httpmsg.rsp = NULL; httpreqdata.callbackfn(&httpmsg); } } else if(ev == PROCESS_EVENT_EXIT) { break; } } PROCESS_END(); }