Está en la página 1de 19

Universidad de Chile

Facultad de Ciencias Físicas y Matemáticas


Departamento de Ingeniería Eléctrica

Proyecto 1.
Implementación Sockets.

Integrantes: Constanza Bustamante


Baoyi Guo
Diego Pincheira
Profesor: Cesár Azurdia.
Auxiliares: Hojin Kang.
Nicolás López.
Pablo Palacios.
Javier Rojas.
Ayudante: Diego Corvalán.
Fecha de entrega: 22 de Mayo de 2020
Santiago, Chile
Índice de Contenidos i

Índice de Contenidos

1. Introducción 1

2. Marco Teórico 1
2.1. ¿Qué es un socket? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2.2. ¿Cómo funciona un socket? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

3. Descripción del servidor 2

4. Descripción del Cliente 9

5. Discusión 14

6. Conclusiones 14

Referencias 16

Índice de Figuras
1. Ilustración concepto de socket . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Índice de Códigos
1. Server inicialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2. Bucle que mantiene el Server activo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
3. Función msg_to_all . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
4. Función que acepta a los clientes aceptarCon . . . . . . . . . . . . . . . . . . . . . . . 4
5. Función que procesa los mensajes de los clientes . . . . . . . . . . . . . . . . . . . . . . 4
6. Función procesarCon chequeo de nicknames. . . . . . . . . . . . . . . . . . . . . . . . . 5
7. procesarCon adición de oro. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
8. procesarCon salida de un cliente. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
9. procesarCon cantidad de oro y lista de usuarios conectados. . . . . . . . . . . . . . . . 6
10. procesarCon mensajes privados. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
11. procesarCon cantidad de item asignado. . . . . . . . . . . . . . . . . . . . . . . . . . . 6
12. Bolsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
13. Offer 1era parte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
14. Offer 2da parte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
15. Inicialización del cliente. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
16. Ingreso de nicknames. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
17. Ingreso de cantidad de oro inicial. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
18. Recepción de mensajes de otros clientes. . . . . . . . . . . . . . . . . . . . . . . . . . . 10
19. Bucle de input. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
20. Presentación de los comandos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
21. Emojis. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

Proyecto 1. EL-4005 Principios de comunicaciones


Índice de Códigos ii

22. Spawn. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
23. Offer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
24. Accept y reject. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
25. Bolsa. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
26. Recepción de mensajes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
27. Envío de mensajes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

Proyecto 1. EL-4005 Principios de comunicaciones


Marco Teórico 1

1. Introducción
En el área de las telecomunicaciones, el término socket se refiere a un concepto abstracto a
partir del cual dos procesos pueden comunicarse e intercambiar información. Las propiedades de
un socket dependen de las características del protocolo en el que se implementan. Los protocolos
más comunes de implementación son TCP/IP y UDP.

Probablemente, la aplicación más conocida de los sockets TCP/IP es el chat. En esta tarea se
tiene por objetivo crear el chat de un juego del género MMORPG (Massively multiplayer online
role-playing game), en el cual los usuarios además de hablar, pueden intercambiar objetos entre
sí. El chat anteriormente mencionado, fue realizado en la plataforma Python, particularmente
utilizando Anaconda en la interfaz Spyder. En el presente informe se describe su implementación y
desarrollo.

2. Marco Teórico
2.1. ¿Qué es un socket?
Las computadoras actuales cuentan con componentes y sistemas complejos, por esta razón, están
equipadas con una capa de software llamada sistema operativo. El fin de este sistema es propor-
cionar a los usuarios un modelo de computador más sencillo, y encargarse de la administración de
todos los recursos que lo componen.

Un concepto clave en todos los sistemas operativos es el proceso. Un proceso es en esencia un


programa de ejecución, que se caracteriza por la ejecución de una secuencia, un estado actual, y un
conjunto de recursos del sistema asociados.

En línea de lo anterior un socket es un punto de comunicación por el cual un proceso puede


emitir o recibir información, como puede observarse en la Figura 1. Los procesos los tratan como
descriptores de archivos, de forma que se pueden intercambiar datos con otros procesos transmi-
tiendo y recibiendo a través de sockets.

Figura 1: Ilustración concepto de socket

Los socket se pueden crear y distribuir en forma dinámica. Al crear un socket se devuelve un
descriptor de archivo, el cual se requiere para establecer una conexión, leer datos, escribir datos y
liberar la conexión.

Proyecto 1. EL-4005 Principios de comunicaciones


Descripción del servidor 2

De acuerdo a las propiedades de comunicación entre ambos puntos; fiabilidad de la transmisión,


conservación del orden de los datos, conservación de los límites de los mensajes, etc., se distinguen
los siguientes tipos de socket:

Stream: los sockets de este tipo permiten comunicaciones fiables, estableciendo un circuito
virtual que une los dos puntos. Una vez establecida la conexión, se puede proceder al envío
secuencial de los datos. Este tipo de socket utiliza el protocolo TCP.

