views:

273

answers:

1

I'm having to write a TCP Server for a project at work and I googled around how to. I got an MSDN answer and managed to get it working, but I have no clue how to get data to be able to receive back and forth. The sockets aren't going to be connected more than about 30 seconds MAX (because my boss doesn't want sockets staying open on the computers here). The goal of this class is to provide a quick way for me in my main program to retrieve data about a computer and it's user.

By the namespace naming, you can probably guess it's for a computer lab which.

namespace LabAssist.Server.Common.Objects {
    using Atlantis.Net.Sockets;

    using LabAssist.Server.Common.Data;

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Windows.Forms;

    public class TcpServer {

        #region Constructor(s)

        public TcpServer(IPEndPoint endPoint) {
            RemoteEndPoint = endPoint;
            Host = new Socket(RemoteEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        }

        public TcpServer(String hostNameOrIpAddress, Int32 port) {
            RemoteEndPoint = new IPEndPoint(Dns.GetHostEntry(hostNameOrIpAddress).AddressList[0], port);
            Host = new Socket(RemoteEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        }

        public TcpServer(Int32 port) {
            RemoteEndPoint = new IPEndPoint(Dns.GetHostEntry(Dns.GetHostName()).AddressList[0], port);
            Host = new Socket(RemoteEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        }

        #endregion

        #region Fields

        private Boolean m_IsBound = false;
        private List<Socket> m_Connections = new List<Socket>(50); // allow 50 sockets 
        private static System.Threading.ManualResetEvent AllDone = new System.Threading.ManualResetEvent(false);

        #endregion

        #region Properties

        private Int32 m_Backlog = 32;
        /// <summary>
        ///     Gets or sets the number of connections the host can accept at any given point
        /// </summary>
        public Int32 Backlog {
            set {
                m_Backlog = value;
            }
            get {
                return m_Backlog;
            }
        }

        private Socket m_Host = null;
        /// <summary>
        ///     Gets or sets the host master socket
        /// </summary>
        public Socket Host {
            private set {
                m_Host = value;
            }
            get {
                return m_Host;
            }
        }

        private Int32 m_Port = 1337;
        /// <summary>
        ///     Gets or sets the binding port for the server
        /// </summary>
        public Int32 Port {
            set {
                m_Port = value;
            }
            get {
                return m_Port;
            }
        }

        private IPEndPoint m_EndPoint = null;
        /// <summary>
        ///     Gets or sets the binding address to be used when binding the socket
        /// </summary>
        public IPEndPoint RemoteEndPoint {
            private set {
                m_EndPoint = value;
            }
            get {               // follows a singleton pattern with a private-setter.
                if (m_EndPoint == null) {
                    RemoteEndPoint = new IPEndPoint(Dns.GetHostEntry(Dns.GetHostName()).AddressList[0], Port);
                }

                return m_EndPoint;
            }
        }

        #endregion

        #region Methods

        private void AcceptCallback(IAsyncResult ar) {
            Socket client = ((Socket)ar.AsyncState);
            Socket handler = client.EndAccept(ar);
            m_Connections.Add(handler);

            AllDone.Set();

            Console.WriteLine("Client accepted.\t Remote address and port : {0}", handler.RemoteEndPoint.ToString());

            Byte[] buf = Encoding.ASCII.GetBytes("hello world. This is my first TCP Server >:)");
            Int32 ret = 0;
            Boolean ext = false;
            //try-catch temporary until sending is figured out. ><
            try {
                ret = client.Send(buf, buf.Length, SocketFlags.None);
            } catch (Exception ex) {
                ext = true;
                ConsoleColor c = Console.ForegroundColor;
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine(ex.ToString());
                Console.ForegroundColor = c;
            }
            // error check for debugging
            if (ret > 0) {
                Console.WriteLine("Sent -> {0}", Encoding.ASCII.GetString(buf, 0, buf.Length));
            } else {
                if (ext) {
                    ConsoleColor c = Console.ForegroundColor;
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("Caught an exception");
                    Console.ForegroundColor = c;
                }
                Console.WriteLine("Failed to send welcome packet to client.");
            }

            State state = new State();
            state.WorkSocket = handler;
            handler.BeginReceive(state.Buffer, 0, State.BufferSize, 0, new AsyncCallback(RecieveDataCallback), state);
        }

        /// <summary>
        ///     Intialises the socket to listen and begins to accept connections
        /// </summary>
        /// <returns></returns>
        public void Initialise() {
            Host.Bind(RemoteEndPoint);

            Console.WriteLine("Local address and port : {0}", RemoteEndPoint.ToString());

            m_IsBound = true;
            Host.Listen(Backlog);
            try {
                while (true) {
                    AllDone.Reset();

                    Console.WriteLine("Awaiting{0} client connection...", (m_Connections.Count > 0 ? " another" : ""));

                    Host.BeginAccept(new AsyncCallback(AcceptCallback), Host);

                    AllDone.WaitOne();

                    Application.DoEvents();
                }
            } catch (Exception e) {
                Log.HandledException(e);
            }
        }

        private void RecieveDataCallback(IAsyncResult ar) {
            State state = ((State)ar.AsyncState);
            Socket handler = state.WorkSocket;

            if (!handler.IsConnected()) {
                return;
            }

            Int32 read = handler.EndReceive(ar);

            if (read > 0) {
                state.DataRecieved.Append(Encoding.ASCII.GetString(state.Buffer, 0, read));
                handler.BeginReceive(state.Buffer, 0, State.BufferSize, SocketFlags.None, new AsyncCallback(RecieveDataCallback), state);
            } else {
                if (state.DataRecieved.Length > 1) {
                    String content = state.DataRecieved.ToString();
                    Console.WriteLine("Read {0} bytes from socket.\n Data: {1}", content.Length, content);
                }
                handler.Close();
            }
        }

        #endregion
    }
}

At the moment, I'm trying to get a simple version working where on connect the client receives a packet from the server saying something like "hello world from the net!". I keep getting some weird exception. The code above is just about a direct "copy" (so to speak) from MSDN. I used MSDN's example as a guide when writing it and still have some work to do on it (as in remove the blocking-nature it uses). At the moment, I just want to know how to send data from the Host to a client that connects! :(

System.Net.Sockets.SocketException: A request to send or receive data was disall    owed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
   at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
   at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 size, SocketFlags socketFlags)
   at LabAssist.Server.Common.Objects.TcpServer.AcceptCallback(IAsyncResult ar) in F:\Source\ACCL\Lab Suite\Code\LabAssist.Server\Common\Objects\TcpServer.cs:li
ne 119

Thanks for the help in advance!
-Zack

+1  A: 

I would suggest using UDP rather than TCP with manual ACK commands, this way you don't get the constant connected sockets as per your boss. UDP Information

Another point I would like to make is to not use raw Socket as your first TCP connection, use the TCPClient class. TCPClient

Tom Anderson