.NET网络编程学习(二)--手写http服务器程序

.NET网络编程学习(二)
System.Net.Sockets有很多类,其中最重要的就是Socket类.

Socket类
public class Socket : IDisposable
Socket 类为网络通信提供了一套丰富的方法和属性。
Socket 类允许您使用 ProtocolType 枚举中所列出的任何一种协议执行异步和同步数据传输。 
public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolType);

其中:

addressFamily 参数指定Socket使用的寻址方案,比如AddressFamily.InterNetwork表明为IP版本4的地址;
socketType参数指定 Socket的类型,比如:
   SocketType.Stream表明连接是基于流套接字的,
   SocketType.Dgram表示连接是基于数据报套接字的。
protocolType参数指定Socket使用的协议,比如
  ProtocolType.Tcp表明连接协议是运用TCP协议的,
  Protocol.Udp则表明连接协议是运用UDP协议的。

Socket 类对异步方法遵循 .NET Framework 命名模式。
例如,同步的 Receive 方法对应于异步的 BeginReceive 和 EndReceive 方法。

(1)如果当前使用的是面向连接的协议(如 TCP),则服务器可以使用 Listen 方法侦听连接。Accept
方法处理任何传入的连接请求,并返回可用于与远程主机进行数据通信的 Socket。可以使用此返回的 Socket 来调用 Send 或 Receive 方法。 如果要指定本地 IP 地址和端口号,请在调用 Listen 方法之前先调用 Bind 方法。如果您希望基础服务提供程序为您分配可用端口,请使用端口号 0。如果希望连接到侦听主机,请调用 Connect 方法。 若要进行数据通信,请调用 Send 或 Receive 方法。

(2)如果当前使用的是无连接协议(如 UDP),则根本不需要侦听连接。 调用 ReceiveFrom 方法可接受任何传入的数据报。使用
SendTo 方法可将数据报发送到远程主机。

同步模式的Socket编程的基本过程如下:

(1)创建一个Socket实例对象。
(2)将上述实例对象连接到一个具体的终结点(EndPoint)。
(3)连接完毕,就可以和服务器进行通讯:接收并发送信息。
(4)通讯完毕,用ShutDown()方法来禁用Socket。
(5)最后用Close()方法来关闭Socket。

对于基于Tcp的Socket,涉及到服务器端的侦听(Listen)和客户端的连接(Connect)
以下通过一个简单的Http服务器程序来看看服务器端的程序设计:

(1)BaseConnection是个封装Socket表示一个连接的类.

public class BaseConnection
{
        Socket sock;
        public BaseConnection(Socket sock)
        {
            this.sock = sock;
        }
        //把客户端请求文件中的字符替换掉
        string ProcessString(string input)
        {
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < input.Length; i++)
            {
                if (input[i] == '/')
                {
                    result.Append('\\');
                }
                else
                {
                    result.Append(input[i]);
                }
            }
            return result.ToString();
        }
        /// <summary>
        /// 读取请求信息,返回客户端请求的文件名
        /// </summary>
        /// <returns></returns>
        public string getRequest()  
        {
            string s = null ;
            byte[] buff = new byte[1024];
            int recCount = 0;
            try
            {
                //从socket读取请求信息
                recCount = sock.Receive(buff, buff.Length, SocketFlags.None);
                s = ASCIIEncoding.ASCII.GetString(buff, 0, recCount);
                Console.WriteLine("请求的详细信息是:" + s);
                if (s.IndexOf("GET") > -1)
                {
                    s = s.Substring(5);
                    int fend = s.IndexOf(" ");
                    s = s.Substring(0, fend); //取得请求文件的路径及文件名
                }
                return ProcessString(s);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            return "";
        }
        /// <summary>
        /// 返回客户端请求的文件
        /// </summary>
        /// <param name="fname"></param>
        public void sendResponse(string fname) 
        {
            byte[] buff = new byte[1024];
            string filePath = @"C:\Inetpub\wwwroot\" + fname;

            int count = 0;
            if (filePath.IndexOf("..") > -1) //使访问的文件限制在目录C:\Inetpub\wwwroot\下
            {
                Console.WriteLine("没有权限访问!");
                return;
            }
            Console.WriteLine("look for " + filePath);
            if (File.Exists(filePath)) //检查文件是否存在
            {
                using (BinaryReader fs = new BinaryReader(File.Open(filePath, FileMode.Open)))
                {
                    while ((count = fs.Read(buff, 0, 1024)) > 0)
                    {
                        sock.Send(buff, count, SocketFlags.None);//发送信息
                    }
                }
            }
            else
            {
                throw new FileNotFoundException("文件:" + filePath + "不存在!");
            }
        }
        public void ConClose()  //关闭socket
        {
            sock.Shutdown(SocketShutdown.Both);
            sock.Close();
        }
}

(2)Main方法

 static void Main(string[] args)
        {
            Socket ssock = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);

            IPAddress hostIp = Dns.GetHostEntry("localhost").AddressList[0];
            IPEndPoint ep = new IPEndPoint(hostIp, 80);
            ssock.Bind(ep); //绑定

            Console.WriteLine("开始侦听.");
            //开始侦听
            ssock.Listen(32);
            while (true)
            {
                Socket sock = ssock.Accept(); //等待客户端请求

                Console.WriteLine("有一个客户端请求");
                BaseConnection client = new BaseConnection(sock);
                try
                {
                    string file = client.getRequest();
                    Console.WriteLine("客户端请求的文件是:" + file);
                    client.sendResponse(file);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
                finally
                {
                    client.ConClose(); //关闭连接
                }
                Console.WriteLine("结束一个请求");
                Console.WriteLine("");
            }
        }

(3)测试
假设在你C:\Inetpub\wwwroot目录下有一个info.html
启动服务器端:
在这里插入图片描述
打开浏览器输入:http://localhost/info.html

结果如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/7374f0c2681c45d99ba18634ab4109d4.png

虽然很简单,但我们却的确写了一个http服务器程序.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是刘彦宏吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值