Dgram: Corresponde a los sockets destinados a la comunicación en modo no conectado para


el envío de datagramas de tamaño limitado. No se garantiza una recepción secuencial de los
datos, ya que la transmisión de los datagramas es a nivel de paquetes. Este tipo de socket
utiliza el protocolo UDP.

Raw: Permite el acceso a los protocolos de más bajo nivel (por ejemplo, el protocolo IP en el
dominio Internet).

Seqpacket: Corresponde a las comunicaciones que se encuentran en el dominio XNS.

En la implementación, se utilizó el tipo de socket stream, cuyas características fueron descritas


anteriormente.

2.2. ¿Cómo funciona un socket?


Un Socket queda definido por un par de direcciones IP local y remota, un protocolo de transporte
y un par de números de puerto local y remoto. Antes de poder usar un socket para trabajar en la
red, este debe enlazarse con una dirección. Dicha dirección puede estar en uno o varios dominios
de nombramiento. El nombramiento más común es Internet.

Una vez que se crea un socket en los computadores de origen y de destino, se puede establecer
una conexión entre ellos. Una de las partes realiza una llamada al sistema listen en un socket local,
con lo cual se crea un buffer y se bloquea hasta que lleguen los datos. La otra parte realiza una
llamada al sistema connect y proporciona como parámetros el descriptor de archivo para un socket
local y la dirección de un socket remoto. Si la parte remota acepta la llamada, entonces el sistema
establece una conexión entre los sockets. Cuando el proceso ya no requiere la conexión, esta se
puede cerrar a través de la llamada al sistem close.

3. Descripción del servidor


Para esta tarea, se utilizará como base el código provisto en [1], realizándole las modificaciones
necesarias para llevar a cabo los objetivos requeridos para esta actividad. Una vez realizadas, se
obtiene una clase y 5 funciones principales contenidas dentro de esta, las cuales realizan las funciones
que requiere el servidor para operar de la forma solicitada. Estas requieren las siguientes librerías,
las cuales se importan al inicio del código: socket, threading, sys, pickle, y re.

La primera función se puede observar en el código 1. Su objetivo es inicializar el socket y


levantar los hilos que mantienen la conexión con los clientes. Define el número de puerto, el host,
el dominio y el tipo de socket. Además, crea las listas y diccionarios que guardan la información

Proyecto 1. EL-4005 Principios de comunicaciones


Descripción del servidor 3

requerida de cada usuario y un listen que permite hasta 6 usuarios conectados. Se hace uso del
método sock.setblocking en la línea 13, ya que permite que el servidor no se quede anclado a un
solo cliente, dejando en espera a los demás.

Es importante destacar que se levantan dos hilos o threading, uno con el fin de aceptar y
mantener la conexión de los clientes, y otro con el objetivo de procesar los mensajes. Se observan
el las líneas 17 y 18 del Código 1.

Código 1: Server inicialización


1 class Servidor() :
2 def __init__(self, host="localhost", port=3000):
3

4 self . clientes = []
5 self . nicks = [] # lista de nombres de de clientes
6 self .Nickname_Dic = {}
7 self .Oro_Dic = {}
8 self .items = {} # diccionario de items
9 self .sock = socket.socket(socket .AF_INET, socket.SOCK_STREAM)
10 #self.sock.bind((str (host) , int (port)))
11 self .sock.bind((host, port))
12 self .sock. listen (6)
13 self .sock.setblocking(False)
14
15 #Dos hilos, uno para aceptar y otro para procesar
16
17 aceptar= threading.Thread(target=self.aceptarCon)
18 procesar = threading.Thread(target=self.procesarCon)
19

20 aceptar.daemon = True
21 aceptar. start ()
22

23 procesar.daemon = True
24 procesar. start ()

El bucle que permite que el Server se mantenga activo constantemente se puede observar el
Código 2. Mientras no se le envíe un mensaje de salida (q) al servidor, la conexión se mantiene
constante. Este bucle también es parte de la primera función.

Código 2: Bucle que mantiene el Server activo


1
2 while True:
3 msg = input(’->’)
4 if msg == ’q’:
5 self . socket . close ()
6 sys . exit ()
7 else :
8 pass

La segunda y tercera función están implementadas con el fin de enviar mensajes. La segunda
función, msg_to_all permite enviar mensajes a todos los clientes, removiendo clientes de la lista

Proyecto 1. EL-4005 Principios de comunicaciones


Descripción del servidor 4

self.clientes, en caso de que detecte que uno de ellos ya no está conectado. La tercera función
msg_to_one permite enviar mensajes a un cliente en particular.

Los mensajes se envían con el comando send y los clientes son removidos utilizando el comando
remove, como se puede observar en el Código 3.

Código 3: Función msg_to_all


1 #Enviar el mensaje a todos los demás clientes conectados.
2 def msg_to_all(self, msg, cliente ) :
3 for c in self . clientes :
4 try :
5 if c != cliente :
6 c.send(msg)
7 except:
8 self . clientes .remove(c) #removemos el cliente

