Bladeren bron

Added datagram support to sockets; reworked sockets a bit; updated socket bananas.

Mark Sibly 9 jaren geleden
bovenliggende
commit
d4d355d07e

+ 19 - 21
bananas/echoserver/echoserver.monkey2

@@ -14,73 +14,71 @@ Class MyWindow Extends Window
 		New Fiber( Server )
 		
 		New Fiber( Client )
-		
-'		New Timer( 60,App.RequestRender )
 	End
 	
 	Method Server()
 	
-		Local server:=SocketServer.Listen( 12345 )
-		If Not server print "Server:Failed to create server" ; Return
+		Local server:=Socket.Listen( 12345 )
+		If Not server print "Server: Failed to create server" ; Return
 		
-		Print "Server:Listening"
+		Print "Server @"+server.Address+" listening"
 		
 		Repeat
 		
 			Local socket:=server.Accept()
 			If Not socket Exit
 			
-			Print "Server:Accepted"
+			Print "Server accepted client @"+socket.PeerAddress
+			
+			Local stream:=New SocketStream( socket )
 			
 			New Fiber( Lambda()
 			
 				Repeat
 				
-					Local line:=socket.ReadCString()
+					Local line:=stream.ReadSizedString()
 					If Not line Exit
 					
-					socket.WriteCString( line )
+					stream.WriteSizedString( line )
+					
 				Forever
 				
-				socket.Close()
+				stream.Close()
 				
 			End )
 		
 		Forever
 		
-		'Never gets here!
-		'
 		Print "Server:Bye"
 		
 		server.Close()
-	
 	End
 	
 	Method Client()
 	
-		Fiber.Sleep( .5 )	'wait a bit for server to start
+		Fiber.Sleep( .5 )
+	
+		Local socket:=Socket.Connect( "localhost",12345 )
+		If Not socket Print "Client: Couldn't connect to server" ; Return
 		
-		Local socket:=SocketStream.Connect( "localhost",12345 )
-		If Not socket Print "Client:Couldn't connect to server" ; Return
+		Print "Client @"+socket.Address+" connected to server @"+socket.PeerAddress
 		
-		Print "Client:Connected"
+		Local stream:=New SocketStream( socket )
 
 		For Local i:=0 Until 100
 		
-			socket.WriteCString( "This is a number:"+i )
+			stream.WriteSizedString( "This is a number:"+i )
 			
-			Print "Reply:"+socket.ReadCString()
+			Print "Reply:"+stream.ReadSizedString()
 		Next
 		
 		Print "Client:Bye"
 		
-		socket.Close()
+		stream.Close()
 	End
 	
 	Method OnRender( canvas:Canvas ) Override
 	
-'		App.RequestRender()
-		
 		Global ticks:=0
 		ticks+=1
 		

+ 99 - 0
bananas/echoserver_udp/echoserver_udp.monkey2

@@ -0,0 +1,99 @@
+
+#Import "<mojox>"
+#Import "<mojo>"
+#Import "<std>"
+
+Using mojox..
+Using mojo..
+Using std..
+
+Class MyWindow Extends Window
+
+	Method New()
+	
+		New Fiber( Server )
+		
+		For Local i:=0 Until 5
+			New Fiber( Client )
+		Next
+	End
+	
+	Method Server()
+	
+		Local socket:=Socket.Bind( 12345 )
+		If Not socket print "Server: Failed to create server" ; Return
+		
+		Print "Server @"+socket.Address+" ready"
+		
+		Local addr:=New SocketAddress
+				
+		Repeat
+		
+			Local data:Int
+			
+			If socket.ReceiveFrom( Varptr data,4,addr )<>4 Exit
+			
+			Print "Server received msg:"+data+" from client @"+addr
+			
+			data=-data
+				
+			socket.SendTo( Varptr data,4,addr )
+				
+		Forever
+		
+		socket.Close()
+		
+	End
+	
+	Method Client()
+	
+		Global _id:Int
+		
+		_id+=1
+		
+		Local id:=_id
+	
+		Fiber.Sleep( .5 )	'wait a bit for server to start
+		
+		Local socket:=Socket.Connect( "localhost",12345,SocketType.Datagram )
+		If Not socket Print "Client("+id+"): Couldn't connect to server" ; Return
+		
+		Print "Client("+id+") @"+socket.Address+" connected to @"+socket.PeerAddress
+		
+		For Local i:=0 Until 10
+		
+			Fiber.Sleep( Rnd( .1,.2 ) )
+		
+			Local data:Int=i*10
+			
+			socket.Send( Varptr data,4 )
+			
+			If socket.Receive( Varptr data,4 )<>4 Exit
+			
+			Print "Client("+id+") received reply:"+data+" from server"
+			
+		Next
+		
+		Print "Client("+id+") finished!"
+		
+	End
+	
+	Method OnRender( canvas:Canvas ) Override
+	
+'		App.RequestRender()
+		
+		Global ticks:=0
+		ticks+=1
+		
+		canvas.DrawText( ticks,0,0 )
+	End
+	
+End
+
+Function Main()
+
+	New AppInstance
+	New MyWindow
+	App.Run()
+
+End

