Manual del
Lenguaje de programación
C#
Parte VI-A
Sockets y Ports
OBJETIVO DEL MANUAL 3
OBJETIVO DEL MANUAL 3
Sockets y Ports 3
SOCKET 4
¿Con que contamos en C#? 6
Procesos de un solo thread y sincrónicos.- 7
Procesos de mas de un thread y asincrónicos.- 8
Usar un socket para comunicaciones con el protocolo HTTP 8
El protocolo HTTP 8
Y lo ejecutamos, vemos la salida de texto que se muestra a continuación: 16
Usar sockets para armar un programa de mensajería entre pares (chat) 18
Codigo en Form1.cs 20
Código en program.cs 21
Código en Talker.cs 23
Como ejecutar la aplicación 32
Sockets y Ports 33
El protocolo FTP 33
OBJETIVO DEL MANUAL
Sockets y Ports
Un Socket es una abstracción.-
Representa un punto de intercomunicación entre dos procesos (por ejemplo cliente y servidor). Los procesos pueden estar en el mismo host, nodo o computadora o en nodos diferentes.-
En un host pueden correr diferentes procesos brindando diferentes servicios. Por ejemplo servicios de HTTP, de Telnet, de FTP, de SMTP, etc. Cada uno de estos procesos tiene una dirección que lo identifica unívocamente respecto de los otros. Esta dirección se llama PORT.
Generalmente el servicio de HTTP se brinda en el port 80, el de Telnet en el 23, FTP en el 21 y SMTP en el 25. Decimos entonces que un PORT es una dirección relativa. Identifica al proceso pero no identifica al host en el que corre dicho proceso.
En una red TCP/IP como Internet cada host tiene una identificación única llamada número IP.
Un SOCKET es un número IP + un PORT. Por lo tanto es una dirección absoluta ya que identifica unívocamente un proceso en toda la red.
SOCKET
La especificación Windows Sockets define una interfaz de programación en Windows basada en el paradigma de sockets del BSD (Berkeley Software Distribution).-
Esta biblioteca, escrita en lenguaje C por ingenieros, profesores y estudiantes de la Universidad de Berkeley, fue pensada para la comunicación entre procesos que se ejecutan en la misma computadora o en computadoras de una misma red.-
EL software fue escrito originalmente como una API (Application Programming Interface) y el código que figura a continuación es un pequeño esqueleto que muestra como es la secuencia de inicio y cierre de un proceso de comunicación, pensando en un solo thread de ejecución:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
int main()
{
//Inicio
int32_t i32SocketFD = socket(AF_INET, SOCK_STREAM, 0);
if(0 == i32SocketFD)
{
printf("no se puede crear el socket");
exit(-1);
}
struct sockaddr_in stSockAddr;
bzero(&stSockAddr, sizeof(stSockAddr));
stSockAddr.sin_family = AF_INET;
stSockAddr.sin_port = htons(1100);
stSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
//Enlace
if(-1 == bind(i32SocketFD,(struct sockaddr*) &stSockAddr, sizeof(stSockAddr)))
{
printf("error: bind fallido");
exit(-1);
}
//Escucha
if(-1 == listen(i32SocketFD, 10))
{
printf("error: listen fallido");
exit(-1);
}
for(; ;)
{
int32_t i32ConnectFD = accept(i32SocketFD, NULL, NULL);
if(0 > i32ConnectFD)
{
printf("error accept fallido");
exit(-1);
}
// acá es donde se debe escribir el código especifico ...
…..
//cierre
shutdown(i32ConnectFD, 2);
close(i32ConnectFD);
}
return 0;
}
¿Con que contamos en C#?
En C#, encontramos la funcionalidad de esta especificación, encapsulada en una clase llamada Socket, que es la que provee los métodos para la comunicación a través de la red así como la transferencia (sincrónica o asíncrona de datos).-
La clase Socket se encarga de proveer métodos y propiedades para la comunicación en red.-
Lo realmente interesante de esta clase es que encapsula la complejidad de la comunicación y una vez establecida el manejo de lo entrante y saliente se hace manejando flujos (streams).-
Estos flujos de comunicación pueden ser sincrónicos o asíncronos.-
Procesos de un solo thread y sincrónicos.-
Igual que en el ejemplo en lenguaje C visto al inicio de este manual, para procesos de un solo thread podemos utilizar los métodos específicos para modo de operación asíncrona: Listen(), Accept(), Bind(), Send() y Receive().-
Protocolo TCP
Estos métodos se utilizan en protocolos orientados a conexión (connection-oriented) como por ejemplo TCP.-
En este caso el servidor de la comunicación puede escuchar los pedidos con el método Listen .-
EL método Accept() procesa cualquier pedido de conexión entrante y retorna un Socket que es el que se utilizara para comunicar datos con el host remoto.-
Es este Socket el que se usa para llamar a los métodos Send() o Receive().-
Como en el caso general se desea indicar la dirección IP y el numero de puerto, se debe llamar al método Bind(), previo a llamar a Listen().-
En caso de no seguir esta secuencia, el proveedor del servicio subyacente asignara estos valores por nosotros. Es posible luego obtener la dirección IP y port asignados a nuestro Socket, del modo recién indicado.-
En el caso de querer conectarse a un servidor que esta escuchando requisiciones, se debe llamar al método Connect() y luego utilizar Send() y Receive() para envío y recepción de datos.-
// crear un socket
Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// enlazar el socket que escucha
IPAddress hostIP = (Dns.Resolve(IPAddress.Any.ToString())).AddressList[0];
IPEndPoint ep = new IPEndPoint(hostIP, port);
listenSocket.Bind(ep);
// escuchar
listenSocket.Listen(backlog);
Protocolo UDP
Si usamos un protocolo como UDP (protocolo sin conexión: connectionless), entonces no es necesario "escuchar" para conectarse. El método ReceiveFrom() es el que debe usarse para recibir los datagramas entrantes y el método SendTo() es el que debe usarse para el envio de datagramas al host remoto.
Procesos de mas de un thread y asincrónicos.-
Para procesar comunicaciones utilizando threads separados, existen una serie de mtodos pensados al efecto, es decir en un modo de operacion asincrono.-
Protocolo TCP
Cuando se utilizan protocolos orientados a la conexión (connection-oriented) como TCP, entonces debe utilizar los métodos Socket(), BeginConnect() y EndConnect() para comenzar las tareas de conexiones con el server que atiende los pedidos.-
El asincronismo en el envío y la recepción de datos (en los casos donde trabajamos con mas de un thread) necesita del uso de los métodos específicos de comunicación asincrónica. Estos son: BeginSend(),EndSend() , BeginReceive() y EndReceive(). Los pedidos de conexión entrante los debe procesar con los métodos: BeginAccept() y EndAccept().
Si estamos usando UDP entonces los métodos adecuados son para enviar datagramas son BeginSendTo() y EndSendTo() y para la recepción se debe usar BeginReceiveFrom() y EndReceiveFrom().
Usar un socket para comunicaciones con el protocolo HTTP
El ejemplo siguiente utiliza una clase Socket para enviar y recibir respuestas de un servidor HTTP.
Pensemos en un servidor WEB que esta corriendo en algún punto de la red.
El modo en el que escribimos el código requiere de algunos conceptos de cómo trabaja el protocolo HTTP, damos un repaso que consideramos adecuado.-
El protocolo HTTP
El Protocolo de Transferencia de Hipertexto (Hypertext Transfer Protocol) es un protocolo para comunicación entre un nodo cliente y otro nodo servidor.-
Su uso central es en la WEB, pero aclaremos que un servidor HTTP, puede recibir pedidos y enviar información sin necesidad de la existencia de browsers. Es decir es un mecanismo de comunicación que puede llegar a utilizarse en procesos puramente batch , no solo para interactivos.-
El protocolo esta montado sobre el servicio de conexión TCP/IP.
HTTP se basa en sencillas operaciones de solicitud/respuesta.
Un cliente establece una conexión con un servidor y envía un mensaje con los datos de la solicitud.-
El servidor responde con un mensaje similar, que contiene el estado de la operación y su posible resultado.-
La primera parte de una transacción HTTP es cuando un cliente crea y envia una solicitud al servidor.
Por ejemplo:
GET /index.html /HTTP 1.1
Es una pedido absolutamente correcto para un servidor HTTP activo
Existen tres verbos básicos que un cliente puede utilizar para dialogar con el servidor: GET, para recoger un objeto, POST, para enviar información al servidor y HEAD, para solicitar las características de un objeto (por ejemplo, la fecha de modificación de un documento HTML).
Todas las operaciones pueden adjuntar un objeto o recurso sobre el que actúan; cada objeto Web (documento HTML, fichero multimedia o aplicación CGI) es conocido por su URL (Uniform resources location podría concebirse como la extensión del concepto de nombre completo de un archivo (path)).
Cada operación HTTP implica una conexión con el servidor, que es liberada al término de la misma. (Esa es la verdadera esencia de Internet ¡!)
Es decir, en una operación se puede recoger un único objeto y no se mantienen estados.
Cada petición de un cliente a un servidor no es influida por las transacciones anteriores. El servidor trata cada petición como una operación totalmente independiente del resto.
El acceso a los servidores HTTP se realiza a través de las URLs, que empaquetan toda la información necesaria para localizar un determinado recurso en Internet. Cuando el servidor recibe una URL, a través del protocolo HTTP, la decodifica y devuelve al cliente la información correspondiente, que puede ser de dos tipos, en función del contenido de la URL:
Información estática: que se publica a base de situar los ficheros que la contienen en unos directorios especiales que define cada servidor. Este es el procedimiento que se ha utilizado hasta ahora.
Información dinámica: en este caso, el servidor HTTP arranca una aplicación utilizando la propia URL como parámetro de ejecución. El resultado de la ejecución de la aplicación será devuelto al cliente
El término 'información dinámica' se amplia cada vez más, a medida que se dispone de nuevas aplicaciones y tecnologías de publicación de información.
A través de estas nuevas tecnologías, es posible generar vistas a través del Web de elementos tan dispares como hojas de cálculo, bases de datos, agendas de reuniones, correos electrónicos e incluso periódicos completos, que se actualizan automáticamente en función de los cambios en el contenido de información original.
using System;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
public class mySocket
{
private static Socket connectSocket(string server, int port)
{
Socket s = null;
IPHostEntry iphe = null;
try
{
//
iphe = Dns.Resolve(server);
// Esta iteracion a traves de la lista de direcciones ( AddressList) es para obtener la
//llamada familia de direcciones ( AddressFamily) soportada.Esto evitara una excepcion en el caso
//en que la dirección IP no sea compatible con dicha familia.-
foreach (IPAddress ipad in iphe.AddressList)
{
IPEndPoint ipe = new IPEndPoint(ipad, port);
Socket tmpS =
new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
tmpS.Connect(ipe);
if (tmpS.Connected)
{
s = tmpS;
break;
}
else
continue;
}
}
catch (SocketException e)
{
Console.WriteLine("SocketException !!!");
Console.WriteLine("Origen : " + e.Source);
Console.WriteLine("Mensaje : " + e.Message);
}
catch (Exception e)
{
Console.WriteLine("Exception atrapada!!!");
Console.WriteLine("Origen : " + e.Source);
Console.WriteLine("Mensaje : " + e.Message);
}
return s;
}
// este método consulta el contenido de la pagina Home del servidor indicado.
// y muestra el contenido recibido
private static string socketSendReceive(string server, int port)
{
//Preparamos para escribir al server
Encoding ASCII = Encoding.ASCII;
string Get = "GET / HTTP/1.1\r\nHost: " + server +
"\r\nConnection: Close\r\n\r\n";
Byte[] ByteGet = ASCII.GetBytes(Get);
Byte[] RecvBytes = new Byte[256];
String strRetPage = null;
// Ahora creamos un socket como conneccion con el server y el port indicado
Socket s = connectSocket(server, port);
if (s == null)
return ("Conneccion fallida");
// Ahi viaja el pedido al servidor.
s.Send(ByteGet, ByteGet.Length, 0);
// Aca viene la respuesta de la pagina home del server
Int32 bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);
// Leer los primeros 256 bytes.Esto lo determina el uso de
//RecvBytes.Length en el método Receive
strRetPage = "Pagina Default HTML en el " + server + ":\r\n";
strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, bytes);
while (bytes > 0)
{
bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);
strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, bytes);
}
return strRetPage;
}
public static void Main(string[] args)
{
string host;
int port = 91;
if (args.Length == 0)
// si no hay server indicado, usamos el nombre del host.
host = Dns.GetHostName();
else
host = args[0];
string result = socketSendReceive(host, port);
Console.WriteLine(result);
}
}
Vamos a hacer la prueba de este programa conectándolo contra un server WEB TOMCAT.
En mi equipo, como se observa, esta levantado en el puerto 8088.-
Si ejecuto el programa, colocando como parámetro en la línea de ejecución del programa, esta dirección.
Y lo ejecutamos, vemos la salida de texto que se muestra a continuación:
Por supuesto que es mejor verla con el browser ¡!
Usar sockets para armar un programa de mensajería entre pares (chat)
Vamos a construir una aplicación Windows que nos entregue una interfaz simple para comunicación. Queremos que sea un contenedor con dos paneles.
Uno que me muestre los mensajes que recibo y el otro que me permita escribir mensajes.-
La propia aplicación trabajara de dos modos diferenciados dependiendo de cómo se la invoque en la llamada. En un caso será servidor y en otro cliente.-
Alcanza con solo servidor para atender múltiples clientes.-
El esquema que armamos es el de "estrella". Es decir tal como armamos la aplicación todos los clientes pueden hablar con el servidor, pero no entre si.-
Una variante de este modo en el que el servidor atrape el pedido y lo derive u otro cliente de la red es relativamente sencillo de hacer basado en la tecnología mostrada en este ejemplo.-
Se habrá creado un proyecto con una serie de componentes como muestra el grafico
En este tipo de aplicaciones dentro del Form (Form1.cs), la IDE de .NET escribe algunos códigos de inicialización que no queremos para nuestro caso.-
Codigo en Form1.cs
Ingrese a código de Form1.Designer.cs y reemplácelo por el código que figura a continuación:
namespace UnChat
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
}
#endregion
}
}
Código en program.cs
Reemplace el código del modulo program.cs por el código que figura a continuación
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Net;
using System.Globalization;
using System.Net.Sockets;
namespace UnChat
{
static
class
Program
{
// -
private
static
IPEndPoint endPoint;
//Indica si esta actuando en modo servidor o cliente
private
static
bool client;
// Entrada al programa
public
static
void Main(String[] args)
{
// -
if (ParseArgs(args))
{
//
Talker talker = new
Talker(endPoint, client);
// Referencia del Talker dentro del Form
TalkForm form = new
TalkForm(talker);
// Arranca el talker
talker.Start();
// Arranca la apliacion con el Form
Application.Run(form);
}
}
// Parser de la linea de comandos
private
static
bool ParseArgs(String[] args)
{
try
{
if (args.Length == 0)
{
client = false;
endPoint = new
IPEndPoint(IPAddress.Any, 5150);
return
true;
}
if (args[0][0] != '/' && args[0][0] != '-')
{
ShowUsage();
return
false;
}
switch (Char.ToUpper(args[0][1], CultureInfo.InvariantCulture))
{
case
'L': //Listen: Escucha
int port = 5150;
if (args.Length > 1)
{
port = Convert.ToInt32(args[1]);
}
endPoint = new
IPEndPoint(IPAddress.Any, port);
client = false;
break;
case
'C'://Call: Llama
port = 5150;
String address = "127.0.0.1";
client = true;
if (args.Length > 1)
{
address = args[1];
port = Convert.ToInt32(args[2]);
}
endPoint = new
IPEndPoint(Dns.GetHostEntry(address).AddressList[0], port);
break;
default:
ShowUsage();
return
false;
}
}
catch
{
ShowUsage();
return
false;
}
return
true;
}
// Como se usa
private static void ComoUsarme()
{
MessageBox.Show("UnChat [switch] [parametros...]\n\n" +
" /L [port]\t\t-- Escucha en el port. Default: 5150\n" +
" /C [direccion] [port]\t-- Conecta a una direccion y port.\n\n" +
"Ejemplo Server - \n" +
"UnChat /L\n\n" +
"Ejemplo Client - \n" +
"UnChat /C NombreDeServidor 5150", "Uso de UnChat");
}
}
}
Código en Talker.cs
Creemos ahora la clase que representara al Talker
Y coloquemos dentro del archivo de esta clase el código que se indica a continuación:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace UnChat
{
// Esta clase es una capa que encpasula a la clase Socket y le agrega
// algunas habilidades de chateo.-
class Talker : IDisposable
{
private Socket socket;
private TextReader reader;
private TextWriter writer;
bool client;
IPEndPoint endPoint;
private String prevSendText;
private String prevReceiveText;
private String statusText;
private Status status;
// Constructor
public Talker(IPEndPoint endPoint, bool client)
{
this.endPoint = endPoint;
this.client = client;
socket = null;
reader = null;
writer = null;
statusText = prevSendText = prevReceiveText = String.Empty;
}
// -
~Talker()
{
Dispose();
}
// Este es un modo ordenado y seguro de liberar todos
//los recursos que se usaron en el momento en que se baja
//la aplicacion
public void Dispose()
{
GC.SuppressFinalize(this);
if (reader != null)
{
reader.Close();
reader = null;
}
if (writer != null)
{
writer.Close();
writer = null;
}
if (socket != null)
{
socket.Close();
socket = null;
}
}
// Clase delegate y evento
public delegate
void NotificationCallback(Notification notify, Object data);
public event NotificationCallback Notifications;
// enum de los tipos de notificaciones
public enum Notification
{
Initialized = 1,
StatusChange,
ReceivedRefresh,
ReceivedAppend,
End,
Error
}
// enum de los posibles estados
public enum Status
{
Escuchando,
Conectado,
Cliente
}
// EL inicio de una instancia de la clase es un thread
//que se encola en el pool de threads
public void Start()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(EstablishSocket));
}
// Envio de texto a la coneccion remota
public void EnviaMensaje(String newText)
{
String send;
// es un agregado al texto (append)
if ((prevSendText.Length <= newText.Length) && String.CompareOrdinal(
newText, 0, prevSendText, 0, prevSendText.Length) == 0)
{
String append = newText.Substring(prevSendText.Length);
send = String.Format("A{0}:{1}", append.Length, append);
}// o reemplazo completo
else
{
send = String.Format("R{0}:{1}", newText.Length, newText);
}
// Envio datos y hace flush
writer.Write(send);
writer.Flush();
// Guardar el texto para comparar si es necesario
prevSendText = newText;
}
// Notifica un estado
private void SetStatus(Status status)
{
this.status = status;
Notifications(Notification.StatusChange, status);
}
// Establecer una coneccion via socket y comenzar la recepcion
private void EstablishSocket(Object state)
{
NetworkStream stream = null;
try
{
// Si no es un cliente, entonces levantar una Escucha (listener)
if (!client)
{
Socket listener;
try
{
listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
listener.Blocking = true;
listener.Bind(endPoint);
SetStatus(Status.Escuchando);
listener.Listen(0);
socket = listener.Accept();
listener.Close();
stream = new NetworkStream(socket);
reader = new StreamReader(stream);
writer = new StreamWriter(stream);
writer.Write("WINTALK .NET");
writer.Flush();
}
catch (SocketException e)
{
// Este error indica que alguien esta usando el puerto
//Asumamos que en ese caso se debe intentar conectarse como un cliente
//pensando que ya hay un server para los nodos de la red
if (e.ErrorCode == 10048)
{
client = true;
endPoint = new IPEndPoint(
Dns.Resolve("127.0.0.1").AddressList[0], endPoint.Port);
}
else
{
Notifications(
Notification.Error,
"Error al inicializar el Socket:\n" + e.ToString());
}
}
}
// Intenta coneccion como cliente
if (client)
{
SetStatus(Status.Cliente);
Socket temp = new
Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
temp.Blocking = true;
temp.Connect(endPoint);
socket = temp;
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 5000);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 5000);
stream = new NetworkStream(socket);
reader = new StreamReader(stream);
writer = new StreamWriter(stream);
char[] handshake = new char[12];
try
{
reader.Read(handshake, 0, 12);
string sHandShake = new string(handshake);
if (!(reader.Read(handshake, 0, 12) > 0 && sHandShake == "WINTALK .NET"))
{
socket.Close();
socket = null;
}
else
{
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 0);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 0);
}
}
catch
{
socket.Close();
socket = null;
}
}
// Si establecimos el socket, comenzar a chatear
if (socket != null)
{
SetStatus(Status.Conectado);
Notifications(Notification.Initialized, this);
// -
//NetworkStream.Read()es un metodo llamado por el metodo ReceiveTalk
//El primer metodo genera una excepcion cuando se cierra la coneccion remota
//La excepcion se maneja en el catch
ReceiveTalk();
// Por aca pasa cuando NetworkStream.Read() retorna un cero (algo que hace
//en alguans versiones del SO
Notifications(Notification.End, "La coneccion remota se cerro.");
}
else
{
Notifications(Notification.Error,
"Falla al establecer el , Verifique si especifico el port correcto");
}
}
catch (IOException e)
{
SocketException sockExcept = e.InnerException as SocketException;
if (sockExcept != null && 10054 == sockExcept.ErrorCode)
{
Notifications(Notification.End, "La coneccion remota se cerro.");
}
else
{
if (Notifications != null)
Notifications(Notification.Error, "Error :\n" + e.Message);
}
}
catch (Exception e)
{
Notifications(Notification.Error, "Error:\n" + e.Message);
}
}
// Recibe la comunicacion del cliente remoto
private void ReceiveTalk()
{
char[] commandBuffer = new char[20];
char[] oneBuffer = new char[1];
int readMode = 1;
int counter = 0;
StringBuilder text = new StringBuilder();
while (readMode != 0)
{
if (reader.Read(oneBuffer, 0, 1) == 0)
{
readMode = 0;
continue;
}
switch (readMode)
{
case 1:
if (counter == commandBuffer.Length)
{
readMode = 0;
continue;
}
if (oneBuffer[0] != ':')
{
commandBuffer[counter++] = oneBuffer[0];
}
else
{
counter = Convert.ToInt32(
new String(commandBuffer, 1, counter - 1));
if (counter > 0)
{
readMode = 2;
text.Length = 0;
}
else if (commandBuffer[0] == 'R')
{
counter = 0;
prevReceiveText = String.Empty;
Notifications(Notification.ReceivedRefresh, prevReceiveText);
}
}
break;
case 2:
text.Append(oneBuffer[0]);
if (--counter == 0)
{
switch (commandBuffer[0])
{
case 'R':
prevReceiveText = text.ToString();
Notifications(Notification.ReceivedRefresh, prevReceiveText);
break;
default:
string newText = text.ToString();
prevReceiveText += newText;
Notifications(Notification.ReceivedAppend, newText);
break;
}
readMode = 1;
}
break;
default:
readMode = 0;
continue;
}
}
}
}
}
Como ejecutar la aplicación
Usar una sesión como cliente y otra como servidor
Para eso generar dos archivos .bat (llamémoslos ChatCliente.bat y ChatServer.bat) que inicien cada uno una sesión del programa
Por ejemplo para ChatCliente.bat
UnChat.exe /C m3nb02 5150
Que significa que el programa desea actuar como Cliente (de allí la letra C) del equipo m3nb02 (es el nombre de mi equipo en esta red en la que estoy trabajando) y "hablar" por el port 5150.-
Por ejemplo para ChatServer.bat
UnChat.exe /L 5150
Que significa que el programa desea actuar como Server (La letra L por Listen) y "hablar" por el port 5150.-
Sockets y Ports
El protocolo FTP
Codigos de retorno de FTP
Code | Explanation |
100 | Series: The requested action is being initiated, expect another reply before proceeding with a new command. |
110 | Restart marker replay . In this case, the text is exact and not left to the particular implementation; it must read: MARK yyyy = mmmm where yyyy is User-process data stream marker, and mmmm server's equivalent marker (note the spaces between markers and "="). |
120 | Service ready in nnn minutes. |
125 | Data connection already open; transfer starting. |
150 | File status okay; about to open data connection. |
200 | Command okay. |
202 | Command not implemented, superfluous at this site. |
211 | System status, or system help reply. |
212 | Directory status. |
213 | File status. |
214 | Help message.On how to use the server or the meaning of a particular non-standard command. This reply is useful only to the human user. |
215 | NAME system type. Where NAME is an official system name from the list in the Assigned Numbers document. |
220 | Service ready for new user. |
221 | Service closing control connection. |
225 | Data connection open; no transfer in progress. |
226 | Closing data connection. Requested file action successful (for example, file transfer or file abort). |
227 | Entering Passive Mode (h1,h2,h3,h4,p1,p2). |
228 | Entering Long Passive Mode (long address, port). |
229 | Entering Extended Passive Mode (port). |
230 | User logged in, proceed. Logged out if appropriate. |
231 | User logged out; service terminated. |
232 | Logout command noted, will complete when transfer done. |
250 | Requested file action okay, completed. |
257 | "PATHNAME" created. |
331 | User name okay, need password. |
332 | Need account for login. |
350 | Requested file action pending further information |
421 | Service not available, closing control connection. This may be a reply to any command if the service knows it must shut down. |
425 | Can't open data connection. |
426 | Connection closed; transfer aborted. |
434 | Requested host unavailable. |
450 | Requested file action not taken. |
451 | Requested action aborted. Local error in processing. |
452 | Requested action not taken. Insufficient storage space in system.File unavailable (e.g., file busy). |
500 | Syntax error, command unrecognized. This may include errors such as command line too long. |
501 | Syntax error in parameters or arguments. |
502 | Command not implemented. |
503 | Bad sequence of commands. |
504 | Command not implemented for that parameter. |
530 | Not logged in. |
532 | Need account for storing files. |
550 | Requested action not taken. File unavailable (e.g., file not found, no access). |
551 | Requested action aborted. Page type unknown. |
552 | Requested file action aborted. Exceeded storage allocation (for current directory or dataset). |
553 | Requested action not taken. File name not allowed. |