La cuarta función, aceptarCon acepta a los clientes que se conectan al servidor y muestra un
aviso de conexión en la consola del servidor. También agrega el nuevo cliente a la lista self.clientes.
Consta de un bucle infinito que está permanentemente entregando una dirección y aceptando a los
nuevos usuarios que se conectan.

Código 4: Función que acepta a los clientes aceptarCon


1

2 def aceptarCon(self) :
3 print("aceptarCon iniciado")
4 while True:
5 try :
6 conn, addre = self .sock.accept()
7 conn.setblocking(False)
8 aviso = ’[SERVER] Client: ’ + str(addre) + ’ connected!’
9 print(aviso)
10 self . clientes .append(conn)
11 except:
12 pass

Por último, se define la función para procesar las conexiones, esta contiene un while infinito que
recorre la lista de clientes para saber cuando se recibe un mensaje. Es la función dentro del código
que realiza la mayor cantidad de operaciones, puesto que interactúa directamente con los clientes.
Realiza las ofertas, el intercambio de items, la recepción de nicknames, etc.

Código 5: Función que procesa los mensajes de los clientes


1
2 def procesarCon(self) :
3 print("ProcesarCon iniciado")
4 while True:
5 if len( self . clientes ) > 0:
6 for c in self . clientes :
7 try :
8 data = c.recv(1024)

Proyecto 1. EL-4005 Principios de comunicaciones


Descripción del servidor 5

9 if data:

Si existe mensaje enviado desde algún cliente, es decir, si se cumple que data existe, se realizan
los siguientes if.

Nickname: en la interfaz del cliente se pide insertar el nombre que el usuario desea tener
dentro del chat. Una vez ingresado, se envía al servidor para procesarlo. Si el nombre está en
uso se reenvía un mensaje al cliente para que ingrese un nombre distinto y si no, se guarda el
nombre en el diccionario de nicknames self.Nicknames_Dic.

Como se observa en el Código 6, la implementación utiliza marcadores para realizar los


chequeos. Si el marcador Nickname_Assignator está presente en el mensaje del cliente,
significa que ha enviado un nickname y, por lo tanto, este debe ser procesado rechazan-
do o agregando al server. Los mensajes del cliente se desencriptan usando la librería pic-
kle, con el comando pickle.loads(data), luego, para responder al cliente se encripta usando
c.send(bytes(en_uso,0 utf − 80 )).

Código 6: Función procesarCon chequeo de nicknames.


1 if ’Nickname_Assignator’ in pickle.loads(data):
2 data_deco = pickle.loads(data)
3 Real_NN = data_deco[19:]
4 if Real_NN in self.Nickname_Dic.values():
5 en_uso = ’Usuario_Rechazado: \’’ + Real_NN + ’\’ ya está en uso’
6 c.send(bytes(en_uso, ’utf-8 ’ ))
7 else :
8 self .Nickname_Dic[c] = Real_NN
9 nombre_acept= ’Usuario_Aceptado’
10 c.send(bytes(nombre_acept, ’utf-8’))
11 self .msg_to_all(bytes(Real_NN + ’ se ha conectado al servidor’, ’utf-8’) ,c)
12 saludo = "Welcome to the best chat in the universe! "
13 saludo_encrip = bytes(saludo, ’utf-8 ’ )
14 c.send(saludo_encrip)

Cantidad de oro: una vez que el cliente ingresa su cantidad de oro disponible en la interfaz
de cliente, esta se envía al servidor para ser almacenada en el diccionario correspondiente,
self.Oro_Dic y envíe un mensaje con la cantidad de oro asignado al cliente en particular.
Utiliza el marcador Initial_Gold_Assignator, como se puede observar en el Código 7.

Código 7: procesarCon adición de oro.


1

2 elif ’Initial_Gold_Assignator’ in pickle . loads(data):


3 data_deco = pickle.loads(data)
4 Real_NN = data_deco[23:]
5 self .Oro_Dic[c] = Real_NN# Guardar en un diccionario:
6 print( self .Nickname_Dic[c] + ’ ha asignado ’ + self.Oro_Dic[c] + ’G a su cuenta.’)

Salida: si el mensaje del cliente es 0 q 0 , quiere decir que desea salir del chat y el servidor corta
la conexión removiendo su cantidad de oro, nickname y de la lista de clientes.

Proyecto 1. EL-4005 Principios de comunicaciones


Descripción del servidor 6

Código 8: procesarCon salida de un cliente.


1 elif pickle . loads(data) == ’q’:
2 self .msg_to_all(bytes(’[SERVER] Client: ’+ ’<’ + self.Nickname_Dic[c] + ’>’ + ’ se ha
,→ desconectado! :c’ , ’ utf-8 ’ ) ,c)
3 del self .Nickname_Dic[c]
4 del self .Oro_Dic[c]
5 del self .items[c]
6 self . clientes .remove(c)

