summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortomsmeding <tom.smeding@gmail.com>2017-01-19 18:30:07 +0100
committertomsmeding <tom.smeding@gmail.com>2017-01-19 18:30:07 +0100
commitfcc0658730e541477fa60995d8bd82ff87163b92 (patch)
tree7e993fd0cbc54791d3e641badf8965d0d3a2952f
parent89f613475b8da3076aff70a6cef6b41df551d567 (diff)
Separate sock_/tcp_ like regexbattle
-rw-r--r--global.c16
-rw-r--r--global.h10
-rw-r--r--main.c207
-rw-r--r--tcp.c191
-rw-r--r--tcp.h35
5 files changed, 299 insertions, 160 deletions
diff --git a/global.c b/global.c
new file mode 100644
index 0000000..27f522c
--- /dev/null
+++ b/global.c
@@ -0,0 +1,16 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "global.h"
+
+__attribute__((noreturn, format (printf, 1, 2)))
+void throw(const char *format,...){
+ char *buf;
+ va_list ap;
+ va_start(ap,format);
+ vasprintf(&buf,format,ap);
+ va_end(ap);
+ fprintf(stderr,"THROW: %s\n",buf);
+ exit(1);
+}
diff --git a/global.h b/global.h
new file mode 100644
index 0000000..61d8cee
--- /dev/null
+++ b/global.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <stdint.h>
+#include <inttypes.h>
+
+typedef int64_t i64;
+typedef uint64_t u64;
+
+
+void throw(const char *format,...) __attribute__((noreturn, format (printf, 1, 2)));
diff --git a/main.c b/main.c
index 96d9fc5..dcd886a 100644
--- a/main.c
+++ b/main.c
@@ -11,23 +11,12 @@
#include <sys/socket.h>
#include <sys/select.h>
#include <errno.h>
+#include "global.h"
#include "memory.h"
+#include "tcp.h"
#define MAX_LISTEN (128)
-typedef int64_t i64;
-
-
-__attribute__((noreturn, format (printf, 1, 2)))
-void throw(const char *format,...){
- char *buf;
- va_list ap;
- va_start(ap,format);
- vasprintf(&buf,format,ap);
- va_end(ap);
- fprintf(stderr,"THROW: %s\n",buf);
- exit(1);
-}
i64 max(i64 a,i64 b){
return a>b?a:b;
@@ -148,6 +137,7 @@ i64 list_find_d(void **d,i64 len,void *value){
//Returns false iff conn was already in specified room.
bool room_join(Room *room,Connection *conn){
if(LIST_FIND(&room->members,conn)!=-1)return false;
+ //TODO: notify other members
clist_add(&room->members,conn);
rlist_add(&conn->rooms,room);
return true;
@@ -159,9 +149,9 @@ void room_destroy(Room *room);
bool room_leave(Room *room,Connection *conn){
i64 idx=LIST_FIND(&room->members,conn);
if(idx==-1)return false;
- //TODO: notify other members
clist_remove(&room->members,conn);
rlist_remove(&conn->rooms,room);
+ //TODO: notify other members
if(room->members.len==0){
room_destroy(room);
}
@@ -215,114 +205,11 @@ Room* room_find(RList *rlist,const char *gameid,const char *roomid){
}
-//Returns line length; reallocates buf if needed.
-//If *bufsz==0 || *buf==NULL, allocates buf newly.
-//Returns -1 on error or socket closure.
-i64 sock_read_line(int sock,char **buf,i64 *bufsz){
- if(*bufsz==0||*buf==NULL){
- *bufsz=512;
- *buf=malloc(*bufsz,char);
- }
- i64 len=0;
- while(true){
- if(len==*bufsz-1){
- *bufsz*=2;
- *buf=realloc(*buf,*bufsz,char);
- }
- i64 ret=recv(sock,*buf+len,1,0);
- if(ret<=0)return -1;
- if((*buf)[len]=='\n'){
- (*buf)[len]='\0';
- return len;
- }
- len++;
- }
-}
-
-//Returns -1 on error or connection closure.
-i64 sock_read_data(int sock,char *buf,i64 length){
- i64 got=0;
- while(got<length){
- i64 ret=recv(sock,buf+got,length-got,0);
- if(ret<=0)return -1;
- got+=ret;
- }
- return 0;
-}
-
-//Returns -1 on error or connection closure.
-i64 sock_send_data(int sock,const char *buf,i64 length){
- i64 sent=0;
- while(sent<length){
- i64 ret=send(sock,buf+sent,length-sent,0);
- if(ret==-1){
- if(errno==EINTR)continue;
- return -1;
- }
- sent+=ret;
- }
- return 0;
-}
-
-//Returns -1 on error or connection closure.
-i64 sock_send(int sock,const char *str){
- return sock_send_data(sock,str,strlen(str));
-}
-
-//Returns -1 on error or connection closure.
-i64 sock_send_line(int sock,const char *str){
- if(sock_send(sock,str)==-1)return -1;
- return sock_send(sock,"\n");
-}
-
-//Returns -1 on error or connection closure.
-__attribute__((format (printf,2,3)))
-i64 sock_send_line_f(int sock,const char *format,...){
- va_list ap;
- va_start(ap,format);
- char *buf;
- vasprintf(&buf,format,ap);
- va_end(ap);
- if(buf==NULL)throw("vasprintf: allocation failure");
- i64 ret=sock_send_line(sock,buf);
- free(buf);
- return ret;
-}
-
-//Returns -1 on error or connection closure.
-i64 sock_send_list(int sock,const char *tag,const char *const *list,i64 len){
- char *buf;
- asprintf(&buf,"list %s %" PRIi64,tag,len);
- if(buf==NULL)throw("asprintf: allocation failure");
- if(len==0){
- i64 ret=sock_send_line(sock,buf);
- free(buf);
- return ret;
- }
-
- if(sock_send(sock,buf)==-1){free(buf); return -1;}
- for(i64 i=0;i<len;i++){
- if(sock_send(sock," ")==-1){free(buf); return -1;}
- if(sock_send(sock,list[i])==-1){free(buf); return -1;}
- }
- return sock_send(sock,"\n");
-}
-
-//Returns -1 on error or connection closure.
-i64 sock_send_int(int sock,const char *tag,i64 value){
- char *buf;
- asprintf(&buf,"int %s %" PRIi64,tag,value);
- if(buf==NULL)throw("asprintf: allocation failure");
- i64 ret=sock_send_line(sock,buf);
- free(buf);
- return ret;
-}
-
//Returns -1 on error or connection closure.
i64 handle_message(Connection *conn){
static i64 bufsz=0;
static char *buf=NULL;
- if(sock_read_line(conn->sock,&buf,&bufsz)==-1)return -1;
+ if(tcp_read_line(conn->sock,&buf,&bufsz)==-1)return -1;
char *walker=buf;
char *cmdname=strsep(&walker," ");
@@ -335,7 +222,7 @@ i64 handle_message(Connection *conn){
if(walker[i]==' ')nargs++;
}
if(nargs>=10){
- return sock_send_line_f(conn->sock,"error %s Too many arguments",cmdname);
+ return tcp_send_line_f(conn->sock,"error %s Too many arguments",cmdname);
}
}
char **args=malloc(nargs,char*);
@@ -344,70 +231,70 @@ i64 handle_message(Connection *conn){
while((token=strsep(&walker," "))!=NULL)args[nargs++]=token;
if(strcmp(cmdname,"ping")==0){ // ping
- return sock_send_line(conn->sock,"pong");
+ return tcp_send_line(conn->sock,"pong");
} else if(strcmp(cmdname,"room_create")==0){ // room_create gameid roomid public capacity
- if(nargs!=4)return sock_send_line(conn->sock,"error room_create Invalid command format");
+ if(nargs!=4)return tcp_send_line(conn->sock,"error room_create Invalid command format");
char *endp;
i64 capacity=strtol(args[3],&endp,10);
if(strlen(args[0])==0||strlen(args[1])==0||strlen(args[2])!=1||
strchr("10",args[2][0])==NULL||args[3][0]=='\0'||*endp!='\0'||capacity<=0){
- return sock_send_line(conn->sock,"error room_create Invalid command format");
+ return tcp_send_line(conn->sock,"error room_create Invalid command format");
}
char *gameid=strdup(args[0]),*roomid=strdup(args[1]);
bool public=args[2][0]=='1';
Room *room=room_add(gameid,roomid,public,capacity);
room_join(room,conn);
- return sock_send_line(conn->sock,"ok room_create");
+ return tcp_send_line(conn->sock,"ok room_create");
} else if(strcmp(cmdname,"room_join")==0){ // room_join gameid roomid
- if(nargs!=2)return sock_send_line(conn->sock,"error room_join Invalid command format");
+ if(nargs!=2)return tcp_send_line(conn->sock,"error room_join Invalid command format");
if(strlen(args[0])==0||strlen(args[1])==0){
- return sock_send_line(conn->sock,"error room_join Invalid command format");
+ return tcp_send_line(conn->sock,"error room_join Invalid command format");
}
const char *gameid=args[0],*roomid=args[1];
Room *room=room_find(&rooms,gameid,roomid);
- if(room==NULL)return sock_send_line(conn->sock,"error Room not found");
+ if(room==NULL)return tcp_send_line(conn->sock,"error Room not found");
if(LIST_FIND(&room->members,conn)!=-1){
- return sock_send_line(conn->sock,"error room_join Already in room");
+ return tcp_send_line(conn->sock,"error room_join Already in room");
}
if(room->members.len>=room->capacity){
- return sock_send_line(conn->sock,"error room_join Room full");
+ return tcp_send_line(conn->sock,"error room_join Room full");
}
room_join(room,conn);
- return sock_send_line(conn->sock,"ok room_join");
+ return tcp_send_line(conn->sock,"ok room_join");
} else if(strcmp(cmdname,"room_leave")==0){ // room_leave gameid roomid
- if(nargs!=2)return sock_send_line(conn->sock,"error room_leave Invalid command format");
+ if(nargs!=2)return tcp_send_line(conn->sock,"error room_leave Invalid command format");
if(strlen(args[0])==0||strlen(args[1])==0){
- return sock_send_line(conn->sock,"error room_leave Invalid command format");
+ return tcp_send_line(conn->sock,"error room_leave Invalid command format");
}
const char *gameid=args[0],*roomid=args[1];
Room *room=room_find(&rooms,gameid,roomid);
- if(room==NULL)return sock_send_line(conn->sock,"error room_leave Room not found");
- if(LIST_FIND(&room->members,conn)==-1)return sock_send_line(conn->sock,"error room_leave Not in room");
+ if(room==NULL)return tcp_send_line(conn->sock,"error room_leave Room not found");
+ if(LIST_FIND(&room->members,conn)==-1)return tcp_send_line(conn->sock,"error room_leave Not in room");
room_leave(room,conn);
- return sock_send_line(conn->sock,"ok room_leave");
+ return tcp_send_line(conn->sock,"ok room_leave");
} else if(strcmp(cmdname,"room_query")==0){ // room_query -> list
- if(nargs!=0)return sock_send_line(conn->sock,"error room_query Invalid command format");
+ if(nargs!=0)return tcp_send_line(conn->sock,"error room_query Invalid command format");
- if(conn->rooms.len==0)return sock_send_list(conn->sock,"room_query",NULL,0);
+ if(conn->rooms.len==0)return tcp_send_list(conn->sock,"room_query",NULL,0);
const char **idlist=(const char**)malloc(conn->rooms.len,char*);
for(i64 i=0;i<conn->rooms.len;i++)idlist[i]=conn->rooms.d[i]->roomid;
- i64 ret=sock_send_list(conn->sock,"room_query",idlist,conn->rooms.len);
+ i64 ret=tcp_send_list(conn->sock,"room_query",idlist,conn->rooms.len);
free(idlist);
return ret;
} else if(strcmp(cmdname,"room_list")==0){ // room_list gameid -> list
- if(nargs!=1)return sock_send_line(conn->sock,"error room_list Invalid command format");
+ if(nargs!=1)return tcp_send_line(conn->sock,"error room_list Invalid command format");
const char *gameid=args[0];
- if(strlen(gameid)==0)return sock_send_line(conn->sock,"error room_list Invalid command format");
+ if(strlen(gameid)==0)return tcp_send_line(conn->sock,"error room_list Invalid command format");
i64 nrooms=0;
for(i64 i=0;i<rooms.len;i++){
if(rooms.d[i]->public&&strcmp(rooms.d[i]->gameid,gameid)==0)nrooms++;
}
- if(nrooms==0)return sock_send_list(conn->sock,"room_list",NULL,0);
+ if(nrooms==0)return tcp_send_list(conn->sock,"room_list",NULL,0);
const char **roomids=(const char**)malloc(nrooms,Room*);
nrooms=0;
@@ -417,72 +304,72 @@ i64 handle_message(Connection *conn){
roomids[nrooms++]=rooms.d[i]->roomid;
}
}
- i64 ret=sock_send_list(conn->sock,"room_list",roomids,nrooms);
+ i64 ret=tcp_send_list(conn->sock,"room_list",roomids,nrooms);
free(roomids);
return ret;
} else if(strcmp(cmdname,"room_player_list")==0){ // room_player_list gameid roomid -> list
- if(nargs!=2)return sock_send_line(conn->sock,"error room_player_list Invalid command format");
+ if(nargs!=2)return tcp_send_line(conn->sock,"error room_player_list Invalid command format");
if(strlen(args[0])==0||strlen(args[1])==0){
- return sock_send_line(conn->sock,"error room_player_list Invalid command format");
+ return tcp_send_line(conn->sock,"error room_player_list Invalid command format");
}
const char *gameid=args[0],*roomid=args[1];
Room *room=room_find(&rooms,gameid,roomid);
- if(room==NULL)return sock_send_line(conn->sock,"error room_player_list Room not found");
- if(LIST_FIND(&room->members,conn)==-1)return sock_send_line(conn->sock,"error room_player_list Not in room");
+ if(room==NULL)return tcp_send_line(conn->sock,"error room_player_list Room not found");
+ if(LIST_FIND(&room->members,conn)==-1)return tcp_send_line(conn->sock,"error room_player_list Not in room");
- if(room->members.len==0)return sock_send_list(conn->sock,"room_player_list",NULL,0);
+ if(room->members.len==0)return tcp_send_list(conn->sock,"room_player_list",NULL,0);
char **names=malloc(room->members.len,char*);
for(i64 i=0;i<room->members.len;i++){
asprintf(&names[i],"%" PRIi64,room->members.d[i]->id);
}
- i64 ret=sock_send_list(conn->sock,"room_player_list",(const char*const*)names,room->members.len);
+ i64 ret=tcp_send_list(conn->sock,"room_player_list",(const char*const*)names,room->members.len);
for(i64 i=0;i<room->members.len;i++)free(names[i]);
free(names);
return ret;
} else if(strcmp(cmdname,"room_message")==0){ // room_message gameid roomid target length
- if(nargs!=4)return sock_send_line(conn->sock,"error room_message Invalid command format");
+ if(nargs!=4)return tcp_send_line(conn->sock,"error room_message Invalid command format");
if(strlen(args[0])==0||strlen(args[1])==0){
- return sock_send_line(conn->sock,"error room_message Invalid command format");
+ return tcp_send_line(conn->sock,"error room_message Invalid command format");
}
const char *gameid=args[0],*roomid=args[1];
Room *room=room_find(&rooms,gameid,roomid);
- if(room==NULL)return sock_send_line(conn->sock,"error room_message Room not found");
- if(LIST_FIND(&room->members,conn)==-1)return sock_send_line(conn->sock,"error room_message Not in room");
+ if(room==NULL)return tcp_send_line(conn->sock,"error room_message Room not found");
+ if(LIST_FIND(&room->members,conn)==-1)return tcp_send_line(conn->sock,"error room_message Not in room");
char *endp;
i64 targetid=strtol(args[2],&endp,10);
if(args[2][0]=='\0'||*endp!='\0'||targetid<=0){
- return sock_send_line(conn->sock,"error room_message Invalid command format");
+ return tcp_send_line(conn->sock,"error room_message Invalid command format");
}
i64 msglength=strtol(args[3],&endp,10);
if(args[3][0]=='\0'||*endp!='\0'||msglength<0){
- return sock_send_line(conn->sock,"error room_message Invalid command format");
+ return tcp_send_line(conn->sock,"error room_message Invalid command format");
}
i64 idx;
for(idx=0;idx<room->members.len;idx++){
if(room->members.d[idx]->id==targetid)break;
}
- if(idx==room->members.len)return sock_send_line(conn->sock,"error room_message Target not found");
+ if(idx==room->members.len)return tcp_send_line(conn->sock,"error room_message Target not found");
if(msglength==0)return 0;
char *buf=malloc(msglength,char);
- if(sock_read_data(conn->sock,buf,msglength)==-1){
+ if(tcp_read_data(conn->sock,buf,msglength)==-1){
free(buf);
return -1;
}
- sock_send_line_f(room->members.d[idx]->sock,"room_message %" PRIi64 " %" PRIi64,conn->id,msglength);
- sock_send_data(room->members.d[idx]->sock,buf,msglength);
+ tcp_send_line_f(room->members.d[idx]->sock,"room_message %" PRIi64 " %" PRIi64,conn->id,msglength);
+ tcp_send_data(room->members.d[idx]->sock,buf,msglength);
free(buf);
- return sock_send_line(conn->sock,"ok room_message");
+ return tcp_send_line(conn->sock,"ok room_message");
} else if(strcmp(cmdname,"id")==0){ // id -> int
- return sock_send_int(conn->sock,"id",conn->id);
+ return tcp_send_int(conn->sock,"id",conn->id);
} else if(strcmp(cmdname,"exit")==0){ // exit
return -1;
} else {
- return sock_send_line_f(conn->sock,"error %s Unrecognised command",cmdname);
+ return tcp_send_line_f(conn->sock,"error %s Unrecognised command",cmdname);
}
}
diff --git a/tcp.c b/tcp.c
new file mode 100644
index 0000000..65da79d
--- /dev/null
+++ b/tcp.c
@@ -0,0 +1,191 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include "global.h"
+#include "memory.h"
+#include "tcp.h"
+
+i64 tcp_read_line(int sock,char **buf,i64 *bufsz){
+ if(*bufsz==0||*buf==NULL){
+ *bufsz=512;
+ *buf=malloc(*bufsz,char);
+ }
+ i64 len=0;
+ while(true){
+ if(len==*bufsz-1){
+ *bufsz*=2;
+ *buf=realloc(*buf,*bufsz,char);
+ }
+ i64 ret=recv(sock,*buf+len,1,0);
+ if(ret<=0)return -1;
+ if((*buf)[len]=='\n'){
+ (*buf)[len]='\0';
+ return len;
+ }
+ len++;
+ }
+}
+
+i64 tcp_read_data(int sock,char *buf,i64 length){
+ i64 got=0;
+ while(got<length){
+ i64 ret=recv(sock,buf+got,length-got,0);
+ if(ret<=0)return -1;
+ got+=ret;
+ }
+ return 0;
+}
+
+i64 tcp_send_data(int sock,const char *buf,i64 length){
+ i64 sent=0;
+ while(sent<length){
+ i64 ret=send(sock,buf+sent,length-sent,0);
+ if(ret==-1){
+ if(errno==EINTR)continue;
+ return -1;
+ }
+ sent+=ret;
+ }
+ return 0;
+}
+
+i64 tcp_send_str(int sock,const char *str){
+ return tcp_send_data(sock,str,strlen(str));
+}
+
+i64 tcp_send_line(int sock,const char *str){
+ if(tcp_send_str(sock,str)==-1)return -1;
+ return tcp_send_str(sock,"\n");
+}
+
+__attribute__((format (printf,2,3)))
+i64 tcp_send_line_f(int sock,const char *format,...){
+ va_list ap;
+ va_start(ap,format);
+ char *buf;
+ vasprintf(&buf,format,ap);
+ va_end(ap);
+ if(buf==NULL)throw("vasprintf: allocation failure");
+ i64 ret=tcp_send_line(sock,buf);
+ free(buf);
+ return ret;
+}
+
+
+i64 tcp_send_list(int sock,const char *tag,const char *const *list,i64 len){
+ char *buf;
+ asprintf(&buf,"list %s %" PRIi64,tag,len);
+ if(buf==NULL)throw("asprintf: allocation failure");
+ if(len==0){
+ i64 ret=tcp_send_line(sock,buf);
+ free(buf);
+ return ret;
+ }
+
+ if(tcp_send_str(sock,buf)==-1){free(buf); return -1;}
+ for(i64 i=0;i<len;i++){
+ if(tcp_send_str(sock," ")==-1){free(buf); return -1;}
+ if(tcp_send_str(sock,list[i])==-1){free(buf); return -1;}
+ }
+ return tcp_send_str(sock,"\n");
+}
+
+//Returns -1 on error or connection closure.
+i64 tcp_send_int(int sock,const char *tag,i64 value){
+ char *buf;
+ asprintf(&buf,"int %s %" PRIi64,tag,value);
+ if(buf==NULL)throw("asprintf: allocation failure");
+ i64 ret=tcp_send_line(sock,buf);
+ free(buf);
+ return ret;
+}
+
+
+i64 tcp_read_ok(int sock,const char *tag){
+ char *buf=NULL;
+ i64 bufsz=0;
+ i64 ret=tcp_read_line(sock,&buf,&bufsz);
+ if(ret==-1){
+ if(buf!=NULL)free(buf);
+ return -1;
+ }
+
+ char *walker=buf;
+ char *ok=strsep(&walker," ");
+ char *realtag=strsep(&walker," ");
+ bool success=walker==NULL&&strcmp(ok,"ok")==0&&strcmp(realtag,tag)==0;
+ free(buf);
+ return success?0:-1;
+}
+
+TcpList* tcp_read_list(int sock,const char *tag){
+ char *buf=NULL;
+ i64 bufsz=0;
+ i64 ret=tcp_read_line(sock,&buf,&bufsz);
+ if(ret==-1){
+ if(buf!=NULL)free(buf);
+ return NULL;
+ }
+
+ char *walker=buf;
+ char *word1=strsep(&walker," ");
+ char *word2=strsep(&walker," ");
+ char *word3=strsep(&walker," ");
+ char *argstart=walker;
+ i64 nitems=0;
+ while((strsep(&walker," "))!=NULL)nitems++;
+ if(word1==NULL||word2==NULL||word3==NULL||strcmp(word1,"list")!=0||strcmp(word2,tag)!=0){
+ free(buf);
+ return NULL;
+ }
+ char *endp;
+ i64 word3i=strtol(word3,&endp,10);
+ if(word3[0]=='\0'||*endp!='\0'||word3i!=nitems){
+ free(buf);
+ return NULL;
+ }
+
+ TcpList *list=malloc(1,TcpList);
+ list->nitems=nitems;
+ list->items=malloc(nitems,char*);
+ walker=argstart;
+ for(i64 i=0;i<nitems;i++){
+ list->items[i]=strdup(strsep(&walker," "));
+ }
+
+ return list;
+}
+
+void tcp_list_destroy(TcpList *list){
+ free(list->items);
+ free(list);
+}
+
+
+static const char* itoa(int n){
+ static char buf[64];
+ sprintf(buf,"%d",n);
+ return buf;
+}
+
+
+int tcp_connect(const char *hostname,int port){
+ struct addrinfo hints,*res;
+ memset(&hints,0,sizeof(hints));
+ hints.ai_family=AF_UNSPEC;
+ hints.ai_socktype=SOCK_STREAM;
+ int ret=getaddrinfo(hostname,itoa(port),&hints,&res);
+ if(ret!=0){
+ return -1;
+ }
+
+ int sock=socket(res->ai_family,res->ai_socktype,res->ai_protocol);
+ if(sock==-1)return -1;
+ if(connect(sock,res->ai_addr,res->ai_addrlen)==-1)return -1;
+ return sock;
+}
diff --git a/tcp.h b/tcp.h
new file mode 100644
index 0000000..6d50538
--- /dev/null
+++ b/tcp.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <stdint.h>
+
+//Unless specified otherwise, functions return -1 on error or socket closure.
+
+//If *bufsz==0 || *buf==NULL, allocates buf newly.
+//Returns line length; reallocates buf if needed.
+i64 tcp_read_line(int sock,char **buf,i64 *bufsz);
+
+i64 tcp_read_data(int sock,char *buf,i64 length);
+
+i64 tcp_send_data(int sock,const char *buf,i64 length);
+i64 tcp_send_str(int sock,const char *str);
+i64 tcp_send_line(int sock,const char *str);
+i64 tcp_send_line_f(int sock,const char *format,...) __attribute__((format (printf,2,3)));
+
+i64 tcp_send_list(int sock,const char *tag,const char *const *list,i64 len);
+i64 tcp_send_int(int sock,const char *tag,i64 value);
+
+
+i64 tcp_read_ok(int sock,const char *tag);
+
+typedef struct TcpList{
+ i64 nitems;
+ char **items;
+} TcpList;
+
+//Returns NULL on error instead of -1.
+TcpList* tcp_read_list(int sock,const char *tag);
+void tcp_list_destroy(TcpList *list);
+
+
+//Returns the socket.
+int tcp_connect(const char *hostname,int port);