【问题标题】:C#: How to execute a HTTP request using sockets?C#:如何使用套接字执行 HTTP 请求?
【发布时间】:2019-11-06 18:03:50
【问题描述】:

我正在尝试使用套接字制作HTTP request。我的代码如下:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class test
{
    public static void Main(String[] args)
    {
        string hostName = "127.0.0.1";
        int hostPort = 9887;
        int response = 0;

        IPAddress host = IPAddress.Parse(hostName);
        IPEndPoint hostep = new IPEndPoint(host, hostPort);
        Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        sock.Connect(hostep);

        string request_url = "http://127.0.0.1/register?id=application/vnd-fullphat.test&title=My%20Test%20App";
        response = sock.Send(Encoding.UTF8.GetBytes(request_url));
        response = sock.Send(Encoding.UTF8.GetBytes("\r\n"));

        bytes = sock.Receive(bytesReceived, bytesReceived.Length, 0);
        page = page + Encoding.ASCII.GetString(bytesReceived, 0, bytes);
        Console.WriteLine(page);
        sock.Close();
    }
}

现在,当我执行上面的代码时,什么都没有发生,而当我在浏览器中输入我的 request_url 时,我收到来自 Snarl 的通知,说 Application Registered 并且我从浏览器得到的响应是

SNP/2.0/0/OK/556

我从我的代码中得到的响应是SNP/3.0/107/BadPacket

那么,我的代码有什么问题。

Snarl Request format specification

【问题讨论】:

  • 您是否有理由需要使用 Sockets 而不是 System.Net.Http.HttpClient
  • 或 Webclient 就这点...
  • 不,没有具体原因。其实我不知道HttpCLient or Webclient。但无论如何,我想了解我上面的代码有什么问题。
  • 发送主url后是否需要额外发送“\r\n”?如果您不使用浏览器发送它,请尝试在代码中将其删除,以便复制与您的浏览器完全相同的行为。
  • 另外,正如 jgauffin 所说,您必须包含内容长度、主机等。如果您不想这样做,请使用高级对象发送,例如 HttpWebRequest。

标签: c# sockets


【解决方案1】:

我对 SNP 一无所知。您的代码在接收部分有点混乱。我使用下面的示例来发送和读取 HTTP GET 请求的服务器响应。首先让我们看一下请求,然后检查响应。

HTTP GET 请求:

GET / HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
Accept: text/html
User-Agent: CSharpTests

string - "GET / HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: keep-alive\r\nAccept: text/html\r\nUser-Agent: CSharpTests\r\n\r\n"

服务器 HTTP 响应头:

HTTP/1.1 200 OK
Date: Sun, 07 Jul 2013 17:13:10 GMT
Server: Apache/2.4.4 (Win32) OpenSSL/0.9.8y PHP/5.4.16
Last-Modified: Sat, 30 Mar 2013 11:28:59 GMT
ETag: \"ca-4d922b19fd4c0\"
Accept-Ranges: bytes
Content-Length: 202
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html

string - "HTTP/1.1 200 OK\r\nDate: Sun, 07 Jul 2013 17:13:10 GMT\r\nServer: Apache/2.4.4 (Win32) OpenSSL/0.9.8y PHP/5.4.16\r\nLast-Modified: Sat, 30 Mar 2013 11:28:59 GMT\r\nETag: \"ca-4d922b19fd4c0\"\r\nAccept-Ranges: bytes\r\nContent-Length: 202\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\n\r\n"

我故意省略了服务器响应的正文,因为我们已经知道它正好是 202 字节,由响应标头中的 Content-Length 指定。

查看 HTTP 规范会发现 HTTP 标头以空的新行 ("\r\n\r\n") 结尾。所以我们只需要搜索它。

让我们看看一些实际的代码。假设一个 System.Net.Sockets.Socket 类型的变量套接字。

socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect("127.0.0.1", 80);
string GETrequest = "GET / HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: keep-alive\r\nAccept: text/html\r\nUser-Agent: CSharpTests\r\n\r\n";
socket.Send(Encoding.ASCII.GetBytes(GETrequest));

我们已经将请求发送到服务器,让我们接收并正确解析响应。

bool flag = true; // just so we know we are still reading
string headerString = ""; // to store header information
int contentLength = 0; // the body length
byte[] bodyBuff = new byte[0]; // to later hold the body content
while (flag)
{
// read the header byte by byte, until \r\n\r\n
byte[] buffer = new byte[1];
socket.Receive(buffer, 0, 1, 0);
headerString += Encoding.ASCII.GetString(buffer);
if (headerString.Contains("\r\n\r\n"))
{
    // header is received, parsing content length
    // I use regular expressions, but any other method you can think of is ok
    Regex reg = new Regex("\\\r\nContent-Length: (.*?)\\\r\n");
    Match m = reg.Match(headerString);
    contentLength = int.Parse(m.Groups[1].ToString());
    flag = false;
    // read the body
    bodyBuff = new byte[contentLength];
    socket.Receive(bodyBuff, 0, contentLength, 0);
}
}
Console.WriteLine("Server Response :");
string body = Encoding.ASCII.GetString(bodyBuff);
Console.WriteLine(body);
socket.Close();

这可能是在 C# 中执行此操作的最糟糕的方法,在 .NET 中有大量类可以处理 HTTP 请求和响应,但如果您需要它,它仍然可以工作。

【讨论】:

    【解决方案2】:

    您必须在末尾包含内容长度和双新行以指示标题的结尾。

    var request = "GET /register?id=application/vnd-fullphat.test&title=My%20Test%20App HTTP/1.1\r\n" + 
        "Host: 127.0.0.1\r\n" +
        "Content-Length: 0\r\n" +
        "\r\n";
    

    HTTP 1.1 规范可以在这里找到:http://www.w3.org/Protocols/rfc2616/rfc2616.html

    【讨论】:

    • 谢谢伙计。正是我想要的。
    【解决方案3】:

    您的请求不正确。根据wikipedia, a HTTP Get request 必须看起来像:

    string request = "GET /register?id=application/vnd-fullphat.test&title=My%20Test%20App HTTP/1.1\r\nHost: 127.0.0.1\r\n";
    

    【讨论】:

    • 我仍然收到SNP/3.0/107/BadPacket
    • 听起来您的请求中需要更多内容,例如 Connection、Accept 或 Accept-Encoding。 PVitt 显示的是一个最小的请求,尽管这对于您的特定协议可能还不够。尝试按照上面的建议使用 WebClient 并查看它是否有效,这样您就可以确定 API 可以正常工作。
    • 根据接受的答案,它一定是Content-Length
    【解决方案4】:

    HTTP 请求的最低要求是 “GET / HTTP/1.0\r\n\r\n”(如果允许删除主机)。 但是在 SNARL 中,你必须输入 content-length(我听得最多的)。

    所以,

            Socket sck = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
    
            sck.Connect(ip, port);
            sck.Send(Encoding.UTF8.GetBytes("THE HTTP REQUEST HEADER"));
            Console.WriteLine("SENT");
            string message = null;
            byte[] bytesStored = new byte[sck.ReceiveBufferSize];
            int k1 = sck.Receive(bytesStored);
            for (int i = 0; i < k1; i++)
            {
                message = message + Convert.ToChar(bytesStored[i]).ToString();
            }
            Console.WriteLine(message); // PRINT THE RESPONSE
    

    我在几个网站上进行了测试,效果很好。如果它不起作用,那么您应该通过添加更多标题来修复它(最有可能的解决方案)。

    【讨论】:

      猜你喜欢
      • 2015-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-14
      相关资源
      最近更新 更多