Consulta usuario: si el usuario desea saber la cantidad de oro que tiene disponible o los
usuarios conectados, puede enviar el mensaje gold o u al servidor respectivamente. En ese
caso el servidor le envía su cantidad de oro almacenada hasta el momento o la lista de
usuarios conectados.

Código 9: procesarCon cantidad de oro y lista de usuarios conectados.


1 elif pickle . loads(data) == ’gold’:
2 oro = self .Oro_Dic[c]
3 oro_encrip = bytes(oro, ’utf-8 ’ )
4 c.send(oro_encrip)
5

6 elif pickle . loads(data) == ’u’:


7 Lista_Usuarios = set(self .Nickname_Dic.values())
8 Mensaje_Cantidad_Usuarios = ’Hay ’ + str(len(Lista_Usuarios)) + ’ usuarios conectados:’
,→ + str(Lista_Usuarios)
9 c.send(bytes(Mensaje_Cantidad_Usuarios,’utf-8’))

Mensajes privados: si uno de los usuarios desea comunicarse de forma privada con otro, puede
enviar el mensaje p - <Usuario_Objetivo> - Mensaje, el cual es procesado por el servidor.
Utiliza el comando re.search el cual busca si existe un string dentro de otro string para enviar
el mensaje al usuario objetivo.

Código 10: procesarCon mensajes privados.


1
2 elif ( ’ - <’ in pickle . loads(data)) and (’> - ’ in pickle . loads(data)) and ’p’ == pickle.loads
,→ (data) [0]:
3 private_deco = pickle.loads(data)
4 Jugador_Msg_Pre = re.search(’p - <(.+?)> -’, private_deco)
5 Jugador_Msg = Jugador_Msg_Pre.group(1)
6 Cota = len(Jugador_Msg) + 8
7 Mensaje = private_deco[Cota:]
8 Mensaje_Privado = ’[PRIVADO]<’ + Jugador_Msg + ’>: ’ + Mensaje
9 print(Mensaje_Privado)
10 self .msg_to_one(bytes(Mensaje_Privado,’utf-8’), c, Jugador_Msg)

Spawn: genera la cantidad del objeto asignado por el cliente. Esto a partir del uso de un
diccionario, cuya llave es el objeto y la cantidad el valor. También utiliza marcadores para
verificar el mensaje del cliente.

Proyecto 1. EL-4005 Principios de comunicaciones


Descripción del servidor 7

Código 11: procesarCon cantidad de item asignado.


1 # Spawn
2 #### ’Item_Assignator’ declara el item que se desea agregar a la bolsa
3 elif ’Item_Assignator’ in pickle . loads(data):
4 dato_item = pickle.loads(data)
5 item_plus = dato_item[15:]
6 self .items[c ][ item_plus] = 0
7
8 #### ’Amount_Assignator’ declara la cantidad del item anterior a agregar a la bolsa
9 elif ’Amount_Assignator’ in pickle.loads(data):
10 dato_amount = pickle.loads(data)
11 amount_plus = dato_amount[17:]
12 self .items[c ][ item_plus] = amount_plus
13 c.send(bytes(’Objetos agregados!’, ’ utf-8 ’ ))

Bolsa_Items: Este comando entrega la lista de items que posee el jugador que envía el co-
mando ’Bolsa’:

Código 12: Bolsa


1 elif ’Bolsa_Items’ in pickle . loads(data):
2 Lista_de_Items = self.items[c]
3 Msj_items = ’Tus items en la bolsa son los siguientes : ’ + str(Lista_de_Items)
4 c.send(bytes(Msj_items,’utf-8’))

Offer: este comando consiste en 2 partes. Primero el cliente establece una oferta especificando
el item, cantidad y precio y la dirige a otro cliente, con previa revisión de los detalles de
la oferta (la conexión de la persona a quien ofrece en self.Nickname_Dic y la cantidad
suficiente de los items a intercambiar en self.items). Segundo al enviar la propuesta de
oferta existosamente al cliente correspondiente, este puede aceptar o rechazar la propuesta,
para el primer caso, se debe disponer de cantidad suficiente de oro para el intercambio de oro
e items en self.Oro_Dic y su luego entrega de items al comprador por parte del vendedor
a través de self.items, en el último caso, se envia un mensaje de oferta rechazada al cliente
que ofrece items por oro. Estos comandos también se realizan con la ayuda de marcadores.

Código 13: Offer 1era parte


1 # ---------------- Offer : 1era parte --------------
2 #### ’ Comprador_Assignator’ verifica si existe el comprador
3 elif ’Comprador_Assignator’ in pickle.loads(data):
4 dato_comprador = pickle.loads(data)[20:]
5 print( ’Buscando el comprador..’)
6 if dato_comprador in self.Nickname_Dic:
7 print( ’Comprador encontrado!’)
8 pass
9 else :
10 c.send(bytes(’Comprador no está en línea. :c’ ))
11 print( ’Comprador no existe..’)
12

13 #### ’Cantidad_Assignator’ determina la cantidad del item a intercambiar


