added some asycn socket code
This commit is contained in:
		| @ -258,7 +258,9 @@ class AsyncHandle(Object) | ||||
|  | ||||
| class Socket(AsyncHandle) from 'sck' | ||||
| { | ||||
| 	var(#get) eventActions. | ||||
| 	var(#get) dataInEventAction. | ||||
| 	var(#get) dataOutEventAction. | ||||
|  | ||||
| 	var pending_bytes, pending_offset, pending_length. | ||||
| 	var outreadysem, outdonesem, inreadysem. | ||||
|  | ||||
| @ -288,13 +290,6 @@ pooldic Socket.Type | ||||
| 	DGRAM  := 2. | ||||
| } | ||||
|  | ||||
| pooldic Socket.EventType | ||||
| { | ||||
| 	CONNECTED := 0. | ||||
| 	DATA_IN := 1. | ||||
| 	DATA_OUT := 2. | ||||
| } | ||||
|  | ||||
| extend Socket | ||||
| { | ||||
| 	method(#class) new { self messageProhibited: #new } | ||||
| @ -308,31 +303,43 @@ extend Socket | ||||
| 	method initialize | ||||
| 	{ | ||||
| 		super initialize. | ||||
| 		self.eventActions := %(nil, nil, nil). | ||||
|  | ||||
| 		self.outdonesem := Semaphore new. | ||||
| 		self.outreadysem := Semaphore new. | ||||
| 		self.inreadysem := Semaphore new. | ||||
|  | ||||
| 		self.outdonesem signalAction: [ :sem | | ||||
| 			(self.eventActions at: Socket.EventType.DATA_OUT) value: self. | ||||
| 			self.dataOutEventAction value: self. | ||||
| 			##(self.eventActions at: Socket.EventType.DATA_OUT) value: self. | ||||
| 			System unsignal: self.outreadysem. | ||||
| 		]. | ||||
|  | ||||
| 		self.outreadysem signalAction: [ :sem | | ||||
| 			| nwritten | | ||||
| 			nwritten := self _writeBytes: self.pending_bytes offset: self.pending_offset length: self.pending_length. | ||||
| 			if (nwritten >= 0) | ||||
| 			| nbytes pos rem | | ||||
|  | ||||
| 			pos := self.pending_offset. | ||||
| 			rem := self.pending_length. | ||||
|  | ||||
| 			while (rem > 0) | ||||
| 			{ | ||||
| 				nbytes := self _writeBytes: self.pending_bytes offset: pos length: rem. | ||||
| 				if (nbytes <= -1) { break }. | ||||
| 				pos := pos + nbytes. | ||||
| 				rem := rem - nbytes. | ||||
| 			}. | ||||
|  | ||||
| 			if (rem <= 0) | ||||
| 			{ | ||||
| 				self.pending_bytes := nil. | ||||
| 				self.pending_offset := 0. | ||||
| 				self.pending_length := 0. | ||||
| 				self.outdonesem signal. | ||||
| 			} | ||||
| 			}. | ||||
| 		]. | ||||
|  | ||||
| 		self.inreadysem signalAction: [ :sem | | ||||
| 			(self.eventActions at: Socket.EventType.DATA_IN) value: self. | ||||
| 			##(self.eventActions at: Socket.EventType.DATA_IN) value: self. | ||||
| 			self.dataInEventAction value: self. | ||||
| 		]. | ||||
| 	} | ||||
|  | ||||
| @ -370,14 +377,26 @@ extend Socket | ||||
| 		^super close. | ||||
| 	} | ||||
|  | ||||
| 	 | ||||
| 	method onEvent: event_type do: action_block | ||||
| 	{ | ||||
| 		self.eventActions at: event_type put: action_block. | ||||
| 		if (event_type == #data_in)  | ||||
| 		{  | ||||
| 			self.dataInEventAction := action_block. | ||||
| 		} | ||||
| 		elsif (event_type == #data_out) | ||||
| 		{ | ||||
| 			self.dataOutEventAction := action_block. | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			Exception signal: 'unknown event type ' & event_type asString. | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	method writeBytes: bytes offset: offset length: length | ||||
| 	{ | ||||
| 		| n | | ||||
| 		| n pos rem | | ||||
|  | ||||
| 		if (self.outreadysem _group notNil) | ||||
| 		{ | ||||
| @ -387,40 +406,43 @@ extend Socket | ||||
| 		## n >= 0: written | ||||
| 		## n <= -1: tolerable error (e.g. EAGAIN) | ||||
| 		## exception: fatal error | ||||
| 		##while (true) ## TODO: loop to write as much as possible | ||||
| 		##{ | ||||
| 			n := self _writeBytes: bytes offset: offset length: length. | ||||
| 			if (n >= 0)  | ||||
| 			{ | ||||
| 				self.outdonesem signal. | ||||
| 				^n  | ||||
| 			}. | ||||
| 		##}. | ||||
|  | ||||
| 		## TODO: adjust offset and length  | ||||
| 		pos := offset. | ||||
| 		rem := length. | ||||
|  | ||||
| 		while (rem > 0) ## TODO: loop to write as much as possible | ||||
| 		{ | ||||
| 			n := self _writeBytes: bytes offset: pos length: rem. | ||||
| 			if (n <= -1)  { break }. | ||||
| 			rem := rem - n. | ||||
| 			pos := pos + n. | ||||
| 		}. | ||||
|  | ||||
| 		if (rem <= 0) | ||||
| 		{ | ||||
| 			self.outdonesem signal. | ||||
| 			^length | ||||
| 		}. | ||||
|  | ||||
| 		self.pending_bytes := bytes. | ||||
| 		self.pending_offset := offset. | ||||
| 		self.pending_length := length. | ||||
| 		self.pending_offset := pos. | ||||
| 		self.pending_length := rem | ||||
|  | ||||
| 		System addAsyncSemaphore: self.outreadysem. | ||||
| 		System signal: self.outreadysem onOutput: self.handle. | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	method beWatched | ||||
| 	{ | ||||
| 		System addAsyncSemaphore: self.inreadysem. | ||||
| 		System signal: self.inreadysem onInput: self.handle. | ||||
| 		System addAsyncSemaphore: self.outdonesem. | ||||
| 	} | ||||
|  | ||||
| 	method beUnwatched | ||||
| 	{ | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class ClientSocket(Socket) | ||||
| { | ||||
| 	var(#get) connectedEventAction. | ||||
| 	var connsem. | ||||
|  | ||||
| 	method initialize | ||||
| @ -438,13 +460,8 @@ class ClientSocket(Socket) | ||||
| 				System unsignal: sem. | ||||
| 				System removeAsyncSemaphore: sem. | ||||
|  | ||||
| 'CHECKING FOR CONNECTION.....' dump. | ||||
| 				(self.eventActions at: Socket.EventType.CONNECTED) value: self value: (soerr == 0). | ||||
|  | ||||
| 				if (soerr == 0) | ||||
| 				{ | ||||
| 					self beWatched | ||||
| 				}. | ||||
| 				self.connectedEventAction value: self value: (soerr == 0). | ||||
| 				if (soerr == 0) { self beWatched }. | ||||
| 			}. | ||||
| 			(* HOW TO HANDLE TIMEOUT? *) | ||||
| 		]. | ||||
| @ -461,6 +478,16 @@ class ClientSocket(Socket) | ||||
| 		^super close | ||||
| 	} | ||||
|  | ||||
| 	method onEvent: event_type do: action_block | ||||
| 	{ | ||||
| 		if (event_type == #connected) | ||||
| 		{ | ||||
| 			self.connectedEventAction := action_block. | ||||
| 			^self. | ||||
| 		}. | ||||
|  | ||||
| 		^super onEvent: event_type do: action_block | ||||
| 	} | ||||
| 	method connect: target | ||||
| 	{ | ||||
| 		| sem | | ||||
| @ -473,7 +500,7 @@ class ClientSocket(Socket) | ||||
| 		{ | ||||
| 			## connected immediately | ||||
| 'IMMEDIATELY CONNECTED.....' dump. | ||||
| 			(self.eventActions at: Socket.EventType.CONNECTED) value: self value: true. | ||||
| 			self.connectedEventAction value: self value: true. | ||||
|  | ||||
| 			System addAsyncSemaphore: self.inreadysem. | ||||
| 			System signal: self.inreadysem onInput: self.handle. | ||||
| @ -484,6 +511,8 @@ class ClientSocket(Socket) | ||||
|  | ||||
| class ServerSocket(Socket) | ||||
| { | ||||
| 	var(#get) acceptedEventAction. | ||||
|  | ||||
| 	method initialize | ||||
| 	{ | ||||
| 'Server Socket initialize...........' dump. | ||||
| @ -499,10 +528,9 @@ class ServerSocket(Socket) | ||||
| 				## i should invoke it manually here. | ||||
| 				clisck initialize. | ||||
|  | ||||
| 				cliact := self.eventActions at: Socket.EventType.CONNECTED. | ||||
| 				if (cliact notNil)  | ||||
| 				if (self.acceptedEventAction notNil)  | ||||
| 				{  | ||||
| 					cliact value: self value: clisck (* value: cliaddr *). | ||||
| 					self.acceptedEventAction value: self value: clisck value: cliaddr. | ||||
| 					clisck beWatched. | ||||
| 				} | ||||
| 				else { clisck close }. | ||||
| @ -523,6 +551,17 @@ class ServerSocket(Socket) | ||||
| 		^super close. | ||||
| 	} | ||||
|  | ||||
| 	method onEvent: event_type do: action_block | ||||
| 	{ | ||||
| 		if (event_type == #accepted) | ||||
| 		{ | ||||
| 			self.acceptedEventAction := action_block. | ||||
| 			^self. | ||||
| 		}. | ||||
|  | ||||
| 		^super onEvent: event_type do: action_block | ||||
| 	} | ||||
|  | ||||
| 	method listen: backlog | ||||
| 	{ | ||||
| 		System addAsyncSemaphore: self.inreadysem. | ||||
| @ -533,72 +572,90 @@ class ServerSocket(Socket) | ||||
|  | ||||
| class MyObject(Object) | ||||
| { | ||||
| 	method(#class) start_server_socket | ||||
| 	{ | ||||
| 		| s2 buf | | ||||
| 		s2 := ServerSocket domain: Socket.Domain.INET type: Socket.Type.STREAM. | ||||
| 		buf := ByteArray new: 128. | ||||
|  | ||||
| 		s2 onEvent: #accepted do: [ :sck :clisck :cliaddr | | ||||
| 'SERVER ACCEPTED new client' dump. | ||||
| 			clisck onEvent: #data_in do: [ :csck | | ||||
| 				| nbytes | | ||||
| 				while (true) | ||||
| 				{ | ||||
| 					nbytes := csck readBytes: buf.  | ||||
| 					if (nbytes <= 0) | ||||
| 					{ | ||||
| 						if (nbytes == 0) { csck close }. | ||||
| 						('Got ' & (nbytes asString)) dump. | ||||
| 						break. | ||||
| 					}. | ||||
|  | ||||
| 					buf dump. | ||||
| 					csck writeBytes: buf offset: 0 length: nbytes. | ||||
| 				}. | ||||
| 			]. | ||||
| 			clisck onEvent: #data_out do: [ :csck | | ||||
| 				##csck writeBytes: #[ $a, $b, C'\n' ]. | ||||
| 			]. | ||||
| 		]. | ||||
|  | ||||
| 		s2 bind: (SocketAddress fromString: '0.0.0.0:7777'). | ||||
| 		s2 listen: 10. | ||||
| 		^s2. | ||||
| 	} | ||||
|  | ||||
| 	method(#class) start_client_socket | ||||
| 	{ | ||||
| 		| s buf count | | ||||
| 		s := ClientSocket domain: Socket.Domain.INET type: Socket.Type.STREAM. | ||||
| 		buf := ByteArray new: 128. | ||||
|  | ||||
| 		count := 0. | ||||
|  | ||||
| 		s onEvent: #connected do: [ :sck :state | | ||||
| 			if (state) | ||||
| 			{ | ||||
| 				s writeBytes: #[ $a, $b, $c ]. | ||||
| 				s writeBytes: #[ $d, $e, $f ]. | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				'FAILED TO CONNECT' dump. | ||||
| 			}. | ||||
| 		].  | ||||
|  | ||||
| 		s onEvent: #data_in do: [ :sck | | ||||
| 			| nbytes | | ||||
| 			while (true) | ||||
| 			{ | ||||
| 				nbytes := sck readBytes: buf.  | ||||
| 				if (nbytes <= 0)  | ||||
| 				{ | ||||
| 					if (nbytes == 0) { sck close }. | ||||
| 					break. | ||||
| 				}. | ||||
| 				('Got ' & (nbytes asString)) dump. | ||||
| 				buf dump. | ||||
| 			}. | ||||
| 		]. | ||||
| 		s onEvent: #data_out do: [ :sck | | ||||
| 			if (count < 10) { sck writeBytes: #[ $a, $b, C'\n' ]. count := count + 1. }. | ||||
| 		]. | ||||
|  | ||||
| 		s connect: (SocketAddress fromString: '127.0.0.1:9999'). | ||||
| 	} | ||||
|  | ||||
| 	method(#class) main | ||||
| 	{ | ||||
| 		[ | ||||
| 			| s s2 st sg ss buf count | | ||||
| 			| s s2 ss | | ||||
|  | ||||
| 			count := 0. | ||||
| 			[ | ||||
| 				buf := ByteArray new: 128. | ||||
| 				s := ClientSocket domain: Socket.Domain.INET type: Socket.Type.STREAM. | ||||
| 				s2 := ServerSocket domain: Socket.Domain.INET type: Socket.Type.STREAM. | ||||
| 		 | ||||
| 				s2 onEvent: Socket.EventType.CONNECTED do: [ :sck :clisck | | ||||
| 'SERVER ACCEPTED new client' dump. | ||||
| 					clisck onEvent: Socket.EventType.DATA_IN do: [ :csck | | ||||
| 						| nbytes | | ||||
| 						nbytes := csck readBytes: buf.  | ||||
| 						if (nbytes == 0) | ||||
| 						{ | ||||
| 							csck close. | ||||
| 						}. | ||||
| 						('Got ' & (nbytes asString)) dump. | ||||
| 						 | ||||
| 						if (nbytes > 0)  | ||||
| 						{  | ||||
| 					 | ||||
| 							buf dump. | ||||
| 							csck writeBytes: buf offset: 0 length: nbytes. | ||||
| 						}. | ||||
| 					]. | ||||
| 					clisck onEvent: Socket.EventType.DATA_OUT do: [ :csck | | ||||
| 						##csck writeBytes: #[ $a, $b, C'\n' ]. | ||||
| 					]. | ||||
| 					###clisck close. | ||||
| 				]. | ||||
| 				s := self start_client_socket. | ||||
| 				s2 := self start_server_socket. | ||||
|  | ||||
| 				s2 bind: (SocketAddress fromString: '0.0.0.0:7777'). | ||||
| 				s2 listen: 10. | ||||
|  | ||||
| 				s onEvent: Socket.EventType.CONNECTED do: [ :sck :state | | ||||
| 					if (state) | ||||
| 					{					 | ||||
| 						s writeBytes: #[ $a, $b, $c ]. | ||||
| 						s writeBytes: #[ $d, $e, $f ]. | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						'FAILED TO CONNECT' dump. | ||||
| 					}. | ||||
| 				].  | ||||
|  | ||||
| 				s onEvent: Socket.EventType.DATA_IN do: [ :sck | | ||||
| 					| nbytes | | ||||
| 					nbytes := sck readBytes: buf.  | ||||
| 					if (nbytes == 0) | ||||
| 					{ | ||||
| 						sck close. | ||||
| 						s := nil. | ||||
| 					}. | ||||
| 					('Got ' & (nbytes asString)) dump. | ||||
| 					buf dump. | ||||
| 				]. | ||||
| 				s onEvent: Socket.EventType.DATA_OUT do: [ :sck | | ||||
| 					if (count < 10) { sck writeBytes: #[ $a, $b, C'\n' ]. count := count + 1. }. | ||||
| 				]. | ||||
|  | ||||
| 				s connect: (SocketAddress fromString: '127.0.0.1:9999'). | ||||
| 				while (true) | ||||
| 				{ | ||||
| 					ss := System handleAsyncEvent. | ||||
| @ -609,6 +666,7 @@ class MyObject(Object) | ||||
| 			ensure: | ||||
| 			[ | ||||
| 				if (s notNil) { s close }. | ||||
| 				if (s2 notNil) { s2 close }. | ||||
| 			] | ||||
|  | ||||
| 		] on: Exception do: [:ex | ('Exception - '  & ex messageText) dump ]. | ||||
|  | ||||
		Reference in New Issue
	
	Block a user