• 网络知识


    简易的自定义Web服务器

    基于浏览器向服务端发起请求

    两台主机各自的进程之间相互通信,需要协议、IP地址和端口号,IP表示了主机的网络地址,而端口号则表示了主机上的某个进程的地址,IP加Port统称为端点(EndPoint),在网络编程的世界里,.NET提供了Socket(套接字)类,此类处于传输层之中,Socket使开发人员可以以编程的方式侦听远程主机向本机发送的数据,并对到达传输层的数据包做出处理,同时它还可以向远程发送数据包。也即,Socket用于处理传输的数据。

    using System.Net;
    using System.Net.Sockets;

    namespace ConsoleHttp
    {
        class Program
        {
            static void Main( string[] args )
            {
                //本机IP
                IPAddress address = IPAddress.Loopback;
                //本程序的IP和端口(端点)
                IPEndPoint ipPoint = new IPEndPoint( address, 49155 );
                //ipv4
                var netWork = AddressFamily.InterNetwork;
                //创建Socket对象
                Socket socket = new Socket( netWork, SocketType.Stream, ProtocolType.Tcp );
                //将Socket绑定到端点
                socket.Bind( ipPoint );
                socket.Listen( 100 );//侦听请求的队列的最大长度为100
                while (true)
                {
                    Console.WriteLine( $"已开启侦听, 本机端点为: { ipPoint }  正在等待远程主机的请求……" );

                    //接收……

                    Socket clientSocket = socket.Accept( );//阻塞线程直到至少有一台远程主机发送的数据包被socket接收     
                    byte[] dataBuffer = new byte[1024];//数据存储区,最大存储1M的数据
                    int len = clientSocket.Receive( dataBuffer, 1024SocketFlags.None );//将接收的数据存入存储区,返回数据的字节长度
                    string DataStr = Encoding.UTF8.GetString( dataBuffer, 0, len );//将字节转换为字符串

                    Console.WriteLine( $"远程主机端点:{clientSocket.RemoteEndPoint}" );//输出远程主机的端点
                    Console.WriteLine( $"数据字节长度:{len}" ); //输出接收的数据的字节长度
                    Console.WriteLine( $"请求数据:  {DataStr}" ); //输出接收的数据

                    //响应……
                    string HttpDataLine = "HTTP/1.1 200 OK "//报文状态行
                    string HttpBody = "<html><head><title>Default Page</title></head><body><p style='font:bold;font-size:24pt'>寂静的春天</p></body></html>"//报文主体
                    string HttpHeader = $"Content-Type: text/html; charset=UTf-8  Content-Length: {HttpBody.Length} ";


                    byte[] HttpDataLineByte = Encoding.UTF8.GetBytes( HttpDataLine );
                    byte[] HttpBodyByte = Encoding.UTF8.GetBytes( HttpBody );
                    byte[] HttpHeaderByte = Encoding.UTF8.GetBytes( HttpHeader );
                    byte[] HttpNullLineByte = new byte[] { 1310 };

                    clientSocket.Send( HttpDataLineByte );
                    clientSocket.Send( HttpHeaderByte );
                    clientSocket.Send( HttpNullLineByte );
                    clientSocket.Send( HttpBodyByte );

                    //断开连接
                    clientSocket.Close( );              
                }
            }
        }
    }

    在浏览器输入端点进行访问,因为浏览器实已经实现了Http协议,浏览器处于应用层,封装好请求后会往下传递给传输层,封装TCP端口再传递给网络层直到请求发送至服务端,所以可以直接看到服务端返回的结果:

    TcpListener封装了Socket,所以也可以使用TcpListener来监听请求 

    using System.Net;
    using System.Net.Sockets;

    namespace ConsoleHttp
    {
        class Program
        {
            static void Main( string[] args )
            {
                //本机IP
                IPAddress address = IPAddress.Loopback;
                //本程序的IP和端口(端点)
                IPEndPoint ipPoint = new IPEndPoint( address, 49155 );
                //ipv4
                var netWork = AddressFamily.InterNetwork;
                //创建Tcp监听
                TcpListener tcp = new TcpListener( ipPoint );
                tcp.Start( );
              
                while (true)
                {
                    Console.WriteLine( $"已开启侦听, 本机端点为: { ipPoint }  正在等待远程主机的请求……" );
                    //接收……
                    TcpClient clientTcp = tcp.AcceptTcpClient( );
                    if(clientTcp.Connected)
                    {
                        Console.WriteLine( "连接已经建立……" );
                        NetworkStream networkStream = clientTcp.GetStream( ); //此类可自动从Socket中读取远程主机发起的请求数据,也可以输出数据

                        byte[] dataBuffer = new byte[1024];//数据存储区,最大存储1M的数据
                        int len = networkStream.Read(dataBuffer,0,1024);//将接收的数据存入存储区,返回数据的字节长度
                        string DataStr = Encoding.UTF8.GetString( dataBuffer, 0, len );//将字节转换为字符串

                        Console.WriteLine( $"远程主机端点:{clientTcp.Client.RemoteEndPoint}" );//输出远程主机的端点
                        Console.WriteLine( $"数据字节长度:{len}" ); //输出接收的数据的字节长度
                        Console.WriteLine( $"请求数据:  {DataStr}" ); //输出接收的数据

                        //响应……
                        string HttpDataLine = "HTTP/1.1 200 OK "; //报文状态行
                        string HttpBody = "<html><head><title>Default Page</title></head><body><p style='font:bold;font-size:24pt'>寂静的春天</p></body></html>"; //报文主体
                        string HttpHeader = $"Content-Type: text/html; charset=UTf-8  Content-Length: {HttpBody.Length} ";//报头

                        byte[] HttpDataLineByte = Encoding.UTF8.GetBytes( HttpDataLine );
                        byte[] HttpBodyByte = Encoding.UTF8.GetBytes( HttpBody );
                        byte[] HttpHeaderByte = Encoding.UTF8.GetBytes( HttpHeader );
                        byte[] HttpNullLineByte = new byte[] { 13, 10 };

                        networkStream.Write( HttpDataLineByte, 0, HttpDataLineByte.Length );
                        networkStream.Write( HttpHeaderByte, 0, HttpHeaderByte.Length );
                        networkStream.Write( HttpNullLineByte, 0, HttpNullLineByte.Length );
                        networkStream.Write( HttpBodyByte, 0, HttpBodyByte.Length );
                    }
                    //断开连接
                    clientTcp.Close( );              
                }
            }
        }
    }
    View Code

     

    基于windows窗体实现双方发送即时通信 

    分别创建两个windows窗体项目,命名为TCPServer和TCPClient。两个项目的窗体控件的名称是一样的,如下:

    服务端通过TcpListener开启监听,然后通过开启新的线程并使用TcpListener的AcceptTcpClient方法去监听客户端的请求,而客户端则开启新线程并通过TcpClient发起远程连接请求。这样双方就可以建立一个连接。接着,服务端的AcceptTcpClient方法会阻塞线程直到接受到一个请求为止,此时它会返回一个NetworkStream实例,此类提供了读取远程数据、发送数据的方法,此后,双方的互动都是通过这个唯一的NetworkStream实例的方法(Read、Write)来完成,发送数据和接收数据时都使用新线程来处理,并且应将发送数据和接收数据的逻辑都放入try块,这样一旦互动过程出现异常则可以关闭当前的Tcp连接、清空NetworkStream资源,然后服务端重新开启新线程继续监听客户端的连接请求,而客户端则重新发送远程连接的请求即可。

    服务端源码

     
    using System.Threading;
    using System.Net;
    using System.Net.Sockets;
    using System.IO;

    namespace TCPServer
    {
        public partial class Server : Form
        {
            public const int Port = 51388;
            public TcpListener lister;
            public TcpClient client;
            public IPAddress ipAddress;
            public NetworkStream networkStream;
            public BinaryReader reader;
            public BinaryWriter writer;
            public IPEndPoint ipPoint;
            public delegate void ShowStatusMessagestring msg );
            public ShowStatusMessage showStatusMessage;
            public delegate void ShowGetOrSendMessagestring msg );
            public ShowGetOrSendMessage showGetOrSendMessage;

            //构造器
            public Server( )
            {
                InitializeComponent( );          

                //状态栏信息和公共消息框信息
                showStatusMessage = new ShowStatusMessage( ShowStatusCallBack );
                showGetOrSendMessage = new ShowGetOrSendMessage( ShowGetOrSendCallBack );

                //本机IP
                IPAddress address = IPAddress.Loopback;
                //端点
                ipPoint = new IPEndPoint( address, Port );
                //创建Socket监听
                lister = new TcpListener( ipPoint );

                //显示本机IP和端口
                IPAddressBox.ReadOnly = true;
                PortBox.ReadOnly = true;
                IPAddressBox.Text = address.ToString( );
                PortBox.Text = Port.ToString();
            }

            //状态栏显示目前的连接状态和数据发送、接收的状态
            public void ShowStatusCallBack( string msg )
            {
                toolStripStatusLabel.Text = msg;
            }

            //设置公共消息框的数据
            public void ShowGetOrSendCallBack(string msg)
            {
                ShowMessageBox.Text +=$" 来自{client.Client.RemoteEndPoint}的消息:";
                ShowMessageBox.Text += " " + msg;
            }

            //开启监听
            private void TcpListenStart_Click( object sender, EventArgs e )
            {
                lister.Start( );
                //开启新线程
                Thread thread = new Thread( Request );
                thread.Start( );
            }        

            //接收请求
            private void Request( )
            {
                statusStrip.Invoke( showStatusMessage, "正在监听……" );
                Thread.Sleep( 1000 );
                
                try
                {
                    statusStrip.Invoke( showStatusMessage, "等待连接……" );
                    client = lister.AcceptTcpClient( ); //阻塞线程,接收队列中的客户端请求
                    if (client!=null)
                    {
                        statusStrip.Invoke( showStatusMessage, "连接已经建立……" );
                        networkStream = client.GetStream( );
                        reader = new BinaryReader( networkStream );
                        writer = new BinaryWriter( networkStream );
                    }
                }
                catch 
                {
                    statusStrip.Invoke( showStatusMessage, "连接失败……" );
                }
            }

            //接收消息
            private void GetMessage_Click( object sender, EventArgs e )
            {
                try
                {
                    statusStrip.Invoke( showStatusMessage, "消息接收中……" );
                    ShowMessageBox.Invoke( showGetOrSendMessage, reader.ReadString( ) );//在创建"公共消息框控件"的线程上调用showGetOrSendMessage委托来显示消息  
                }
                catch
                {
                    //如果出现异常则关闭现有连接,清除所有资源
                    statusStrip.Invoke( showStatusMessage, "对方没有发送消息或接收消息失败……" );            
                    if (client != null) client.Close( );
                    if (reader != null) reader.Close( );
                    if (writer != null) writer.Close( );
                    statusStrip.Invoke( showStatusMessage, "连接已经断开……" );
                    //重新开启新线程来接收请求
                    Thread thread = new Thread( Request );
                    thread.Start( );
                }
            }

            //发送消息
            private void SenMessage_Click( object sender, EventArgs e )
            {           
                string senMsg = SendMessageBox.Text;
                if(senMsg == string.Empty)
                {
                    MessageBox.Show("发送的消息不能为空" );
                    return;
                }

                statusStrip.Invoke( showStatusMessage, "正在发送消息……" );
                Thread proxyThread = new Thread( ( ) =>
                {
                    try
                    {
                        writer.Write( senMsg );
                        Thread.Sleep( 3000 ); //模拟发送延时
                        writer.Flush( );
                        statusStrip.Invoke( showStatusMessage, "消息发送成功……" );
                        ShowMessageBox.Invoke( showGetOrSendMessage, senMsg );//在创建"公共消息框控件"的线程上调用showGetOrSendMessage委托来显示消息
                    }
                    catch 
                    {
                        //如果出现异常则需要关闭现有连接,清除所有资源后重新开始
                        statusStrip.Invoke( showStatusMessage, "消息发送失败……" );
                        if (client != null) client.Close( );
                        if (reader != null) reader.Close( );
                        if (writer != null) writer.Close( );
                        //重新开启新线程来接收请求
                        Thread thread = new Thread( Request );
                        thread.Start( );
                    }
                } );

                proxyThread.Start( );
            }

            //关闭监听
            private void CloseTcpListen_Click( object sender, EventArgs e )
            {
                if (client != null) client.Close( );
                if (reader != null) reader.Close( );
                if (writer != null) writer.Close( );
                lister.Stop( );
                statusStrip.Invoke( showStatusMessage, "监听已经关闭……" );
            }

            //断开连接
            private void NoConnect_Click( object sender, EventArgs e )
            {
                if (client != null) client.Close( );
                if (reader != null) reader.Close( );
                if (writer != null) writer.Close( );
                statusStrip.Invoke( showStatusMessage, "连接已断开……" );
            }

            //清空消息
            private void ClearMessage_Click( object sender, EventArgs e )
            {
                ShowMessageBox.Clear( );
            }

            //点击关闭窗口按钮时,关闭TCP侦听,否则它会一直开启
            private void Server_FormClosing( object sender, FormClosingEventArgs e )
            {
                lister.Stop( );
            }
        }
    }

    客户端源码

    using System.Threading;
    using System.Net;
    using System.Net.Sockets;
    using System.IO;

    namespace TCPClient
    {
        public partial class Client : Form
        {
            public TcpClient server;
            public IPAddress ipAddress;
            public NetworkStream networkStream;
            public BinaryReader reader;
            public BinaryWriter writer;
            public IPEndPoint ipPoint;
            public delegate void ShowStatusMessagestring msg );
            public ShowStatusMessage showStatusMessage;
            public delegate void ShowGetOrSendMessagestring msg );
            public ShowGetOrSendMessage showGetOrSendMessage;

            //构造器
            public Client( )
            {
                InitializeComponent( );

                //状态栏信息和公共消息框信息
                showStatusMessage = new ShowStatusMessage( ShowStatusCallBack );
                showGetOrSendMessage = new ShowGetOrSendMessage( ShowGetOrSendCallBack );
            }

            //状态栏显示目前的连接状态和数据发送、接收的状态
            public void ShowStatusCallBack( string msg )
            {
                toolStripStatusLabel.Text = msg;
            }

            //设置公共消息框的数据
            public void ShowGetOrSendCallBack( string msg )
            {
                ShowMessageBox.Text += $" 来自{server.Client.RemoteEndPoint}的消息:";
                ShowMessageBox.Text += " " + msg;
            }

            //连接服务器
            private void ConnectToServer_Click( object sender, EventArgs e )
            {            
                if (ServerIP.Text == string.Empty || Port.Text == string.Empty)
                {
                    MessageBox.Show( "IP和端口都必须提供" );
                    return;
                }
                string IPData = ServerIP.Text;
                int PortData = Convert.ToInt32( Port.Text );

                //开启新线程发起连接请求
                Thread thread = new Thread(()=>
                {
                    statusStrip.Invoke( showStatusMessage, "正在连接服务器……" );
                    try
                    {
                        server = new TcpClient( );//服务端开启了TCP监听,正等待着接收客户端的连接请求,此处根据服务端的端点调用连接方法,就可以建立连接
                        server.Connect( IPData, PortData );
                        Thread.Sleep( 1000 ); //模拟发送延时
                        if (server != null)
                        {
                            statusStrip.Invoke( showStatusMessage, "连接服务器成功……" );
                            networkStream = server.GetStream( ); //接收到ASK数据包后会得到一个NetworkStream,利用它可以读取远程数据或向远程发送数据
                            reader = new BinaryReader( networkStream );
                            writer = new BinaryWriter( networkStream );
                        }
                    }
                    catch
                    {
                        statusStrip.Invoke( showStatusMessage, "连接服务器失败……" );
                    }
                } );
                thread.Start( );
            }

            //接收消息
            private void GetMessage_Click( object sender, EventArgs e )
            {
                //开启新线程来接收远程消息
                Thread proxyThread = new Thread( ( ) =>
                {
                    statusStrip.Invoke( showStatusMessage, "消息接收中……" );
                    try
                    {
                        ShowMessageBox.Invoke( showGetOrSendMessage, reader.ReadString( ) );
                        statusStrip.Invoke( showStatusMessage, "消息接收成功……" );
                    }
                    catch
                    {
                        //如果出现异常则关闭现有连接,清除所有资源
                        statusStrip.Invoke( showStatusMessage, "对方没有发送消息或接收消息失败……" );
                        if (server != null) server.Close( );
                        if (reader != null) reader.Close( );
                        if (writer != null) writer.Close( );
                        statusStrip.Invoke( showStatusMessage, "连接已经断开,请重新点击'连接服务器'按钮……" );
                    }
                } );
                proxyThread.Start( );
            }

            //发送消息
            private void SenMessage_Click( object sender, EventArgs e )
            {
                if (SendMessageBox.Text == string.Empty)
                {
                    MessageBox.Show( "发送消息不能为空" );
                }
                string sendMsg = SendMessageBox.Text;

                //开启新线程来发送消息
                Thread proxyThread = new Thread( ( ) =>
                {
                    try
                    {
                        statusStrip.Invoke( showStatusMessage, "消息发送中……" );                    
                        writer.Write( sendMsg );
                        writer.Flush( );
                        Thread.Sleep( 2000 );
                        ShowMessageBox.Invoke( showGetOrSendMessage, sendMsg );
                        statusStrip.Invoke( showStatusMessage, "消息发送成功……" );
                    }
                    catch
                    {
                        //如果出现异常则关闭现有连接,清除所有资源
                        statusStrip.Invoke( showStatusMessage, "消息发送失败……" );
                        if (server != null) server.Close( );
                        if (reader != null) reader.Close( );
                        if (writer != null) writer.Close( );
                        statusStrip.Invoke( showStatusMessage, "连接已经断开,请重新点击'连接服务器'按钮……" );
                    }
                } );
                proxyThread.Start( );
            }

            //断开连接
            private void NoConnect_Click( object sender, EventArgs e )
            {
                if (server != null) server.Close( );
                if (reader != null) reader.Close( );
                if (writer != null) writer.Close( );
                statusStrip.Invoke( showStatusMessage, "连接已断开……" );
            }

            //清空消息
            private void ClearMessage_Click( object sender, EventArgs e )
            {
                ShowMessageBox.Clear( );
            }

            //关闭窗口
            private void CloseWin_Click( object sender, EventArgs e )
            {
                this.Close( );
            }
        }
    }

    参考资料

    msdn:基于TCP协议的简单通信程序

     网络知识 - 学习总目录 

     

  • 相关阅读:
    grunt安装
    RequireJS实例分析
    Linux下解压rar文件
    windows(64位)下使用curl命令
    RequireJS学习资料汇总
    Linux下firefox安装flash player插件
    caj转pdf——包含下载链接
    《社会化营销:人人参与的营销力量》—— 读后总结
    《税的真相》—— 读后总结
    基于代理的数据库分库分表框架 Mycat实践
  • 原文地址:https://www.cnblogs.com/myrocknroll/p/8458609.html
Copyright © 2020-2023  润新知