14 elif ’Cantidad_Assignator’ in pickle.loads(data):

Proyecto 1. EL-4005 Principios de comunicaciones


Descripción del servidor 8

15 dato_cantidad = int(pickle.loads(data) [19:])


16 print( ’Confirmando stock..’)
17
18 #### ’Objeto_Assignator’ determina el item a intercambiar
19 elif ’Objeto_Assignator’ in pickle. loads(data):
20 dato_objeto = pickle.loads(data) [17:]
21 print( ’Buscando el item en la bolsa .. ’ )
22 if dato_objeto in self .items[c ]. keys() :
23 cantidad_exist = self .items[c ]. get( str (dato_objeto))
24 print( ’Item existente en el diccionario ! ’ )
25 if cantidad_exist >= dato_cantidad:
26 print( ’Stock suficiente ! ’ )
27 else :
28 stock_ins = ’Stock insuficiente , tienes disponible ’ + str(cantidad_exist) + ’
,→ unidades en la bolsa para vender!’
29 c.send(bytes(stock_ins,’ utf-8 ’ ))
30 print( ’Stock insuficiente ! ’ )
31 else :
32 c.send(bytes(’Item no encontrado en la bolsa :c intente con otro! ’ ))
33 print( ’Item no existe en la bolsa del usuario. uwu’)
34

35 #### ’ Monto_Assignator’ determina el dinero propuesto para la transacción


36 elif ’Monto_Assignator’ in pickle.loads(data):
37 dato_monto = int(pickle.loads(data)[16:])
38 print( ’Monto solicitado para la transaccion :D’)
39 msg_a_comprador = ’Propuesta_Oferta[PRIVADO] <’ + self.Nickname_Dic[c] + ’> Te
,→ ofrece ’ + str(dato_cantidad) + ’ unidades de ’ + str(dato_objeto) + ’ por ’ + str(
,→ dato_monto) + ’ de oro! :D (accept/reject)’
40 self .msg_to_one(bytes(msg_a_comprador,’utf-8’), c, dato_comprador)
41 print( ’Aviso de oferta enviado al comprador!’)

Código 14: Offer 2da parte


1 # ---------------- Offer : 2da parte ----------------
2 #### ’Acepto_Oferta’ indica que el jugador ha aceptado la oferta
3 elif ’Acepto_Oferta’ in pickle. loads(data):
4 aceptar = pickle. loads(data) [13:]
5 vendedor_nick = re.search(’<(.+?)>’, aceptar).group(1)
6 vendedor = list( self .Nickname_Dic.keys())[list( self .Nickname_Dic.values()).index(
,→ vendedor_nick)]
7 aceptar_split = re. split (r ’\s’ , aceptar)
8 dato_objeto = aceptar_split[7]
9 dato_cantidad = int(aceptar_split[4])
10 dato_monto = int(aceptar_split[9])
11 oro_comprador = int(self.Oro_Dic[c])
12 print( ’ Verificando saldo para la compra..’)
13 if dato_monto <= oro_comprador:
14 # El comprador paga al vendedor
15 self .Oro_Dic[c] = str(oro_comprador - dato_monto)
16 oro_vendedor = int(self.Oro_Dic[vendedor])
17 self .Oro_Dic[vendedor] = str(oro_vendedor + dato_monto)
18 print( ’Se pagó la oferta ! ’ )

Proyecto 1. EL-4005 Principios de comunicaciones


Descripción del Cliente 9

19 # El vendedor le entrega los items al comprador


20 if dato_objeto in self .items[c ]. keys() :
21 cantidad_objeto_compr = int(self.items[c][dato_objeto])
22 self .items[c ][ dato_objeto] = cantidad_objeto_compr + dato_cantidad
23 cant_objeto_vend = int(self.items[vendedor][dato_objeto])
24 self .items[vendedor][dato_objeto] = cant_objeto_vend - dato_cantidad
25 c.send(bytes(’Compra exitosa! :D’,’ utf-8 ’ ))
26 vendedor.send(bytes(’Oferta aceptada! :D’, ’ utf-8 ’ ))
27 print( ’Compraventa realizada!’)
28 else :
29 self .items[c ][ dato_objeto] = dato_cantidad
30 cant_objeto_vend = int(self.items[vendedor][dato_objeto])
31 self .items[vendedor][dato_objeto] = cant_objeto_vend - dato_cantidad
32 print( ’Items traspasados a la bolsa del comprador :D’)
33 c.send(bytes(’Compra exitosa! :D’,’ utf-8 ’ ))
34 vendedor.send(bytes(’Oferta aceptada! :D’, ’ utf-8 ’ ))
35 print( ’Compraventa realizada!’)
36 else :
37 c.send(bytes(’Saldo insuficiente :c’ , ’ utf-8 ’ ))
38 vendedor.send(bytes(’No se pudo completar la transacción. uwu’,’utf-8 ’ ))
39 #### Se rechaza la oferta
40 elif pickle . loads(data) == ’reject’ :
41 c.send(bytes(’Oferta rechazada. uwu’,’utf-8 ’ ))
42 print( ’Oferta rechazada por el comprador. :o’)

