잊지 않겠습니다.

: RouteBase, Route, RouteCollection

응용프로그램은 하나의 System.Web.Routing.RouteTable.Routes를 가지며, 응용프로그램의
실제 동작하는 Routing 구성 설정을 갖는다.

ASP.NET MVC에서 구성되는 Global.asax에서 application_start()로 구현이 된다.


Route의 동작

1. Url 요청
2. UrlRoutingModule module 호출(System.Web.Routing)
3. System.Web.Routing.RouteTable.Routes 안에서 요청과 일치하는 RouteBase 객체 반환
4. RouteData 구조체 확인
   1) RouteHandler : 요청을 처리할 객체 (ASP .NET MVC에서는 MvcRouteHandler의 instance가 된다.)
   2) RouteValueDictionary : 요청에서 추출된 중괄호 매개변수의 이름과 값의 dictionary
   3) Route : RouteBase에 대한 참조 객체
   4) DataTokens : Routing Entry에 의해 제공되는 모든 추가적인 구성 설정 옵션의 Dictionary
5. RouteData의 RouteHandler 호출
   : RouteHandler에 RouteData, Http Header, Cookie, Auth, Query string, post data를 포함한 모든 HttpContextBase전달

* Route Entry의 순서는 매우 중요하다. System.Web.Routing.RouteTable.Routes는 정렬된 목록이 제공되며,
  이는 Global.asxs 파일안에 정의되어있는 순서대로 구성이 되게 된다. 보다 명확한 매치에 해당되는 Route를
  먼저 등록시켜야지 된다.

* Server의 HDD에 있는 경우에는 언제나 그 정적인 파일이 우선적으로 호출된다. Routing 구성 설정보다
  우선권을 갖게 된다.


기본 Routing 작성


Route defaultRoute = new Route("{controller}/{action}/{id}", new MvcRouteHandler())
{                
    Defaults = new RouteValueDictionary(new { controller = "Home", action = "index", id = "" })
};
routes.Add("default", defaultRoute);

routes.Add(new Route("Catalog/{color}", new MvcRouteHandler())
{
    Defaults = new RouteValueDictionary(
        new { controller = "Products", action = "List", color=(string)null }
        )                
});


제약사항이 있는 Routing 작성
: Constraint를 이용. 제약 사항을 추가적으로 만들어주기 위해서는 System.Web.Routing.IRouteConstraint를
  상속받는 Class를 제공해서 RouteValueDictionary에 넣어주면 된다.


routes.Add(new Route("Article/{id}", new MvcRouteHandler())
{
    Defaults = new RouteValueDictionary(new { controller = "Articles", action = "Show" }),
    Constraints = new RouteValueDictionary(
            new
            {
                id = @"\d{1,6}", //ID 값이 항시 정규표현식 안에 일치하는지 제약조건 강제화.
                httpMethod = new HttpMethodConstraint("GET"), //항시 GET method로만 call이 가능
                userAgent = new UserAgentConstraint("iPhone") //iPhone web agent로만 접근 가능 
            }
        )
});

public class UserAgentConstraint : IRouteConstraint
{
    private string _requestSubString;
    public UserAgentConstraint(string requestSubString)
    {
        _requestSubString = requestSubString;
    }

    #region IRouteConstraint Members

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        if(httpContext.Request.UserAgent == null)
            return false;
        return httpContext.Request.UserAgent.Contains(_requestSubString);
    }

    #endregion
}