+ 216 - 145
modules/std/socket/native/socket.cpp

@@ -52,6 +52,14 @@ namespace bbSocket{
 			return result;
 		}
 	};
+	
+	int err(){
+#if _WIN32	
+		return WSAGetLastError();
+#else
+		return errno;
+#endif
+	}
 		
 	void init(){
 		static bool done;
@@ -77,7 +85,7 @@ namespace bbSocket{
 #endif
 	}
 	
-	int _connect( const char *hostname,const char *service ){
+	int _connect( const char *hostname,const char *service,int type ){
 	
 		init();
 	
@@ -85,7 +93,7 @@ namespace bbSocket{
 		memset( &hints,0,sizeof( hints ) );
 
 		hints.ai_family=AF_UNSPEC;
-		hints.ai_socktype=SOCK_STREAM;
+		hints.ai_socktype=(type==1) ? SOCK_DGRAM : SOCK_STREAM;
 
 		addrinfo *res=0;
 		if( getaddrinfo( hostname,service,&hints,&res ) ) return -1;
@@ -116,7 +124,7 @@ namespace bbSocket{
 		return sock;
 	}
 	
-	int _listen( const char *service,int queue ){
+	int _bind( const char *service,int type ){
 	
 		init();
 	
@@ -124,7 +132,7 @@ namespace bbSocket{
 		memset( &hints,0,sizeof( hints ) );
 		
 		hints.ai_family=AF_UNSPEC;
-		hints.ai_socktype=SOCK_STREAM;
+		hints.ai_socktype=(type==1) ? SOCK_DGRAM : SOCK_STREAM;
 		hints.ai_flags=AI_PASSIVE;
 		
 		addrinfo *res=0;
@@ -140,7 +148,53 @@ namespace bbSocket{
 			
 			if( sock>=0 ){
 			
-				if( !bind( sock,res->ai_addr,res->ai_addrlen ) ) break;
+				if( !::bind( sock,res->ai_addr,res->ai_addrlen ) ) break;
+				
+				::closesocket( sock );
+				sock=-1;
+			}
+			
+			res=res->ai_next;
+		}
+		
+		freeaddrinfo( pres );
+		
+		if( sock<0 ) return -1;
+		
+// So server ports can be quickly reused...
+//
+#if __APPLE__ || __linux
+		int flag=1;
+		setsockopt( sock,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag) );
+#endif
+		return sock;
+	}
+	
+	int _listen( const char *service,int queue,int type ){
+	
+		init();
+	
+		addrinfo hints;
+		memset( &hints,0,sizeof( hints ) );
+		
+		hints.ai_family=AF_UNSPEC;
+		hints.ai_socktype=(type==1) ? SOCK_DGRAM : SOCK_STREAM;
+		hints.ai_flags=AI_PASSIVE;
+		
+		addrinfo *res=0;
+		if( getaddrinfo( 0,service,&hints,&res ) ) return -1;
+		
+		addrinfo *pres=res;
+		
+		int sock=-1;
+		
+		while( res ){
+		
+			sock=socket( res->ai_family,res->ai_socktype,res->ai_protocol );
+			
+			if( sock>=0 ){
+			
+				if( !::bind( sock,res->ai_addr,res->ai_addrlen ) ) break;
 				
 				::closesocket( sock );
 				sock=-1;
@@ -164,7 +218,7 @@ namespace bbSocket{
 		return sock;
 	}
 	
-	int connect( bbString hostname,bbString service ){
+	int connect( bbString hostname,bbString service,int type ){
 	
 		if( hostname.length()>1023 || service.length()>79 ) return -1;
 
@@ -182,7 +236,7 @@ namespace bbSocket{
 			
 			std::thread thread( [=,&future](){
 
-				future.set( _connect( _hostname,_service ) );
+				future.set( _connect( _hostname,_service,type ) );
 	
 			} );
 			
@@ -192,7 +246,38 @@ namespace bbSocket{
 
 		}else{
 		
-			result=_connect( _hostname,_service );
+			result=_connect( _hostname,_service,type );
+		}
+		
+		return result;
+	}
+	
+	int bind( bbString service ){
+	
+		if( service.length()>79 ) return -1;
+	
+		char _service[80];
+		strcpy( _service,service.c_str() );
+		
+		int result=-1;
+		
+		if( bbFiber::getCurrentFiber() ){
+
+			Future future;
+			
+			std::thread thread( [=,&future](){
+			
+				future.set( _bind( _service,1 ) );
+	
+			} );
+			
+			result=future.get();
+			
+			thread.join();
+			
+		}else{
+		
+			result=_bind( _service,1 );
 		}
 		
 		return result;
@@ -213,7 +298,7 @@ namespace bbSocket{
 			
 			std::thread thread( [=,&future](){
 			
-				future.set( _listen( _service,queue ) );
+				future.set( _listen( _service,queue,0 ) );
 	
 			} );
 			
@@ -223,7 +308,7 @@ namespace bbSocket{
 			
 		}else{
 		
-			result=_listen( _service,queue);
+			result=_listen( _service,queue,0 );
 		}
 		
 		return result;
@@ -279,53 +364,95 @@ namespace bbSocket{
 		}
 	}
 	
-	int send( int socket,void *data,int size ){
+	int cansend( int socket ){
+#if _WIN32
+		u_long count=0;
+#else
+		int count=0;
+		if( ioctl( socket,FIONWRITE,&count )<0 ) count=0;
+#endif
+		return count;
+	}
 	
-		int sent=0;
-		
-		while( size>0 ){
+	int canrecv( int socket ){
+#if _WIN32
+		u_long count=0;
+		if( ioctlsocket( socket,FIONREAD,&count )==SOCKET_ERROR ){
+			puts( "ERROR!" );
+			count=0;
+		}
+#else
+		int count=0;
+		if( ioctl( socket,FIONREAD,&count )<0 ) count=0;
+#endif
+		return count;
+	}
 	
-			int n=-1;
+	int send( int socket,void *data,int size ){
+	
+		int n=-1;
 		
-			if( bbFiber::getCurrentFiber() ){
+		if( bbFiber::getCurrentFiber() && cansend( socket )<size ){
 			
-				Future future;
+			Future future;
 				
-				std::thread thread( [=,&future](){
+			std::thread thread( [=,&future](){
 				
-					future.set( ::send( socket,(const char*)data,size,0 ) );
+				future.set( ::send( socket,(const char*)data,size,0 ) );
 					
-				} );
+			} );
 				
-				n=future.get();
+			n=future.get();
 				
-				thread.join();
+			thread.join();
 				
-			}else{
+		}else{
 	
-				n=::send( socket,(const char*)data,size,0 );
-			}
+			n=::send( socket,(const char*)data,size,0 );
+		}
 			
-			if( !n ) return sent;
+		if( n<0 ){
+			printf( "socket_send error! err=%i, socket=%i, data=%p, size=%i\n",err(),socket,data,size );fflush( stdout );
+		}
+		
+		return n;
+	}
+	
+	int sendto( int socket,void *data,int size,const void *addr,int addrlen ){
+	
+		int n=-1;
+		
+		if( bbFiber::getCurrentFiber() && cansend( socket )<size  ){
 			
-			if( n<0 ){
-				printf( "socket_send error! socket=%i, data=%p, size=%i\n",socket,data,size );fflush( stdout );
-				return sent;
-			}
+			Future future;
+				
+			std::thread thread( [=,&future](){
+				
+				future.set( ::sendto( socket,(const char*)data,size,0,(const sockaddr*)addr,addrlen ) );
+					
+			} );
+				
+			n=future.get();
+				
+			thread.join();
+				
+		}else{
+	
+			n=::sendto( socket,(const char*)data,size,0,(const sockaddr*)addr,addrlen );
+		}
 			
-			data=(char*)data+n;
-			size-=n;
-			sent+=n;
+		if( n<0 ){
+			printf( "socket_sendto error! err=%i, socket=%i, data=%p, size=%i\n",err(),socket,data,size );fflush( stdout );
 		}
 		
-		return sent;
+		return n;
 	}
 	
 	int recv( int socket,void *data,int size ){
 	
 		int n=-1;
 	
-		if( bbFiber::getCurrentFiber() ){
+		if( bbFiber::getCurrentFiber() && canrecv( socket )<size ){
 		
 			Future future;
 			
@@ -344,11 +471,38 @@ namespace bbSocket{
 			n=::recv( socket,(char*)data,size,0 );
 		}
 		
-		if( !n ) return 0;
+		if( n<0 ){
+			printf( "socket_recv error! err=%i, socket=%i, data=%p, size=%i\n",err(),socket,data,size );fflush( stdout );
+		}
+		
+		return n;
+	}
+	
+	int recvfrom( int socket,void *data,int size,void *addr,int *addrlen ){
+	
+		int n=-1;
+	
+		if( bbFiber::getCurrentFiber() && canrecv( socket )<size ){
+		
+			Future future;
+			
+			std::thread thread( [=,&future](){
+			
+				future.set( ::recvfrom( socket,(char*)data,size,0,(sockaddr*)addr,addrlen ) );
+				
+			} );
+			
+			n=future.get();
+			
+			thread.join();
+			
+		}else{
+
+			n=::recvfrom( socket,(char*)data,size,0,(sockaddr*)addr,addrlen );
+		}
 		
 		if( n<0 ){
-			printf( "socket_recv error! socket=%i, data=%p, size=%i\n",socket,data,size );fflush( stdout );
-			return 0;
+			printf( "socket_recvfrom error! n=%i, err=%i, socket=%i, data=%p, size=%i\n",n,err(),socket,data,size );fflush( stdout );
 		}
 		
 		return n;
@@ -356,8 +510,15 @@ namespace bbSocket{
 	
 	void setopt( int socket,bbString name,int value ){
 	
+		const char *ip=(const char*)&value;
+		int sz=sizeof(int);
+		
 		if( name=="TCP_NODELAY" ){
-			setsockopt( socket,IPPROTO_TCP,TCP_NODELAY,(const char*)&value,sizeof(value) );
+			setsockopt( socket,IPPROTO_TCP,TCP_NODELAY,ip,sz );
+		}else if( name=="SO_SNDTIMEO" ){
+			setsockopt( socket,SOL_SOCKET,SO_SNDTIMEO,ip,sz );
+		}else if( name=="SO_RCVTIMEO" ){
+			setsockopt( socket,SOL_SOCKET,SO_RCVTIMEO,ip,sz );
 		}
 	}
 	
@@ -366,122 +527,32 @@ namespace bbSocket{
 		int value=-1;
 		socklen_t optlen=sizeof(value);
 		
+		char *ip=(char*)&value;
+		int sz=sizeof(int);
+		int *szp=&sz;
+		
 		if( name=="TCP_NODELAY" ){
-			getsockopt( socket,IPPROTO_TCP,TCP_NODELAY,(char*)&value,&optlen );
+			getsockopt( socket,IPPROTO_TCP,TCP_NODELAY,ip,szp );
+		}else if( name=="SO_SNDTIMEO" ){
+			getsockopt( socket,SOL_SOCKET,SO_SNDTIMEO,ip,szp );
+		}else if( name=="SO_RCVTIMEO" ){
+			getsockopt( socket,SOL_SOCKET,SO_RCVTIMEO,ip,szp );
 		}
-		
-		return value;
 	}
 	
-
-	/*	***** EXPERIMENTAL *****
+	int getsockaddr( int socket,void *addr,int *addrlen ){
 	
-	int send( int socket,void *data,int size ){
+		return getsockname( socket,(sockaddr*)addr,addrlen );
+	}
 	
-		if( !size ) return 0;
+	int getpeeraddr( int socket,void *addr,int *addrlen ){
 	
-		int sent=0;
-		
-		while( size ){
-		
-			int n=::send( socket,(const char*)data,size,0 );
-			
-			if( !n ) return sent;
-			
-			if( n<0 ){
-
-				if( !wouldBlock() ){
-					printf( "socket_send error!\n",fflush( stdout ) );
-					return sent;
-				}
-				
-				Future future;
-	
-				std::thread thread( [=,&future](){
-					
-					fd_set writeset;
-						
-					FD_ZERO( &writeset );
-					FD_SET( socket,&writeset );
-					
-					if( ::select( socket+1,0,&writeset,0,0 )==1 ){
-						
-						future.set( ::send( socket,(const char*)data,size,0 ) );
-						
-					}else{
-					
-						future.set( -1 );
-					}
-					
-				} );
-					
-				n=future.get();
-				
-				thread.join();
-				
-				if( n<0 ){
-					printf( "socket_send error!\n",fflush( stdout ) );
-					return sent;
-				}
-			}
-
-			data=(char*)data+n;
-			size-=n;
-			sent+=n;
-		}
-		
-		return sent;
+		return getpeername( socket,(sockaddr*)addr,addrlen );
 	}
-	*/
-
-	/*	***** EXPERIMENTAL *****
 	
-	int recv( int socket,void *data,int size ){
-		
-		if( !size ) return 0;
-		
-		for( ;; ){
+	int sockaddrname( const void *addr,int addrlen,char *host,char *service ){
 	
-			int n=::recv( socket,(char*)data,size,0 );
-				
-			if( !n ) return 0;
-				
-			if( n<0 ){
-				
-				if( !wouldBlock() ){
-					printf( "socket_recv error!\n" );fflush( stdout );
-					return 0;
-				}
-				
-				Future future;
-						
-				std::thread thread( [=,&future](){
-						
-					fd_set readset;
-							
-					FD_ZERO( &readset );
-					FD_SET( socket,&readset );
-							
-					if( ::select( socket+1,&readset,0,0,0 )==1 ){
-						future.set( ::recv( socket,(char*)data,size,0 ) );
-					}else{
-						future.set( -1 );
-					}
-				} );
-						
-				n=future.get();
-					
-				thread.join();
-					
-				if( n<0 ){
-					printf( "socket_recv error!\n" );fflush( stdout );
-					return 0;
-				}
-			}
-				
-			return n;
-		}
+		getnameinfo( (const sockaddr*)addr,addrlen,host,1023,service,79,0 );
 	}
-	*/
 	
 }

+ 19 - 3
modules/std/socket/native/socket.h

@@ -6,7 +6,9 @@
 
 namespace bbSocket{
 
-	int connect( bbString hostname,bbString service );
+	int connect( bbString hostname,bbString service,int type );
+	
+	int bind( bbString service );
 
 	int listen( bbString service,int queue );
 	
@@ -15,12 +17,26 @@ namespace bbSocket{
 	void close( int socket );
 	
 	int send( int socket,void *data,int size );
-	
-	int recv( int socket,void *data,int size );
 
+	int recv( int socket,void *data,int size );
+	
+	int sendto( int socket,void *data,int size,const void *sockaddr,int addrlen );
+	
+	int recvfrom( int socket,void *data,int size,void *sockaddr,int *addrlen );
+	
 	void setopt( int socket,bbString name,int value );
 	
 	int getopt( int socket,bbString name );
+	
+	int cansend( int socket );
+	
+	int canrecv( int socket );
+	
+	int getsockaddr( int socket,void *sockaddr,int *addrlen );
+
+	int getpeeraddr( int socket,void *sockaddr,int *addrlen );
+
+	int sockaddrname( const void *sockaddr,int addrlen,char *host,char *service );
 }
 
 #endif

+ 197 - 9
modules/std/socket/socket.monkey2

@@ -10,11 +10,13 @@ Namespace std.socket
 
 Extern
 
-'Note: should probably just make this an extern bbSocket struct, ala bbProcess.
+#rem monkeydoc @hidden
+#end
+Function socket_connect:Int( hostname:String,service:String,type:Int )="bbSocket::connect"
 
 #rem monkeydoc @hidden
 #end
-Function socket_connect:Int( hostname:String,service:String )="bbSocket::connect"
+Function socket_bind:Int( service:String )="bbSocket::bind"
 
 #rem monkeydoc @hidden
 #end
@@ -36,6 +38,14 @@ Function socket_send:Int( socket:Int,data:Void Ptr,size:Int )="bbSocket::send"
 #end
 Function socket_recv:Int( socket:Int,data:Void Ptr,size:Int )="bbSocket::recv"
 
+#rem monkeydoc @hidden
+#end
+Function socket_sendto:Int( socket:Int,data:Void Ptr,size:Int,addr:Void ptr,addrlen:Int )="bbSocket::sendto"
+
+#rem monkeydoc @hidden
+#end
+Function socket_recvfrom:Int( socket:Int,data:Void Ptr,size:Int,addr:Void ptr,addrlen:Int Ptr )="bbSocket::recvfrom"
+
 #rem monkeydoc @hidden
 #end
 Function socket_setopt( socket:Int,opt:String,value:Int )="bbSocket::setopt"
@@ -44,15 +54,153 @@ Function socket_setopt( socket:Int,opt:String,value:Int )="bbSocket::setopt"
 #end
 Function socket_getopt:Int( socket:Int,opt:String )="bbSocket::getopt"
 
+#rem monkeydoc @hidden
+#end
+Function socket_cansend:Int( socket:Int )="bbSocket::cansend"
+
+#rem monkeydoc @hidden
+#end
+Function socket_canrecv:Int( socket:Int )="bbSocket::canrecv"
+
+#rem monkeydoc @hidden
+#end
+Function socket_getsockaddr:Int( socket:Int,addr:Void Ptr,addrlen:Int Ptr )="bbSocket::getsockaddr"
+
+#rem monkeydoc @hidden
+#end
+Function socket_getpeeraddr:Int( socket:Int,addr:Void Ptr,addrlen:Int Ptr )="bbSocket::getpeeraddr"
+
+#rem monkeydoc @hidden
+#end
+Function socket_sockaddrname:Int( addr:Void Ptr,addrlen:Int,host:libc.char_t Ptr,service:libc.char_t Ptr )="bbSocket::sockaddrname"
+
 Public
 
-Struct Socket
+Enum SocketType
+
+	Stream=0
+	Datagram=1
+
+End
+
+Class SocketAddress
+
+	Property Host:String()
+		Validate()
+		Return _host
+	End
+	
+	Property Service:String()
+		Validate()
+		Return _service
+	End
+	
+	Method To:String()
+		Return Host+":"+Service
+	End
+	
+	Private
+	
+	Field _addr:=New Byte[128]
+	Field _addrlen:Int=0
+
+	Field _dirty:Bool=False	
+	Field _host:String=""
+	Field _service:String=""
+	
+	Method Validate()
+		If Not _dirty Return
+		
+		Local host:=New libc.char_t[1024]
+		Local service:=New libc.char_t[80]
+		
+		If socket_sockaddrname( _addr.Data,_addrlen,host.Data,service.Data )>=0
+			_host=String.FromCString( host.Data )
+			_service=String.FromCString( service.Data )
+		Else
+			_host=""
+			_service=""
+		Endif
+		
+		_dirty=False
+	End
+
+	Property Addr:Void Ptr()
+		Return _addr.Data
+	End
+	
+	Property Addrlen:Int()
+		Return _addrlen
+	End
+	
+	Method Update( addrlen:Int )
+		_addrlen=addrlen
+		If _addrlen
+			_dirty=True
+			Return
+		Endif
+		_host=""
+		_service=""
+		_dirty=False
+	End
+	
+End
+
+Class Socket
 
-	#rem monkeydoc True if socket is currently open.
+	#rem Not on Windows...
+	
+	#rem monkeydoc The number of bytes that be sent to the socket without it blocking.
+	#end
+	Property CanSend:Int()
+		If _socket=-1 Return 0
+		
+		Return socket_cansend( _socket )
+	End
 	#end
-	Property IsOpen:Bool()
 	
-		Return _socket<>-1
+	#rem  monkeydoc True if socket has been closed
+	#end
+	Property Closed:Bool()
+		Return _socket=-1
+	End
+	
+	#rem monkeydoc The number of bytes that can be received from the socket without blocking.
+	#end
+	Property CanReceive:Int()
+		If _socket=-1 Return 0
+		
+		Return socket_canrecv( _socket )
+	End
+	
+	#rem monkeydoc The address of the socket.
+	#end
+	Property Address:SocketAddress()
+		If _socket=-1 Return Null
+	
+		If Not _addr
+			Local addrlen:Int=128
+			_addr=New SocketAddress
+			Local n:=socket_getsockaddr( _socket,_addr.Addr,Varptr addrlen )
+			_addr.Update( n>=0 ? addrlen Else 0 )
+		Endif
+		
+		Return _addr
+	End
+	
+	#rem monkeydoc The address of the socket peer.
+	#end
+	Property PeerAddress:SocketAddress()
+		If _socket=-1 Return Null
+
+		If Not _peer
+			Local addrlen:Int=128
+			_peer=New SocketAddress
+			Local n:=socket_getpeeraddr( _socket,_peer.Addr,Varptr addrlen )
+			_peer.Update( n>=0 ? addrlen Else 0 )
+		Endif
+		
+		Return _peer
 	End
 
 	#rem monkeydoc Accepts a new incoming connection on a listening socket.
@@ -78,9 +226,10 @@ Struct Socket
 	#end	
 	Method Close()
 		If _socket=-1 Return
-
 		socket_close( _socket )
 		_socket=-1
+		_addr=Null
+		_peer=null
 	End
 	
 	#rem monkeydoc Sends data on a connected socket.
@@ -104,6 +253,14 @@ Struct Socket
 		Return socket_send( _socket,data,size )
 	End
 	
+	Method SendTo:Int( data:Void Ptr,size:Int,address:SocketAddress )
+		If _socket=-1 Return 0
+		
+		DebugAssert( address.Addrlen,"SocketAddress is invalid" )
+		
+		Return socket_sendto( _socket,data,size,address.Addr,address.Addrlen )
+	End
+	
 	#rem monkeydoc Receives data on a connected socket.
 	
 	Reads at most `size` bytes from the socket.
@@ -125,6 +282,18 @@ Struct Socket
 		Return socket_recv( _socket,data,size )
 	End
 	
+	Method ReceiveFrom:Int( data:Void Ptr,size:Int,address:SocketAddress )
+		If _socket=-1 Return 0
+		
+		Local addrlen:Int=128
+		
+		Local n:=socket_recvfrom( _socket,data,size,address.Addr,Varptr addrlen  )
+		
+		address.Update( n>=0 ? addrlen Else 0 )
+		
+		Return n
+	End
+	
 	#rem monkeydoc Sets a socket option.
 
 	Currently, only "TCP_NODELAY" is supported, which should be 1 to enable, 0 to disable.
@@ -153,9 +322,9 @@ Struct Socket
 	@return A new socket.
 	
 	#end	
-	Function Connect:Socket( hostname:String,service:String )
+	Function Connect:Socket( hostname:String,service:String,type:SocketType=SocketType.Stream )
 
-		Local socket:=socket_connect( hostname,service )
+		Local socket:=socket_connect( hostname,service,type )
 		If socket=-1 Return Null
 		
 		Return New Socket( socket )
@@ -169,6 +338,23 @@ Struct Socket
 
 	@return A new socket.
 	
+	#end
+	Function Bind:Socket( service:String )
+	
+		Local socket:=socket_bind( service )
+		If socket=-1 Return Null
+		
+		Return New Socket( socket )
+	End
+
+	#rem monkeydoc Creates a server socket and listens on it.
+	
+	Returns a new server socket listening at `service` if successful.
+	
+	Returns a closed socket upon failure.
+
+	@return A new socket.
+	
 	#end
 	Function Listen:Socket( service:String,queue:Int=128 )
 	
@@ -181,6 +367,8 @@ Struct Socket
 	Private
 	
 	Field _socket:Int=-1
+	Field _addr:SocketAddress
+	Field _peer:SocketAddress
 	
 	Method New( socket:Int )
 

+ 23 - 85
modules/std/socket/socketstream.monkey2

@@ -3,6 +3,16 @@ Namespace std.socket
 
 Class SocketStream Extends std.stream.Stream
 
+	#rem monkeydoc Creates a new socketsteam from an existing socket.
+
+	Note: Closing the stream will also close the socket.
+	
+	#end	
+	Method New( socket:Socket )
+	
+		_socket=socket
+	End
+	
 	#rem monkeydoc The underlying socket.
 	#end
 	Property Socket:Socket()
@@ -14,7 +24,7 @@ Class SocketStream Extends std.stream.Stream
 	#end
 	Property Eof:Bool() Override
 
-		Return Not _socket.IsOpen
+		Return _socket.Closed
 	End
 
 	#rem monkeydoc Always 0.
@@ -80,96 +90,24 @@ Class SocketStream Extends std.stream.Stream
 	#end
 	Method Write:Int( buf:Void Ptr,count:Int ) Override
 	
-		Return _socket.Send( buf,count )
-	End
-
-	#rem monkeydoc Connects to a host/service.
-	
-	Returns a new socket stream if successful, else null.
-	
-	`service` can be an integer port number.
-	
-	@param hostname The name of the host to connect to.
-	
-	@param service The service or port to connect to.
-	
-	#end	
-	Function Connect:SocketStream( hostname:String,service:String )
-	
-		Local socket:=std.socket.Socket.Connect( hostname,service )
-		If Not socket.IsOpen Return Null
+		Local sent:=0
 		
-		Return New SocketStream( socket )
-	End
-	
-	Private
-	
-	Field _socket:Socket
-	
-	Method New( socket:Socket )
-	
-		_socket=socket
-	End
-
-End
-
-Class SocketServer
-
-	#rem monkeydoc The underlying socket.
-	#end
-	Property Socket:Socket()
-	
-		Return _socket
-	End
-
-	#rem monkeydoc Closes the server.
-	#end
-	Method Close()
-	
-		_socket.Close()
-	End
-
-	#rem monkeydoc Accepts a new connection.
-	
-	Waits until a new incoming connection is available.
-	
-	@return A new connection, or null if there is a network error.
-	
-	#end
-	Method Accept:SocketStream()
-	
-		Local socket:=_socket.Accept()
-		If Not socket.IsOpen Return Null
+		While count>0
 		
-		Return New SocketStream( socket )
-	End
-
-	#rem monkeydoc Creates a server and starts listening.
-	
-	Returns a new server if successful, else null.
-	
-	`service` can be an integer port number.
-	
-	@param service The service or port to listen on.
-	
-	@param queue The number of incoming connections that can be queued.
-	
-	#end
-	Function Listen:SocketServer( service:String,queue:Int=128 )
-	
-		Local socket:=std.socket.Socket.Listen( service,queue )
-		If Not socket.IsOpen Return Null
+			Local n:=_socket.Send( buf,count )
+			If n<0 Exit
+			
+			buf=Cast<Byte Ptr>( buf )+n
+			count-=n
+			sent+=n
+
+		Wend
 		
-		Return New SocketServer( socket )
+		Return sent
 	End
-	
+
 	Private
 	
 	Field _socket:Socket
-	
-	Method New( socket:Socket )
-
-		_socket=socket
-	End
 
 End

+ 35 - 3
modules/std/stream/stream.monkey2

@@ -299,21 +299,33 @@ Class Stream
 		Return str
 	End
 	
+	#rem monkeydoc Reads a size prefixed string from the stream.
+	
+	Reads an int from the stream, then a string from that many bytes.
+
+	@return the string read.
+	
+	#end
+	Method ReadSizedString:String()
+		Local n:=ReadInt()
+		Local data:=ReadAll( n )
+		Local str:=data.PeekString( 0 )
+		data.Discard()
+		Return str
+	End
+	
 	#rem monkeydoc Reads a null terminated string from the stream.
 	
 	@return the string read.
 	
 	#end
 	Method ReadNullTerminatedString:String()
-	
 		Local buf:=New Stack<Byte>
-		
 		While Not Eof
 			Local chr:=ReadByte()
 			If Not chr Exit
 			buf.Push( chr )
 		Wend
-		
 		Return String.FromCString( buf.Data.Data,buf.Length )
 	End
 	
@@ -429,6 +441,26 @@ Class Stream
 		buf.Discard()
 	End
 	
+	#rem monkeydoc Writes a size prefixed string to the stream.
+	
+	Writes an int containing the size of the string to the stream, followed the string itself.
+	
+	#end
+	Method WriteSizedString( str:String )
+		WriteInt( str.CStringLength )
+		WriteString( str )
+	End
+	
+	#rem monkeydoc Writes a null terminate string to the stream.
+	
+	@param str The string to write.
+	
+	#end
+	Method WriteNullTerminatedString( str:String )
+		WriteString( str )
+		WriteByte( 0 )
+	End
+	
 	#rem monkeydoc Opens a stream
 	
 	`mode` should be "r" for read, "w" for write or "rw" for read/write.