[Mono-list] TcpClient & TcpListener - request for code review
Phillip Pearson
pp@myelin.co.nz
Thu, 22 Nov 2001 00:25:49 +1300
This is a multi-part message in MIME format.
------=_NextPart_000_0075_01C172EC.3C8F09E0
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
> You might want to have an `internal' method (this is a method that can
> only be called from within the assembly) to "configure" the TcpClient:
>
> internal SetTcpClient (Socket s)
> {
> MySocket = s;
> }
Aha, so *that's* how you do 'friend' in C# :)
I've attached the now complete (but probably buggy) code for
System.Net.Sockets.TcpClient and System.Net.Sockets.TcpListener to this
message. Can the person in charge of System.Net[.Sockets] (Miguel?) review
the files and commit them if they look OK, or tell me what needs to be done
if they aren't OK?
Thanks,
Phillip.
------=_NextPart_000_0075_01C172EC.3C8F09E0
Content-Type: text/plain;
name="TcpClient.cs"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="TcpClient.cs"
// System.Net.Sockets.TcpClient.cs=0A=
//=0A=
// Author:=0A=
// Phillip Pearson (pp@myelin.co.nz)=0A=
//=0A=
// Copyright (C) 2001, Phillip Pearson=0A=
// http://www.myelin.co.nz=0A=
//=0A=
=0A=
// NB: This is untested (probably buggy) code - take care if using it=0A=
=0A=
using System;=0A=
using System.Net;=0A=
=0A=
namespace System.Net.Sockets=0A=
{=0A=
/// <remarks>=0A=
/// A slightly more abstracted way to create an=0A=
/// outgoing network connections than a Socket.=0A=
/// </remarks>=0A=
public class TcpClient : IDisposable=0A=
{=0A=
// private data=0A=
=0A=
private NetworkStream stream;=0A=
private bool active;=0A=
private Socket client;=0A=
private bool disposed =3D false;=0A=
=0A=
// constructor=0A=
=0A=
/* TODO: Code common to all the constructors goes here.=0A=
Can anyone tell me why I can't call a constructor from=0A=
another constructor? */=0A=
=0A=
/// <summary>
/// Some code that is shared between the constructors.
/// </summary>=0A=
private void common_constructor ()=0A=
{=0A=
active =3D false;=0A=
client =3D new Socket(AddressFamily.InterNetwork,=0A=
SocketType.Stream, ProtocolType.Tcp);=0A=
}=0A=
=0A=
/// <summary>
/// Constructs a new TcpClient with no connection set up
/// </summary>=0A=
public TcpClient ()=0A=
{=0A=
common_constructor();=0A=
client.Bind(new IPEndPoint(IPAddress.Any, 0));=0A=
}=0A=
=0A=
/// <summary>
/// Constructs a new TcpClient with a specified local endpoint.
/// Use this if you want to have your connections originating
/// from a certain port, or a certain IP (on a multi homed
/// system).
/// </summary>
/// <param name=3D"local_end_point">The aforementioned local =
endpoint</param>=0A=
public TcpClient (IPEndPoint local_end_point)=0A=
{=0A=
common_constructor();=0A=
client.Bind(local_end_point);=0A=
}=0A=
=0A=
/// <summary>
/// Constructs a new TcpClient and connects to a specified
/// host on a specified port. A quick way to set up a network
/// connection.
/// </summary>
/// <param name=3D"hostname">The host to connect to, e.g.
/// 192.168.0.201 or www.myelin.co.nz</param>
/// <param name=3D"port">The port to connect to, e.g. 80 for =
HTTP</param>=0A=
public TcpClient (string hostname, int port)=0A=
{=0A=
common_constructor();=0A=
client.Bind(new IPEndPoint(IPAddress.Any, 0));=0A=
Connect(hostname, port);=0A=
}=0A=
=0A=
/// <summary>
/// A flag that is 'true' if the TcpClient has an active connection
/// </summary>=0A=
protected bool Active=0A=
{=0A=
get { return active; }=0A=
set { active =3D value; }=0A=
}=0A=
=0A=
/// <summary>
/// The socket that all network comms passes through
/// </summary>=0A=
protected Socket Client=0A=
{=0A=
get { return client; }=0A=
set { client =3D value; } //TODO: should we be able to set the socket =
like this?=0A=
}=0A=
=0A=
/// <summary>
/// Internal function to allow TcpListener.AcceptTcpClient
/// to work (it needs to be able to set protected property
/// 'Client')
/// </summary>
/// <param name=3D"s"></param>=0A=
internal void SetTcpClient (Socket s)=20
{=0A=
Client =3D s; // client or Client? They are the same at the moment=0A=
}=0A=
=0A=
/// <summary>
/// If set, the socket will remain open after it has been
/// instructed to close, in order to send data that remains
/// in the buffer.
/// </summary>=0A=
public LingerOption LingerState=0A=
{=0A=
get {=0A=
return (LingerOption)client.GetSocketOption(=0A=
SocketOptionLevel.Socket,=0A=
SocketOptionName.Linger);=0A=
}=0A=
set {=0A=
client.SetSocketOption(=0A=
SocketOptionLevel.Socket,=0A=
SocketOptionName.Linger, value);=0A=
}=0A=
}=0A=
=0A=
/// <summary>
/// <p>If set, outbound data will be sent at once rather than =
collected
/// until enough is available to fill a packet.</p>
///=20
/// <p>This is the TCP_NODELAY sockopt from BSD sockets and WinSock.
/// For more information, look up the Nagle algorithm.</p>
/// </summary>=0A=
public bool NoDelay=0A=
{=0A=
get {=0A=
return (bool)client.GetSocketOption(=0A=
SocketOptionLevel.Socket,=0A=
SocketOptionName.NoDelay);=0A=
}=0A=
set {=0A=
client.SetSocketOption(=0A=
SocketOptionLevel.Socket,=0A=
SocketOptionName.NoDelay, value);=0A=
}=0A=
}=0A=
=0A=
/// <summary>
/// How big the receive buffer is (from the connection socket)
/// </summary>=0A=
public int ReceiveBufferSize=0A=
{=0A=
get {=0A=
return (int)client.GetSocketOption(=0A=
SocketOptionLevel.Socket,=0A=
SocketOptionName.ReceiveBuffer);=0A=
}=0A=
set {=0A=
client.SetSocketOption(=0A=
SocketOptionLevel.Socket,=0A=
SocketOptionName.ReceiveBuffer, value);=0A=
}=0A=
}=0A=
=0A=
/// <summary>
/// How long before the socket will time out on a=20
/// Receive() call
/// </summary>=0A=
public int ReceiveTimeout=0A=
{=0A=
get {=0A=
return (int)client.GetSocketOption(=0A=
SocketOptionLevel.Socket,=0A=
SocketOptionName.ReceiveTimeout);=0A=
}=0A=
set {=0A=
client.SetSocketOption(=0A=
SocketOptionLevel.Socket,=0A=
SocketOptionName.ReceiveTimeout, value);=0A=
}=0A=
}=0A=
=0A=
/// <summary>
/// How big the send buffer is (from the connection socket)
/// </summary>=0A=
public int SendBufferSize=0A=
{=0A=
get {=0A=
return (int)client.GetSocketOption(=0A=
SocketOptionLevel.Socket,=0A=
SocketOptionName.SendBuffer);=0A=
}=0A=
set {=0A=
client.SetSocketOption(=0A=
SocketOptionLevel.Socket,=0A=
SocketOptionName.SendBuffer, value);=0A=
}=0A=
}=0A=
=0A=
/// <summary>
/// How long before the socket will time out on a
/// Send() call
/// </summary>=0A=
public int SendTimeout=0A=
{=0A=
get {=0A=
return (int)client.GetSocketOption(=0A=
SocketOptionLevel.Socket,=0A=
SocketOptionName.SendTimeout);=0A=
}=0A=
set {=0A=
client.SetSocketOption(=0A=
SocketOptionLevel.Socket,=0A=
SocketOptionName.SendTimeout, value);=0A=
}=0A=
}=0A=
=0A=
=0A=
// methods=0A=
=0A=
/// <summary>
/// Closes the socket and disposes of all managed resources
/// </summary>=0A=
public void Close ()=0A=
{=0A=
Dispose(true);=0A=
}=0A=
=0A=
/// <summary>
/// Connects to a specified remote endpoint
/// </summary>
/// <param name=3D"remote_end_point">The aforementioned =
endpoint</param>=0A=
public void Connect (IPEndPoint remote_end_point)=0A=
{=0A=
client.Connect(remote_end_point);=0A=
stream =3D new NetworkStream(client, true);=0A=
active =3D true;=0A=
}=0A=
=0A=
/// <summary>
/// Connects to an IP address on a port
/// </summary>
/// <param name=3D"address">The IP address (get it from =
Dns.GetHostByName)</param>
/// <param name=3D"port">The port to connect to, e.g. 80 for =
HTTP</param>=0A=
public void Connect (IPAddress address, int port)=0A=
{=0A=
Connect(new IPEndPoint(address, port));=0A=
}=0A=
=0A=
/// <summary>
/// Resolves a fully qualified domain name to an IP address
/// and connects to it on a specified port
/// </summary>
/// <param name=3D"hostname">The hostname, e.g. =
www.myelin.co.nz</param>
/// <param name=3D"port">The port, e.g. 80 for HTTP</param>=0A=
public void Connect (string hostname, int port)=0A=
{=0A=
IPHostEntry host =3D Dns.GetHostByName(hostname);=0A=
/* TODO: This will connect to the first IP address returned=0A=
from GetHostByName. Is that right? */=0A=
Connect(new IPEndPoint(host.AddressList[0], port));=0A=
}=0A=
=0A=
/// <summary>
/// Gets rid of all managed resources
/// </summary>=0A=
public void Dispose()=0A=
{=0A=
Dispose(true);=0A=
GC.SuppressFinalize(this);=0A=
}=0A=
=0A=
/// <summary>
/// Gets rid of all unmanaged resources
/// </summary>
/// <param name=3D"disposing">If this is true, it gets rid of all=0A=
/// managed resources as well</param>=0A=
protected virtual void Dispose (bool disposing)=0A=
{=0A=
if (disposed =3D=3D false) {=0A=
if (active) {=0A=
stream.Close();=0A=
active =3D false;=0A=
}=0A=
disposed =3D true;=0A=
}=0A=
}=0A=
=0A=
/// <summary>
/// Destructor - just calls Dispose()
/// </summary>=0A=
~TcpClient ()=0A=
{=0A=
Dispose(false);=0A=
}=0A=
=0A=
/// <returns>A NetworkStream object connected to the=0A=
/// connection socket</returns>=0A=
public NetworkStream GetStream()=0A=
{=0A=
return stream;=0A=
}=0A=
=0A=
}=0A=
}=0A=
------=_NextPart_000_0075_01C172EC.3C8F09E0
Content-Type: text/plain;
name="TcpListenerTest.cs"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="TcpListenerTest.cs"
// System.Net.Sockets.TcpListenerTest.cs
//
// Author:=0A=
// Phillip Pearson (pp@myelin.co.nz)=0A=
//=0A=
// Copyright (C) 2001, Phillip Pearson=0A=
// http://www.myelin.co.nz=0A=
//=0A=
using System;
using System.Net;
using System.Net.Sockets;
using NUnit.Framework;
namespace MonoTests.System.Net.Sockets {
/// <summary>
/// Tests System.Net.Sockets.TcpListener
/// </summary>
public class TcpListenerTest : TestCase {
=09
public TcpListenerTest(string name) : base(name) {}
public static ITest Suite {
get {
return new TestSuite(typeof (TcpListenerTest));
}
}
/// <summary>
/// Tests the TcpListener object
/// (from System.Net.Sockets)
/// </summary>
public void test_TcpListener()
{
// listen with a new listener
TcpListener inListener =3D new TcpListener(1234);
inListener.Start();
=09
// connect to it from a new socket
Socket outSock =3D new Socket(AddressFamily.InterNetwork, =
SocketType.Stream,
ProtocolType.IP);
IPHostEntry hostent =3D Dns.GetHostByAddress("127.0.0.1");
IPEndPoint remote =3D new IPEndPoint(hostent.AddressList[0], 1234);
outSock.Connect(remote);
=09
// make sure the connection arrives
Assert(inListener.Pending());
Socket inSock =3D inListener.AcceptSocket();
// now send some data and see if it comes out the other end
const int len =3D 1024;
byte[] outBuf =3D new Byte[len];
for (int i=3D0; i<len; i++)=20
{
outBuf[i] =3D (byte)(i % 256);
}
outSock.Send(outBuf, 0, len, 0);
byte[] inBuf =3D new Byte[len];
int ret =3D inSock.Receive(inBuf, 0, len, 0);
// let's see if it arrived OK
Assert(ret !=3D 0);
for (int i=3D0; i<len; i++)=20
{
Assert(inBuf[i] =3D=3D outBuf[i]);
}
// tidy up after ourselves
inSock.Close();
inListener.Stop();=09
}
=09
=09
}
}
------=_NextPart_000_0075_01C172EC.3C8F09E0
Content-Type: text/plain;
name="TcpListener.cs"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="TcpListener.cs"
// System.Net.Sockets.TcpListener.cs=0A=
//=0A=
// Author:=0A=
// Phillip Pearson (pp@myelin.co.nz)=0A=
//=0A=
// Copyright (C) 2001, Phillip Pearson=0A=
// http://www.myelin.co.nz=0A=
//=0A=
=0A=
// NB: This is untested (probably buggy) code - take care using it=0A=
=0A=
using System;=0A=
using System.Net;=0A=
=0A=
namespace System.Net.Sockets=0A=
{=0A=
/// <remarks>=0A=
/// A slightly more abstracted way to listen for incoming=0A=
/// network connections than a Socket.=0A=
/// </remarks>=0A=
public class TcpListener=0A=
{=0A=
// private data=0A=
=0A=
private bool active;=0A=
private Socket server;=0A=
=0A=
// constructor=0A=
=0A=
/* TODO: Code common to all the constructors goes here. I can't=0A=
call a constructor from another constructor, for some=0A=
reason. Why? */=0A=
=0A=
/// <summary>
/// Some code that is shared between the constructors.
/// </summary>=0A=
private void common_constructor ()=0A=
{=0A=
active =3D false;=0A=
server =3D new Socket(AddressFamily.InterNetwork,=0A=
SocketType.Stream, ProtocolType.Tcp);=0A=
}=0A=
=0A=
/// <summary>
/// Constructs a new TcpListener to listen on a specified port
/// </summary>
/// <param name=3D"port">The port to listen on, e.g. 80 if you=20
/// are a web server</param>
public TcpListener (int port)=0A=
{=0A=
common_constructor();=0A=
server.Bind(new IPEndPoint(IPAddress.Any, port));=0A=
}=0A=
=0A=
/// <summary>
/// Constructs a new TcpListener with a specified local endpoint
/// </summary>
/// <param name=3D"local_end_point">The endpoint</param>
public TcpListener (IPEndPoint local_end_point)=0A=
{=0A=
common_constructor();=0A=
server.Bind(local_end_point);=0A=
}=0A=
=0A=
/// <summary>
/// Constructs a new TcpListener, listening on a specified port
/// and IP (for use on a multi-homed machine)
/// </summary>
/// <param name=3D"listen_ip">The IP to listen on</param>
/// <param name=3D"port">The port to listen on</param>=0A=
public TcpListener (IPAddress listen_ip, int port)=0A=
{=0A=
common_constructor();=0A=
server.Bind(new IPEndPoint(listen_ip, port));=0A=
}=0A=
=0A=
=0A=
// properties=0A=
=0A=
/// <summary>
/// A flag that is 'true' if the TcpListener is listening,
/// or 'false' if it is not listening
/// </summary>=0A=
protected bool Active=0A=
{=0A=
get { return active; }=0A=
}=0A=
=0A=
/// <summary>
/// The local end point
/// </summary>=0A=
public EndPoint LocalEndPoint=0A=
{=0A=
get { return server.LocalEndPoint; }=0A=
}=0A=
=0A=
/// <summary>
/// The listening socket
/// </summary>=0A=
protected Socket Server=0A=
{=0A=
get { return server; }=0A=
}=0A=
=0A=
=0A=
// methods=0A=
=0A=
/// <summary>
/// Accepts a pending connection=0A=
/// <returns>A Socket object for the new connection</returns>=0A=
public Socket AcceptSocket ()=0A=
{=0A=
return server.Accept();=0A=
}=0A=
=0A=
/// <summary>
/// Accepts a pending connection
/// </summary>
/// <returns>A TcpClient
/// object made from the new socket.</returns>=0A=
public TcpClient AcceptTcpClient ()=0A=
{=0A=
/* TODO: How do we set the socket in the new client,=0A=
without having tcpclient as our base class?=0A=
Does C# have something like 'friend'?=0A=
=0A=
The commented code below doesn't work because=0A=
TcpClient.Client is protected. If we derive=0A=
=0A=
*/=0A=
=0A=
TcpClient client =3D new TcpClient();=0A=
client.SetTcpClient(AcceptSocket());=0A=
return client;=0A=
}=0A=
=0A=
/// <summary>
/// Destructor - stops the listener listening
/// </summary>=0A=
~TcpListener ()=0A=
{=0A=
if (active =3D=3D true) {=0A=
Stop();=0A=
}=0A=
}=0A=
=0A=
/// <returns>
/// Returns 'true' if there is a connection waiting to be accepted
/// with AcceptSocket() or AcceptTcpClient().
/// </returns>=0A=
public bool Pending ()=0A=
{=0A=
return server.Poll(1000, SelectMode.SelectRead);=0A=
}=0A=
=0A=
/// <summary>
/// Tells the TcpListener to start listening.
/// </summary>=0A=
public void Start ()=0A=
{=0A=
server.Listen(-1); //TODO: How big a backlog should we specify? -1 =
=3D=3D MAX?=0A=
active =3D true;=0A=
}=0A=
=0A=
/// <summary>
/// Tells the TcpListener to stop listening and dispose
/// of all managed resources.
/// </summary>=0A=
public void Stop ()=0A=
{=0A=
server.Close();=0A=
}=0A=
=0A=
}=0A=
}=0A=
------=_NextPart_000_0075_01C172EC.3C8F09E0
Content-Type: text/plain;
name="TcpClientTest.cs"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="TcpClientTest.cs"
// System.Net.Sockets.TcpClientTest.cs
//
// Author:=0A=
// Phillip Pearson (pp@myelin.co.nz)=0A=
//=0A=
// Copyright (C) 2001, Phillip Pearson=0A=
// http://www.myelin.co.nz=0A=
//=0A=
using System;
using System.Net;
using System.Net.Sockets;
using NUnit.Framework;
namespace MonoTests.System.Net.Sockets {
/// <summary>
/// Tests System.Net.Sockets.TcpClient
/// </summary>
public class TcpClientTest : TestCase {
=09
public TcpClientTest(string name) : base(name) {}
=09
public static ITest Suite {
get {
return new TestSuite(typeof (TcpClientTest));
}
}
/// <summary>
/// Tests the TcpClient object
/// (from System.Net.Sockets)
/// </summary>
public void test_TcpClient()
{
// set up a listening Socket
Socket lSock =3D new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
=09
lSock.Bind(new IPEndPoint(IPAddress.Any, 1234));
lSock.Listen(-1);
// connect to it with a TcpClient
TcpClient outClient =3D new TcpClient("localhost", 1234);
Socket inSock =3D lSock.Accept();
=09
// now try exchanging data
NetworkStream stream =3D outClient.GetStream();
const int len =3D 1024;
byte[] outBuf =3D new Byte[len];
for (int i=3D0; i<len; i++)=20
{
outBuf[i] =3D (byte)(i % 256);
}
// send it
stream.Write(outBuf,0,len);
// and see if it comes back
byte[] inBuf =3D new Byte[len];
int ret =3D inSock.Receive(inBuf, 0, len, 0);
Assert(ret !=3D 0);
for (int i=3D0; i<len; i++)=20
{
Assert(inBuf[i] =3D=3D outBuf[i]);
}
=09
// tidy up
inSock.Close();
outClient.Close();
lSock.Close();
=09
}=09
=09
}
}
------=_NextPart_000_0075_01C172EC.3C8F09E0--