* 현재 사용의 매개변수는 재사용될 수 있다는 점을 염두해둬야지 된다.이 때 고려될 것은 매개변수보다
  앞쪽에 나오는 매개변수에 대한 값들만이 재사용된다.
  routes.MapRoute(null, "{controller}/{action}/{color}/{page}");  의 경우에,
  <%=Html.ActionLink("ClickMe", "List", "Catalog", new {page=789}, null)%>로 구성이 될 경우에는 color
  값은 ActionLink가 호출이 되는 현재 요청의 color 값이 사용이 된다. 그러나,
  <%=Html.ActionLink("ClickMe", "List", "Catalog", new {color="black"},null%>로 구성이 될 경우에는
  page값이 재사용되지 못하고 route entry는 일치되지 못한다.


Posted by Y2K
,
Client 소스는 좀더 간단하다. Server의 경우에는 Bind와 Listen의 절차를 거치고, Listen에서 BeginListen, EndListen으로
AsyncCallback을 통해야지 되지만, Client의 경우에는 간단히 Connect만으로 가능하고, 하나의 Client에서는 역시 하나의
연결정도만 신경을 쓰면 되기 때문에 더욱더 편한 개발이 가능하다.

솔찍한 생각으로 예제 소스이긴 하지만, UI단만 조금 붙여버리면 거의 일반 프로그램들과 큰 차이가 안보인다.
Socket의 Close시에 Shutdown으로 Socket을 정리하는 부분에 신경을 쓰면 될 것 같다.


/****************************** Module Header ******************************\
* Module Name:	Program.cs
* Project:		CSSocketClient
* Copyright (c) Microsoft Corporation.
* 
* Sockets are an application programming interface(API) in an operating system
* used for in inter-process communication. Sockets constitute a mechanism for
* delivering incoming data packets to the appropriate application process or
* thread, based on a combination of local and remote IP addresses and port
* numbers. Each socket is mapped by the operational system to a communicating
* application process or thread.
*
*
* .NET supplies a Socket class which implements the Berkeley sockets interface.
* It provides a rich set of methods and properties for network communications. 
* Socket class allows you to perform both synchronous and asynchronous data
* transfer using any of the communication protocols listed in the ProtocolType
* enumeration. It supplies the following types of socket:
*
* Stream: Supports reliable, two-way, connection-based byte streams without 
* the duplication of data and without preservation of boundaries.
*
* Dgram:Supports datagrams, which are connectionless, unreliable messages of
* a fixed (typically small) maximum length. 
*
* Raw: Supports access to the underlying transport protocol.Using the 
* SocketTypeRaw, you can communicate using protocols like Internet Control 
* Message Protocol (Icmp) and Internet Group Management Protocol (Igmp). 
*
* Rdm: Supports connectionless, message-oriented, reliably delivered messages, 
* and preserves message boundaries in data. 
*
* Seqpacket:Provides connection-oriented and reliable two-way transfer of 
* ordered byte streams across a network.
*
* Unknown:Specifies an unknown Socket type.
* 
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
* 
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/

#region Using directives
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Security;
using System.Security.Permissions;
#endregion


public class Program
{
    static void Main(string[] args)
    {
        // Receiving byte array 
        byte[] bytes = new byte[1024];
        try
        {
            // Create one SocketPermission for socket access restrictions
            SocketPermission permission = new SocketPermission(
                NetworkAccess.Connect,    // Connection permission
                TransportType.Tcp,        // Defines transport types
                "",                       // Gets the IP addresses
                SocketPermission.AllPorts // All ports
                );

            // Ensures the code to have permission to access a Socket
            permission.Demand();

            // Resolves a host name to an IPHostEntry instance           
            IPHostEntry ipHost = Dns.GetHostEntry("");

            // Gets first IP address associated with a localhost
            IPAddress ipAddr = ipHost.AddressList[0];

            // Creates a network endpoint
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 6000);

            // Create one Socket object to setup Tcp connection
            Socket sender = new Socket(
                ipAddr.AddressFamily,// Specifies the addressing scheme
                SocketType.Stream,   // The type of socket 
                ProtocolType.Tcp     // Specifies the protocols 
                );

            sender.NoDelay = true;   // Using the Nagle algorithm

            // Establishes a connection to a remote host
            sender.Connect(ipEndPoint);
            Console.WriteLine("Socket connected to {0}",
                sender.RemoteEndPoint.ToString());

            // Sending message
            // is the sign for end of data
            string theMessage = "Hello World!";
            byte[] msg = 
                Encoding.Unicode.GetBytes(theMessage +"");

            // Sends data to a connected Socket.
            int bytesSend = sender.Send(msg);

            // Receives data from a bound Socket.
            int bytesRec = sender.Receive(bytes);

            // Converts byte array to string
            theMessage = Encoding.Unicode.GetString(bytes, 0, bytesRec);

            // Continues to read the data till data isn't available
            while (sender.Available > 0)
            {
                bytesRec = sender.Receive(bytes);
                theMessage += Encoding.Unicode.GetString(bytes, 0, bytesRec);
            }
            Console.WriteLine("The server reply: {0}", theMessage);

            // Disables sends and receives on a Socket.
            sender.Shutdown(SocketShutdown.Both);

            //Closes the Socket connection and releases all resources
            sender.Close();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception: {0}", ex.ToString());
        }
    }
}
Posted by Y2K
,
Socket을 사용해서 TCP/IP 서버를 만들게 되면 개발에서 가장 신경쓰게 되는 것이 Multi-Thread 문제이다.
이부분에 대해서 .NET에서 내놓은 최고의 Solution은 IAsyncCallback interface를 이용해서 BeginXXX 와
EndXXX를 이용하는 방법이다. 이 방법은 WebService client, Stream, Socket 등 데이터를 읽고, 쓰는데에
시간이 소요가 되어 Multi-Thread를 구현해야지 되는 거의 모든 경우에서 사용이 가능하다.

예제 소스는 다음과 같다. IPV6를 사용하는 소스를 IPV4로만 변경시킨 예제이다.


/****************************** Module Header ******************************\
* Module Name:	Program.cs
* Project:		CSSocketServer
* Copyright (c) Microsoft Corporation.
* 
* Sockets are an application programming interface(API) in an operating system
* used for in inter-process communication. Sockets constitute a mechanism for
* delivering incoming data packets to the appropriate application process or
* thread, based on a combination of local and remote IP addresses and port
* numbers. Each socket is mapped by the operational system to a communicating
* application process or thread.
*
*
* .NET supplies a Socket class which implements the Berkeley sockets interface.
* It provides a rich set of methods and properties for network communications. 
* Socket class allows you to perform both synchronous and asynchronous data
* transfer using any of the communication protocols listed in the ProtocolType
* enumeration. It supplies the following types of socket:
*
* Stream: Supports reliable, two-way, connection-based byte streams without 
* the duplication of data and without preservation of boundaries.
*
* Dgram:Supports datagrams, which are connectionless, unreliable messages of
* a fixed (typically small) maximum length. 
*
* Raw: Supports access to the underlying transport protocol.Using the 
* SocketTypeRaw, you can communicate using protocols like Internet Control 
* Message Protocol (Icmp) and Internet Group Management Protocol (Igmp). 
*
* Rdm: Supports connectionless, message-oriented, reliably delivered messages, 
* and preserves message boundaries in data. 
*
* Seqpacket:Provides connection-oriented and reliable two-way transfer of 
* ordered byte streams across a network.
*
* Unknown:Specifies an unknown Socket type.
* 
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
* 
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/

#region Using Directive
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Security;
using System.Security.Permissions;
#endregion


public class Program
{
    static void Main(string[] args)
    {
        // Creates one SocketPermission object for access restrictions
        SocketPermission permission = new SocketPermission(
            NetworkAccess.Accept,     // Allowed to accept connections
            TransportType.Tcp,        // Defines transport types
            "",                       // The IP addresses of local host
            SocketPermission.AllPorts // Specifies all ports
            );

        // Listening Socket object
        Socket sListener = null;

        try
        {
            // Ensures the code to have permission to access a Socket
            permission.Demand();

            // Resolves a host name to an IPHostEntry instance
            IPHostEntry ipHost = Dns.GetHostEntry("");

            ///Get First IPV4 Address
            ///아직까지는 IPV4로 만들지 않으면 외부에서 접근을 못하는 경우가 많다.;
            IPAddress ipAddr = ipHost.AddressList.Where(a => a.AddressFamily != AddressFamily.InterNetworkV6).First();

            // Creates a network endpoint
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 6000);

            // Create one Socket object to listen the incoming connection
            sListener = new Socket(
                ipAddr.AddressFamily,
                SocketType.Stream,
                ProtocolType.Tcp
                );

            // Associates a Socket with a local endpoint
            sListener.Bind(ipEndPoint);

            // Places a Socket in a listening state and specifies the maximum
            // Length of the pending connections queue
            sListener.Listen(10);

            Console.WriteLine("Waiting for a connection on port {0}",
                ipEndPoint);

            // Begins an asynchronous operation to accept an attempt
            AsyncCallback aCallback = new AsyncCallback(AcceptCallback);
            sListener.BeginAccept(aCallback, sListener);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception: {0}", ex.ToString());
            return;
        }

        Console.WriteLine("Press the Enter key to exit ...");
        Console.ReadLine();
        
        if (sListener.Connected)
        {
            sListener.Shutdown(SocketShutdown.Receive);
            sListener.Close();
        }
    }


    /// 
    /// Asynchronously accepts an incoming connection attempt and creates
    /// a new Socket to handle remote host communication.
    ///      
    /// the status of an asynchronous operation
    ///  
    public static void AcceptCallback(IAsyncResult ar)
    {
        Socket listener = null;

        // A new Socket to handle remote host communication
        Socket handler = null;
        try
        {
            // Receiving byte array
            byte[] buffer = new byte[1024];
            // Get Listening Socket object
            listener = (Socket)ar.AsyncState;
            // Create a new socket
            handler = listener.EndAccept(ar);

            // Using the Nagle algorithm
            handler.NoDelay = true;

            // Creates one object array for passing data
            object[] obj = new object[2];
            obj[0] = buffer;
            obj[1] = handler;

            // Begins to asynchronously receive data
            handler.BeginReceive(
                buffer,        // An array of type Byt for received data
                0,             // The zero-based position in the buffer 
                buffer.Length, // The number of bytes to receive
                SocketFlags.None,// Specifies send and receive behaviors
                new AsyncCallback(ReceiveCallback),//An AsyncCallback delegate
                obj            // Specifies infomation for receive operation
                );

            // Begins an asynchronous operation to accept an attempt
            AsyncCallback aCallback = new AsyncCallback(AcceptCallback);
            listener.BeginAccept(aCallback, listener);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception: {0}", ex.ToString());
        }
    }

    /// 
    /// Asynchronously receive data from a connected Socket.
    /// 
    /// 
    /// the status of an asynchronous operation
    ///  
    public static void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            // Fetch a user-defined object that contains information
            object[] obj = new object[2];
            obj = (object[])ar.AsyncState;

            // Received byte array
            byte[] buffer = (byte[])obj[0];

            // A Socket to handle remote host communication.
            Socket handler = (Socket)obj[1];

            // Received message
            string content = string.Empty;

            // The number of bytes received.
            int bytesRead = handler.EndReceive(ar);

            if (bytesRead > 0)
            {
                content += Encoding.Unicode.GetString(buffer, 0,
                    bytesRead);
                // If message contains "", finish receiving
                if (content.IndexOf("") > -1)
                {
                    // Convert byte array to string
                    string str =
                        content.TrimEnd(("").ToCharArray());
                    Console.WriteLine(
                        "Read {0} bytes from client.\n Data: {1}",
                        str.Length, str);

                    // Prepare the reply message
                    byte[] byteData =
                        Encoding.Unicode.GetBytes(str);

                    // Sends data asynchronously to a connected Socket
                    handler.BeginSend(byteData, 0, byteData.Length, 0,
                        new AsyncCallback(SendCallback), handler);
                }
                else
                {
                    // Continues to asynchronously receive data
                    byte[] buffernew = new byte[1024];
                    obj[0] = buffernew;
                    obj[1] = handler;
                    handler.BeginReceive(buffernew, 0, buffernew.Length,
                        SocketFlags.None,
                        new AsyncCallback(ReceiveCallback), obj);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception: {0}", ex.ToString());
        }
    }

    /// 
    /// Sends data asynchronously to a connected Socket.
    /// 
    /// 
    /// The status of an asynchronous operation
    ///  
    public static void SendCallback(IAsyncResult ar)
    {
        try
        {
            // A Socket which has sent the data to remote host
            Socket handler = (Socket)ar.AsyncState;

            // The number of bytes sent to the Socket
            int bytesSend = handler.EndSend(ar);
            Console.WriteLine(
                "Sent {0} bytes to Client",bytesSend);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception: {0}", ex.ToString());
        }
    }
}
Posted by Y2K
,