Por último, si mensaje recibido al servidor no cumple ninguno de los requisitos para entrar
a alguno de los apartados anteriores, se interpreta como un mensaje que se le debe enviar al
resto de usuarios conectados:
1 else :
2 self .msg_to_all(bytes(’<’ + self .Nickname_Dic[c] + ’>’ + ’: ’ + pickle. loads(data), ’ utf-8
,→ ’ ) ,c)

4. Descripción del Cliente


El archivo del cliente compone de 3 funciones principales que se encargan de la conexión al
servidor, el envío y la recepción de mensajes. Al ejecutar el archivo se abre un thread desde el
servidor para luego permitir la conexión con threading.local(). Además, se utilizaron 3 marcadores
principales para facilitar los procesos de intercambio: VERIFY_FLAG, ACCEPT_FLAG y
REJECT_FLAG.

Código 15: Inicialización del cliente.


1 global ACCEPT_FLAG
2 global REJECT_FLAG
3 self .sock = socket.socket(socket .AF_INET, socket.SOCK_STREAM)
4 self .sock.connect((str(host) , int (port)))
5 self .Emojis_Dict = {"smile": ":-)" , "angry": " >:( " , "confused": " :S", "awa": "uwu", "
,→ excited": " :D", "sleepy" : "-_- zzz", "hungry": "QAQ", "peron": ":)))" }

Proyecto 1. EL-4005 Principios de comunicaciones


Descripción del Cliente 10

Se verifica si el nickname ingresado por el cliente está en uso, se volverá a pedir hasta que
el cliente ingrese un nickname válido para luego enviarlo al servidor y ser guardado con la
información de su socket:

Código 16: Ingreso de nicknames.


1 # Se ingresan los nicknames, y se verifica que no se esten usando actualmente
2 Nickname_input = input(’Inserte Nickname:’)
3 Nickname_the_real = ’Nickname_Assignator’ + Nickname_input
4 self .send_msg(Nickname_the_real)
5 while True:
6 data = self .sock.recv(1024)
7 if data:
8 if ’Usuario_Rechazado’ in data.decode():
9 print(data.decode())
10 Nickname_input = input(’Inserte Nickname:’)
11 Nickname_the_real = ’Nickname_Assignator’ + Nickname_input
12 self .send_msg(Nickname_the_real)
13 elif data.decode() == ’Usuario_Aceptado’:
14 print( ’Usuario aceptado!’)
15 break

El cliente puede crear una cantidad de oro para iniciar el chat, se comprueba que éste sea un
número entero y positivo:

Código 17: Ingreso de cantidad de oro inicial.


1 # Se ingresa el oro inicial
2 while True:
3 Gold_input = input(’Inserte Oro inicial : ’ )
4 try :
5 Gold_input = int(Gold_input)
6 if Gold_input < 0:
7 print( ’Cantidad de oro debe ser mayor o igual a 0. Ingrese nuevamente.’)
8 else :
9 Gold_input= str(Gold_input)
10 break
11 except:
12 print( ’Cantidad de oro incorrecta. Intente nuevamente.’)
13 Gold_the_real = ’Initial_Gold_Assignator’ + Gold_input
14 self .send_msg(Gold_the_real)
15 thread_local.VERIFY_FLAG = False

Se abre el proceso de recepción de mensajes de otros clientes y sin interferir con los mensajes
enviados.

Código 18: Recepción de mensajes de otros clientes.


1 # Se permite recibir mensajes del resto de las personas:
2 msg_recv = threading.Thread(target=self.msg_recv)
3 msg_recv.daemon = True
4 msg_recv.start()

Proyecto 1. EL-4005 Principios de comunicaciones


Descripción del Cliente 11

Se abre un bucle para su constante envío de solicitud al servidor, con una secuencia de if,
excepto cuando el cliente ingresa ’q’, comando para salir del chat con self.sock.close():

Código 19: Bucle de input.


1 # Comandos del chat:
2 print( ’Hola! Escribe help para ver los comandos disponibles!’)
3 while True:
4 self .msg = input(’Me: ’)
5 if self .msg != ’q’:
6 ## Se envian emojis
7 .
8 .
9 .
10 # Se sale del chat
11 else :
12 self .send_msg(self.msg)
13 print( ’You have been disconnected from the best chat in the universe! ’ )
14 self .sock. close ()
15 sys . exit ()

El archivo cliente también dispone de un comando de ayuda, que al ingresar se presentan una
lista de comandos que puede utilizar el cliente aparte de chatear:

Código 20: Presentación de los comandos.


