Hola, nuevamente escribimos a la lista para pedir una mano. Resumiendo rapidamente: nuestra ausencia fue para hacer algunas adaptaciones a lo que nos mando Edgar. Las minimas modificaciones y adaptaciones fueron hechas y finalmente tenemos un prototipo primitivo del servidor TCP. El UDP se concreto con exito y funciona relativamente bien. El problema se suscito cuando al probar el servidor TCP varias veces (un numero variable) se rompio la VM, dejando a squeak colgado y tuvimos que reiniciar la PC porque las DLL no se liberaban y quedaba parte de la VM en ram, por consiguiente luego de la primer caida teniamos que reiniciar la pc... se caia de vuelta a la 5º ejecucion y lo mismo... variando el numero de ejecuciones. Nuestra duda se incremento cuando nos enteramos que algunas personas opinan que Squeak anda flojito en redes ¿Es eso cierto? ¿Acaso el codigo adaptado tiene algun error inherente que produce la falla? ¿Somos muy maletas? Esperamos ayuda y sugerencias. Muchas gracias,
abajo enviamos el codigo y los ejemplos para montar un servidor y un cliente.
|t| "Ejemplo de servidor"
t:=TCPServer new.
t initialize: 8000.
t condition: [false].
t parameters: (Array new).
t listen.
|c| "Ejemplo de cliente"
c:=ClientApplicationModel newTCP: hostname atPort: 8000.
c client talk: 'conectarse'
c client talk: 'FIN' "indispensable, por ahora"
c client disconnect.
Ahora adjuntamos el .st
Object subclass: #AbstractClient
instanceVariableNames: 'socket application buffer'
classVariableNames: ''
poolDictionaries: ''
category: 'MetoPoo - Capa de red'!
instanceVariableNames: 'socket application buffer'
classVariableNames: ''
poolDictionaries: ''
category: 'MetoPoo - Capa de red'!
!AbstractClient methodsFor: 'getters' stamp: 'AAT 1/4/2005 20:50'!
application
^ application! !
application
^ application! !
!AbstractClient methodsFor: 'getters' stamp: 'A 1/8/2005 15:49'!
buffer
buffer
^buffer.! !
!AbstractClient methodsFor: 'getters' stamp: 'AAT 1/4/2005 21:10'!
socket
^socket.! !
socket
^socket.! !
!AbstractClient methodsFor: 'initialize-release' stamp: 'AH 1/13/2005 23:29'!
connectTo: aHostName port: aPort application: anApplication bufferSize: anInteger
self subclassResponsibility.! !
!AbstractClient methodsFor: 'setters' stamp: 'ah 1/7/2005 12:07'!
application: anApplication
application:=anApplication.! !
!AbstractClient methodsFor: 'setters' stamp: 'A 1/8/2005 15:49'!
buffer: aBuffer
buffer: aBuffer
buffer:= aBuffer.! !
!AbstractClient methodsFor: 'setters' stamp: 'AAT 1/4/2005 21:08'!
socket: aSocket
socket:=aSocket.! !
socket: aSocket
socket:=aSocket.! !
!AbstractClient methodsFor: 'talking' stamp: 'AH 1/13/2005 23:31'!
talk: aMessage
self subclassResponsibility.! !
!AbstractClient methodsFor: 'disconnecting' stamp: 'AH 1/13/2005 23:30'!
disconnect
self socket closeAndDestroy.! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
AbstractClient class
instanceVariableNames: ''!
instanceVariableNames: ''!
!AbstractClient class methodsFor: 'instance creation' stamp: 'AH 1/13/2005 23:28'!
connectTo: aHostName port: aPort application: anApplication
connectTo: aHostName port: aPort application: anApplication
self subclassResponsibility.! !
!AbstractClient class methodsFor: 'instance creation' stamp: 'AH 1/13/2005 23:27'!
connectTo: aHostName port: aPort application: anApplication bufferSize: anInteger
connectTo: aHostName port: aPort application: anApplication bufferSize: anInteger
self subclassResponsibility.! !
Object subclass: #ClientApplicationModel
instanceVariableNames: 'client'
classVariableNames: ''
poolDictionaries: ''
category: 'MetoPoo - Capa de red'!
!ClientApplicationModel methodsFor: 'getters' stamp: 'A 1/8/2005 17:50'!
client
client
^client.! !
!ClientApplicationModel methodsFor: 'initialize-release' stamp: 'AH 3/3/2005 11:06'!
initializeTCP: aHostName atPort: aPort
self client: (TCPClient connectTo: aHostName port: aPort application: self).
! !
!ClientApplicationModel methodsFor: 'initialize-release' stamp: 'AH 3/3/2005 11:08'!
initializeUDP: aHostName atPort: aPort
initializeUDP: aHostName atPort: aPort
self client: (UDPClient connectTo: aHostName port: aPort application: self).
! !
!ClientApplicationModel methodsFor: 'private' stamp: 'A 1/8/2005 18:00'!
cantConnectToPort: aPort
Transcript show: 'Jaja, no te pudiste conectar'! !
!ClientApplicationModel methodsFor: 'private' stamp: 'A 1/8/2005 17:58'!
cantSend: aMessage
cantSend: aMessage
"No se me ocurre que poner como comentario"
Transcript show: 'no se pudo enviar el mensaje:',aMessage.! !
!ClientApplicationModel methodsFor: 'private' stamp: 'AH 1/12/2005 14:55'!
serverNoResponse
serverNoResponse
Transcript show: 'El servidor no me respondio'.! !
!ClientApplicationModel methodsFor: 'private' stamp: 'A 1/8/2005 17:57'!
serverResponse: aBuffer count: aNumber
serverResponse: aBuffer count: aNumber
"Este mensaje es enviado por el cliente con lo servidor contesta luego del envio de un paquete"
"Version rata ya que no utiliza la longitud del buffer."
"Version rata ya que no utiliza la longitud del buffer."
Transcript show: 'El servidor me respondio:',aBuffer.! !
!ClientApplicationModel methodsFor: 'setters' stamp: 'A 1/8/2005 17:49'!
client: aClient
client:= aClient.! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ClientApplicationModel class
instanceVariableNames: ''!
instanceVariableNames: ''!
!ClientApplicationModel class methodsFor: 'instance creation' stamp: 'AH 3/3/2005 11:17'!
newTCP: aHostName atPort: aPort
newTCP: aHostName atPort: aPort
^ super new initializeTCP: aHostName atPort: aPort.! !
!ClientApplicationModel class methodsFor: 'instance creation' stamp: 'AH 3/3/2005 11:17'!
newUDP: aHostName atPort: aPort
newUDP: aHostName atPort: aPort
^ super new initializeUDP: aHostName atPort: aPort.! !
!ClientApplicationModel class methodsFor: 'instance creation' stamp: 'AH 3/3/2005 11:23'!
newUDPWithFirstServerAtPort: aPort
newUDPWithFirstServerAtPort: aPort
| server |
server:= ((self loopForServersAtPort: aPort) first) key.
^ self newUDP:server atPort: aPort.
! !
server:= ((self loopForServersAtPort: aPort) first) key.
^ self newUDP:server atPort: aPort.
! !
AbstractClient subclass: #TCPClient
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'MetoPoo - Capa de red'!
!TCPClient methodsFor: 'initialize-release' stamp: 'ah 2/26/2005 16:00'!
connectTo: aHostName port: aPort application: anApplication bufferSize: anInteger
connectTo: aHostName port: aPort application: anApplication bufferSize: anInteger
self application: anApplication.
self newSocketFor: aHostName to: aPort.
! !
!TCPClient methodsFor: 'initialize-release' stamp: 'AH 3/3/2005 15:24'!
newSocketFor: aHostName to: aPort
socket _ (Socket tcpCreateIfFail: [self application cantConnectToPort: aPort]).
socket connectTo: (NetNameResolver addressForName: aHostName) port: aPort.
socket waitForConnectionFor: (Socket standardDeadline).
! !
newSocketFor: aHostName to: aPort
socket _ (Socket tcpCreateIfFail: [self application cantConnectToPort: aPort]).
socket connectTo: (NetNameResolver addressForName: aHostName) port: aPort.
socket waitForConnectionFor: (Socket standardDeadline).
! !
!TCPClient methodsFor: 'talking' stamp: 'AH 3/7/2005 10:51'!
talk: aMessage
"Permite enviar un mensaje utilizando la coneccion TCP. Envia la respuesta del servidor a la aplicacion para que decida que hacer"
| objRec bytesRec |
objRec:= String new.
(socket isValid and: [socket
isConnected])
ifTrue: [socket sendData: aMessage.]
ifFalse: ["stale connection" socket
destroy.
socket := nil.
self application cantSend: aMessage].
bytesRec:= self socket receiveDataTimeout: 50 into: objRec.
(bytesRec = 0) ifFalse: [ self application serverResponse: objRec count: (objRec size)]
ifTrue: [ self application serverNoResponse]! !
isConnected])
ifTrue: [socket sendData: aMessage.]
ifFalse: ["stale connection" socket
destroy.
socket := nil.
self application cantSend: aMessage].
bytesRec:= self socket receiveDataTimeout: 50 into: objRec.
(bytesRec = 0) ifFalse: [ self application serverResponse: objRec count: (objRec size)]
ifTrue: [ self application serverNoResponse]! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
TCPClient class
instanceVariableNames: ''!
instanceVariableNames: ''!
!TCPClient class methodsFor: 'instance creation' stamp: 'AH 1/12/2005 15:00'!
connectTo: aHostName port: aPort application: anApplication
connectTo: aHostName port: aPort application: anApplication
^super new connectTo: aHostName port: aPort application: anApplication bufferSize: 5000.! !
!TCPClient class methodsFor: 'instance creation' stamp: 'AH 1/12/2005 14:59'!
connectTo: aHostName port: aPort application: anApplication bufferSize: anInteger
connectTo: aHostName port: aPort application: anApplication bufferSize: anInteger
^super new connectTo: aHostName port: aPort application: anApplication bufferSize: anInteger.! !
Object subclass: #TCPServer
instanceVariableNames: 'application buffer clients condition parameters port socket'
classVariableNames: ''
poolDictionaries: ''
category: 'MetoPoo - Capa de red'!
!TCPServer methodsFor: 'listen' stamp: 'A 3/7/2005 10:59'!
listen
| command iPAddress s |
socket _ Socket
tcpCreateIfFail: [^ Transcript show: 'Se rompio todo'].
socket listenOn: (self port).
[self evaluate] whileFalse:
[
socket waitForConnectionFor: (Socket deadlineSecs: 30) ifTimedOut: [^true.].
[socket isConnected]
whileTrue:
[ socket dataAvailable ifTrue:
[
command _ socket getData.
iPAddress := NetNameResolver stringFromAddress: socket remoteAddress.
s := String new.
s := iPAddress , Character tab asString , 'Envio ',command
printString.
Transcript show: s;cr.
(command='FIN') ifFalse:
[
socket sendData: 'OK'.
socket sendData: 'Hola, espero que llegue al menos la mitad de este texto'.
socket sendData: ('Y un cachito de este \' withCRs).
socket sendData: 'FIN'.
]
ifTrue:
[
socket sendData: 'Hasta la vista baby echecopar'.
socket disconnect
].
]
].
].
socket
destroy.! !
listen
| command iPAddress s |
socket _ Socket
tcpCreateIfFail: [^ Transcript show: 'Se rompio todo'].
socket listenOn: (self port).
[self evaluate] whileFalse:
[
socket waitForConnectionFor: (Socket deadlineSecs: 30) ifTimedOut: [^true.].
[socket isConnected]
whileTrue:
[ socket dataAvailable ifTrue:
[
command _ socket getData.
iPAddress := NetNameResolver stringFromAddress: socket remoteAddress.
s := String new.
s := iPAddress , Character tab asString , 'Envio ',command
printString.
Transcript show: s;cr.
(command='FIN') ifFalse:
[
socket sendData: 'OK'.
socket sendData: 'Hola, espero que llegue al menos la mitad de este texto'.
socket sendData: ('Y un cachito de este \' withCRs).
socket sendData: 'FIN'.
]
ifTrue:
[
socket sendData: 'Hasta la vista baby echecopar'.
socket disconnect
].
]
].
].
socket
destroy.! !
!TCPServer methodsFor: 'initialize-release' stamp: 'AAT 2/26/2005 15:57'!
initialize: aPort
"Guarda en la variable port el numero de puerto en el cual esta inicializado."
self port: aPort.
self buffer: (String new: 1024).
self clients: Dictionary new.
Socket initializeNetwork.
self socket: Socket newUDP.
self socket: ( self socket setPort: aPort).
self buffer: (String new: 4000).! !
!TCPServer methodsFor: 'setters' stamp: 'AAT 2/26/2005 15:58'!
application: anApp
application:=anApp.! !
!TCPServer methodsFor: 'setters' stamp: 'AAT 2/26/2005 15:58'!
buffer: aBuffer
buffer:=aBuffer.! !
buffer: aBuffer
buffer:=aBuffer.! !
!TCPServer methodsFor: 'setters' stamp: 'AAT 2/26/2005 15:59'!
clients: aDictionary
clients:=aDictionary.! !
clients: aDictionary
clients:=aDictionary.! !
!TCPServer methodsFor: 'setters' stamp: 'AAT 2/26/2005 15:59'!
condition: aConditionBlock
condition:= aConditionBlock.! !
condition: aConditionBlock
condition:= aConditionBlock.! !
!TCPServer methodsFor: 'setters' stamp: 'AAT 2/26/2005 16:00'!
parameters: anArray
parameters:=anArray.! !
parameters: anArray
parameters:=anArray.! !
!TCPServer methodsFor: 'setters' stamp: 'AAT 2/26/2005 16:00'!
port: aPort
port:=aPort.! !
port: aPort
port:=aPort.! !
!TCPServer methodsFor: 'setters' stamp: 'AAT 2/26/2005 16:01'!
socket: aSocket
socket:=aSocket.! !
socket: aSocket
socket:=aSocket.! !
!TCPServer methodsFor: 'private' stamp: 'AAT 2/26/2005 16:02'!
evaluate
^(self condition) valueWithArguments: self parameters.! !
!TCPServer methodsFor: 'getters' stamp: 'AAT 2/26/2005 16:05'!
application
^application! !
!TCPServer methodsFor: 'getters' stamp: 'AAT 2/26/2005 16:06'!
buffer
^buffer.! !
buffer
^buffer.! !
!TCPServer methodsFor: 'getters' stamp: 'AAT 2/26/2005 16:06'!
clients
^clients! !
clients
^clients! !
!TCPServer methodsFor: 'getters' stamp: 'AAT 2/26/2005 16:07'!
condition
^condition.! !
condition
^condition.! !
!TCPServer methodsFor: 'getters' stamp: 'AAT 2/26/2005 16:07'!
parameters
^parameters! !
parameters
^parameters! !
!TCPServer methodsFor: 'getters' stamp: 'AAT 2/26/2005 16:07'!
port
^port.! !
port
^port.! !
!TCPServer methodsFor: 'getters' stamp: 'AAT 2/26/2005 16:09'!
socket
^socket.! !
socket
^socket.! !
AbstractClient subclass: #UDPClient
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'MetoPoo - Capa de red'!
!UDPClient methodsFor: 'initialize-release' stamp: 'AH 1/13/2005 22:38'!
connectTo: aHostName port: aPort application: anApplication bufferSize: anInteger
connectTo: aHostName port: aPort application: anApplication bufferSize: anInteger
self application: anApplication.
self buffer: (String new: anInteger).
self socket: (Socket udpCreateIfFail: [self application cantConnectToPort: aPort]).
self socket setPeer: (NetNameResolver addressForName: aHostName) port: aPort.
self socket waitForConnectionFor: (Socket standardDeadline).! !
!UDPClient methodsFor: 'talking' stamp: 'AH 1/13/2005 22:31'!
talk: aMessage
"Permite enviar un mensaje utilizando la coneccion UDP. Envia la respuesta del servidor a la aplicacion para que decida que hacer"
| bytesRec |
(self socket isConnected)
ifTrue: [
(self socket) sendData: aMessage. "aca se podria poner un waitForSendDone"
bytesRec:= self socket receiveDataTimeout: 10 into: (self buffer).
(bytesRec = 0) ifFalse: [self application serverResponse: (self buffer) count: bytesRec]
ifTrue: [self application serverNoResponse]
]
ifFalse: [self application cantSend: aMessage]
! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
UDPClient class
instanceVariableNames: ''!
instanceVariableNames: ''!
!UDPClient class methodsFor: 'instance creation' stamp: 'AH 1/13/2005 22:39'!
connectTo: aHostName port: aPort application: anApplication
connectTo: aHostName port: aPort application: anApplication
^super new connectTo: aHostName port: aPort application: anApplication bufferSize: 5000.! !
!UDPClient class methodsFor: 'instance creation' stamp: 'AH 1/13/2005 22:39'!
connectTo: aHostName port: aPort application: anApplication bufferSize: anInteger
connectTo: aHostName port: aPort application: anApplication bufferSize: anInteger
^super new connectToPort: aPort application: anApplication bufferSize: anInteger.! !
!UDPClient class methodsFor: 'utilities' stamp: 'AH 1/13/2005 22:40'!
getBroadcastIp
|address|
address:=self getMyIp.
((address at: 1) >=192) ifTrue: [address at: 4 put: 255. ^address].
((address at: 1) >=128) ifTrue: [address at: 4 put: 255. address at: 3 put: 255. ^ address].
address at: 4 put: 255. address at: 3 put: 255. address at: 2 put: 255.
^address! !
address:=self getMyIp.
((address at: 1) >=192) ifTrue: [address at: 4 put: 255. ^address].
((address at: 1) >=128) ifTrue: [address at: 4 put: 255. address at: 3 put: 255. ^ address].
address at: 4 put: 255. address at: 3 put: 255. address at: 2 put: 255.
^address! !
!UDPClient class methodsFor: 'utilities' stamp: 'AH 1/13/2005 22:40'!
getMyIp
^ NetNameResolver primLocalAddress.! !
getMyIp
^ NetNameResolver primLocalAddress.! !
!UDPClient class methodsFor: 'utilities' stamp: 'AH 1/13/2005 22:41'!
loopForServersAtPort: aPort
loopForServersAtPort: aPort
"busca durante 10 segundos los servidores disponibles y los retorna en un diccionario servers. Esto permite al usuario decidir con que servidor conectarse y de que manera hacerlo"
^ (self loopForServersAtPort: aPort ifFail:[]).! !
^ (self loopForServersAtPort: aPort ifFail:[]).! !
!UDPClient class methodsFor: 'utilities' stamp: 'ah 2/26/2005 15:13'!
loopForServersAtPort: aPort ifFail: failBlock
loopForServersAtPort: aPort ifFail: failBlock
"busca durante 10 segundos los servidores disponibles y los retorna en un diccionario servers. Esto permite al usuario decidir con que servidor conectarse y de que manera hacerlo"
| inicio servers udpSocket bytesRec buffer|
| inicio servers udpSocket bytesRec buffer|
buffer:= String new: 5000.
udpSocket:= (Socket udpCreateIfFail: failBlock).
udpSocket setPeer: (self getBroadcastIp) port: aPort.
inicio:= (DateAndTime now) asSeconds.
servers:= OrderedCollection new.
udpSocket setPeer: (self getBroadcastIp) port: aPort.
inicio:= (DateAndTime now) asSeconds.
servers:= OrderedCollection new.
[(inicio + 10) >= (DateAndTime now) asSeconds] whileTrue:
[udpSocket sendData: 'xxx'.
bytesRec:= udpSocket receiveDataTimeout: 10 into: buffer.
(bytesRec = 0) ifFalse: [servers add: (Association key: (udpSocket peerName) value: (udpSocket remoteAddress) ) ] ].
udpSocket closeAndDestroy.
^ servers.! !
[udpSocket sendData: 'xxx'.
bytesRec:= udpSocket receiveDataTimeout: 10 into: buffer.
(bytesRec = 0) ifFalse: [servers add: (Association key: (udpSocket peerName) value: (udpSocket remoteAddress) ) ] ].
udpSocket closeAndDestroy.
^ servers.! !
Object subclass: #UDPServer
instanceVariableNames: 'port buffer clients socket condition parameters application'
classVariableNames: ''
poolDictionaries: ''
category: 'MetoPoo - Capa de red'!
!UDPServer methodsFor: 'setters' stamp: 'AAT 1/5/2005 01:42'!
application: anApp
application:=anApp.! !
application: anApp
application:=anApp.! !
!UDPServer methodsFor: 'setters' stamp: 'AAT 1/5/2005 01:43'!
buffer: aBuffer
buffer:=aBuffer.! !
buffer: aBuffer
buffer:=aBuffer.! !
!UDPServer methodsFor: 'setters' stamp: 'AAT 1/5/2005 01:44'!
clients: aDictionary
clients:=aDictionary.! !
clients: aDictionary
clients:=aDictionary.! !
!UDPServer methodsFor: 'setters' stamp: 'AAT 1/5/2005 01:43'!
condition: aConditionBlock
condition:= aConditionBlock.! !
condition: aConditionBlock
condition:= aConditionBlock.! !
!UDPServer methodsFor: 'setters' stamp: 'AAT 1/5/2005 01:46'!
parameters: anArray
parameters:=anArray.! !
parameters: anArray
parameters:=anArray.! !
!UDPServer methodsFor: 'setters' stamp: 'AAT 1/5/2005 03:21'!
port: aPort
port:=aPort.! !
port: aPort
port:=aPort.! !
!UDPServer methodsFor: 'setters' stamp: 'AAT 1/5/2005 02:08'!
socket: aSocket
socket:=aSocket.! !
socket: aSocket
socket:=aSocket.! !
!UDPServer methodsFor: 'initialize-release' stamp: 'AAT 1/8/2005 02:57'!
initialize: aPort
"Guarda en la variable port el numero de puerto en el cual esta inicializado."
self port: aPort.
self buffer: (String new: 1024).
self clients: Dictionary new.
Socket initializeNetwork.
self socket: Socket newUDP.
self socket: ( self socket setPort: aPort).
self buffer: (String new: 4000).! !
!UDPServer methodsFor: 'listen' stamp: 'AAT 2/26/2005 15:13'!
listen
"Obtiene los mensajes enviados por los clientes y los envia luego a la aplicacion, tambien envia el tama–o de los mismos y la direccion del cliente. En caso que la aplicacion decida que corresponde responder enviara los mensajes a los clientes por medio del mensaje broadCast:with:to:"
|n address|
[self evaluate] whileFalse: [
((self socket) dataAvailable) ifTrue:
[n := (self socket) receiveDataInto: buffer.
address:=(self socket) address.
"self application processBuffer: (address deepCopy) withMessage: (buffer deepCopy) and: (n deepCopy). "
"(self socket) address: address."
(self socket) sendData: buffer count: n.
]].! !
!UDPServer methodsFor: 'private' stamp: 'AAT 1/5/2005 02:27'!
evaluate
^(self condition) valueWithArguments: self parameters.! !
!UDPServer methodsFor: 'getters' stamp: 'AAT 1/5/2005 02:03'!
application
^application! !
!UDPServer methodsFor: 'getters' stamp: 'AAT 1/5/2005 01:53'!
buffer
^buffer.! !
buffer
^buffer.! !
!UDPServer methodsFor: 'getters' stamp: 'AAT 1/5/2005 01:58'!
clients
^clients! !
clients
^clients! !
!UDPServer methodsFor: 'getters' stamp: 'AAT 1/5/2005 01:48'!
condition
^condition.! !
condition
^condition.! !
!UDPServer methodsFor: 'getters' stamp: 'AAT 1/5/2005 01:48'!
parameters
^parameters! !
parameters
^parameters! !
!UDPServer methodsFor: 'getters' stamp: 'AAT 1/5/2005 01:47'!
port
^port.! !
port
^port.! !
!UDPServer methodsFor: 'getters' stamp: 'AAT 1/5/2005 01:49'!
socket
^socket.! !
socket
^socket.! !
!UDPServer methodsFor: 'routing' stamp: 'AAT 1/8/2005 04:14'!
broadcast: aMessage with: aSize to: aClientCollection
(aClientCollection values) do: [ :clientAddress|
(self socket) address: clientAddress.
(self socket) sendData: aMessage count: aSize.
Transcript show: aMessage.].! !
(self socket) address: clientAddress.
(self socket) sendData: aMessage count: aSize.
Transcript show: aMessage.].! !
250MB gratis, Antivirus y Antispam
Correo Yahoo!, el mejor correo web del mundo
Abrí tu cuenta aquí