1 ## Se ingresa la ayuda
2 elif self .msg == ’help’:
3 print( ’Hola!’ )
4 print( ’Los comandos para el chat son los siguientes : ’ )
5 print( ’ q: Te desconectas del chat’)
6 print( ’ p - <Usuario_Objetivo> - Mensaje: le envías a
,→ Usuario_Objetivo el contenido de Mensaje’)
7 print( ’ ListaEmojis: Despliegas la lista de emojis disponibles ’ )
8 print( ’ Bolsa: Despliega la lista de items que tienes actualmente’)
9 print( ’ spawn : Se despliega un menú donde es posible generar items:’)
10 print( ’ Item a agregar: Se coloca el item que te gustaría tener
,→ ’)
11 print( ’ Cantidad del item: Se coloca la cantidad del item’)
12 print( ’ offer : Se despliega un menú donde se le puede ofrecer un
,→ intercambio a los usuarios : ’ )
13 print( ’ A quién ofrece? : Se coloca a quien desea realizar el
,→ intercambio’)
14 print( ’ Cantidad del ítem a enviar: Se coloca la cantidad del
,→ item a enviar ’ )
15 print( ’ Item: Se coloca el ítem a enviar’)
16 print( ’ Monto: Se coloca el oro que le gustaría recibir a
,→ cambio.’)
17 print( ’ Para aceptar una oferta enviada a usted, escriba
,→ "accept" (sin comillas) cuando sea’)
18 print( ’ necesario ’ )
19 print( ’ Por el contrario , si desea rechazar una oferta
,→ que le han enviado, escriba ’ )

Proyecto 1. EL-4005 Principios de comunicaciones


Descripción del Cliente 12

20 print( ’ " reject " (sin comillas) cuando sea necesario ’ )

El comando emojis permite enviar caras digitales hacia otros clientes, gracias a la previa
definición de éstas en la inicialización del cliente:

Código 21: Emojis.


1 ## Se tiene la lista de emojis disponibles
2 elif self .msg == ’ListaEmojis’:
3 print( ’Los emojis disponibles son los siguientes : ’ )
4 for i in self .Emojis_Dict:
5 print( ’ ’ + i + ’: ’ + self .Emojis_Dict[i])

Al ingresar "Spawn"se recolecta las informaciones del item y cantidad y se envían al servidor
para su luego adición al diccionario de items del cliente:

Código 22: Spawn.


1 ## Se generan los items
2 elif self .msg == ’spawn’:
3 Item_input = input(’Item a agregar: ’)
4 Item_assig = ’Item_Assignator’ + Item_input
5 self .send_msg(Item_assig)
6 Amount_input = input(’Cantidad del item: ’)
7 Amount_assig = ’Amount_Assignator’ + Amount_input
8 self .send_msg(Amount_assig)

Con el comando offer, el cliente puede ingresar datos de la propuesta oferta que son recolec-
tados para luego enviarlos al sevidor y luego al cliente comprador:

Código 23: Offer.


1 ## Se establece una oferta de intercambio
2 elif self .msg == ’offer’:
3 Comprador_input = input(’A quién ofrece?’)
4 Comprador_assig = ’Comprador_Assignator’ + Comprador_input
5 self .send_msg(Comprador_assig)
6 Cantidad_input = input(’Cantidad del ítem a enviar: ’)
7 Cantidad_assig = ’Cantidad_Assignator’ + Cantidad_input
8 self .send_msg(Cantidad_assig)
9 Objeto_input = input(’Item: ’)
10 Objeto_assig = ’Objeto_Assignator’ + Objeto_input
11 self .send_msg(Objeto_assig)
12 Monto_input = input(’Monto: ’)
13 Monto_assig = ’Monto_Assignator’ + Monto_input
14 self .send_msg(Monto_assig)

Ingresando .accept.o reject"se cambian los marcadores predefinidos que permiten aceptar o no
la propuesta oferta llegada al cliente:

Código 24: Accept y reject.

Proyecto 1. EL-4005 Principios de comunicaciones


Descripción del Cliente 13

1 # Se acepta el intercambio
2 elif self .msg == ’accept’:
3 ACCEPT_FLAG = True
4 REJECT_FLAG = False
5
6 # Se rechaza el intercambio
7 elif self .msg == ’reject’:
8 ACCEPT_FLAG = False
9 REJECT_FLAG = True

Bolsa permite consultar la bolsa de items actual que pertenece al cliente:

Código 25: Bolsa.


1 # Se consulta por los items actuales en la bolsa
2 elif self .msg == ’Bolsa’ :
3 self .send_msg(’Bolsa_Items’)

En la recepción de mensajes se especifica el caso de cuando el cliente recibe una oferta, donde se ac-
tivan o desactivan los marcadores ACCEPT_FLAG, REJECT_FLAG y VERIFY_FLAG
según el caso de aceptar o rechazar una propuesta.

Código 26: Recepción de mensajes.


1 # Se reciben los mensajes
2 def msg_recv(self):
3 global ACCEPT_FLAG
4 global REJECT_FLAG
5 while True:
6 try :
7 data = self .sock.recv(1024)
8 if data:
9 try :
10 print( pickle . loads(data))
11 except:
12 print(data.decode())
13 if ’Propuesta_Oferta’ in data.decode() or ’Propuesta_Oferta’ in pickle.
,→ loads(data):
14 #Lock_prueba = threading.Lock()
15 Oferta_Box = data.decode()
16 # Oferta_Base = Propuesta_Oferta[PRIVADO] <Diego> Te ofrece 30
,→ unidades de Doggos por 50 de oro! :D (accept/reject)
17 Oferta_Base = Oferta_Box[16:]
18 # Oferta_Base = [PRIVADO] <Diego> Te ofrece 30 unidades de Doggos
,→ por 50 de oro! :D (accept/reject)
19 print(Oferta_Base)
20 thread_local.VERIFY_FLAG = True
21 while thread_local.VERIFY_FLAG:
22 time.sleep (2)
23 if ACCEPT_FLAG == True:
24 Envio_Oferta = ’Acepto_Oferta’ + Oferta_Base

Proyecto 1. EL-4005 Principios de comunicaciones


Conclusiones 14

25 # Envio_Oferta= Acepto_Oferta[PRIVADO] <Diego> Te ofrece


,→ 30 unidades de Doggos por 50 de oro! :D (accept/reject)
26 self .send_msg(Envio_Oferta)
27 ACCEPT_FLAG = False
28 break
29 elif REJECT_FLAG == True:
30 self .send_msg(’reject’)
31 REJECT_FLAG = False
32 break
33 else :
34 print(data.decode())
35 pass
36 except:
37 pass

Se envían las solicitudes al servidor y los mensajes hacia otros clientes a través del uso del paquete
pickle:

Código 27: Envío de mensajes.


1 # Se utiliza esta funcion para enviar los mensajes
2 def send_msg(self, msg):
3 self .sock.send(pickle .dumps(msg))

5. Discusión
El desarrollo de la tarea se vio estancado por un largo período debido al poco manejo del formato
en el que se estaba trabajando. Si bien se conocía el concepto teórico de los sockets, se desconocía su
forma de implementación en el lenguaje utilizado, python. Una vez que se comprendió la forma de
envío y recepción de datos, además de como codificarlos/decodificarlos, fue posible realizar avances,
completando todos los requerimientos solicitados.

Otro aspecto a discutir, es que la implementación no funciona igual si se realiza en una interfaz
diferente a spyder. Al llevar a cabo la implementación en python propiamente tal, los mensajes
llegan de forma no fluida, quedando clientes en espera por un tiempo. Se cree que esto es posi-
blemente debido a que una interfaz cuenta con librerías que la otra no, ya que como se mencionó
anteriormente, el comando sock.setblocking debería evitar los tiempos de espera cliente-servidor.

6. Conclusiones
Se logró implementar tanto el servidor como cliente solicitados para esta actividad. La imple-
mentación de threads permitió tanto la conexión múltiple de un máximo de 6 usuarios distintos
por el lado del servidor, como el envío y recepción de mensajes entre clientes conectados al sevidor,
mostrándose estos mensajes en las consolas de cada usuario conectado. Estos usuarios fueron capa-
ces de designarse un identificador único por cada usuario, siendo notificados si el identificador ya
ha sido ocupado por otro usuario conectado. Por otra parte, una vez conectados al servidor, estos
usuarios son capaces de interactuar mediante una serie de comandos con otros usuarios conectados

Proyecto 1. EL-4005 Principios de comunicaciones


Conclusiones 15

al servidor, como crear e intercambiar objetos, enviar emojis, entre otros. Mediante diccionarios
se mantiene un registro por cada usuario de la moneda virtual ocupada dentro del chat, así como
también los items generados e intercambiados.Así, considerando lo anterior expuesto, se consideran
cumplidos exitósamente los objetivos propuestos para esta actividad.

Las dificultades presentes consistieron, en primer lugar, en el poco manejo previo del funcio-
namiento práctico de la implementación de sockets en Python, lo que retrasó considerablemente
los avances al elaborar el servidor y el cliente los primeros días. En segundo lugar, no se consideró
durante los primeros días el funcionamiento distinto de las interfaces de python al momento de
probar los códigos, lo que conllevó a que se retrasara el avance. Esto se solucionó una vez se utilizó
una interfaz común (Spyder).

Proyecto 1. EL-4005 Principios de comunicaciones


Referencias 16

Referencias
[1] Sockets con Python, [en línea].https://github.com/hcastillaq/Python-Sockets/blob/
master/Sockets_con_Python.pdf
[2] uma., El interfaz socket, [en línea] <http://www.lcc.uma.es/~eat/services/i_socket/i_
socket.html#link22>
[3] EcuRed., Socket, [en línea] <https://www.ecured.cu/Socket>
[4] Andrew S., Sistemas Operativos Modernos 3°edición., [en línea] <https://www.
lawebdelprogramador.com/pdf/14102-Sistemas-operativos-modernos.html>

Proyecto 1. EL-4005 Principios de comunicaciones

También podría gustarte