Разобрался!
Маленькая тонкость - сервером выступает ElasticSearch.
Действительно, решение через
new Socket()
правильное. Но! Нужно ООООЧЕНЬ внимательно следить за тем, что пишется в заголовках, а особенно за заголовком "
Content-Length: NNN":
1. Обязательно его вставлять, даже если он не указан!
2. Правильно считать длину тела запроса. Не по символам, а по байтам.
Вот переработанный пример кода
MS из совета
Петр, который уже нормально заработал:
private void btnRequest_Click(object _sender, EventArgs e)
{
/* Текстовое поле, из которого я беру текст вида:
GET http://HOST:9200/_search HTTP/1.1
Host: HOST:9200
{
....
"highlight" : {
"fields" : {
"*" : {}
}
}
}
*/
string str = request_text.Text;
byte[] bytes = new byte[100024]; // Примерное число, которое надо поменять, если требуется! Мне для экспериментов подошло. В дальнейшем можно будет сделать какой-нибудь ByteStream
// Connect to a remote device.
try
{
string str_first = str.Split(new char[] { '\r', '\n' })[0];
string[] arr_str_first = str_first.Split(' ');
string[] arr_str_address = arr_str_first[1].Split('/');
Uri uri = (new Uri(arr_str_first[1]));
string new_str_first = arr_str_first[0] + " /" + arr_str_address.Last() + " " + arr_str_first[2];
// разделить запрос на две части - заголовки и тело:
string str_headers = str.Substring(str_first.Length + 2, str.IndexOf("\r\n\r\n") - str_first.Length - 2);
string str_body = str.Substring(str.IndexOf("\r\n\r\n") + 4);
int index_substring_content_length = str_headers.IndexOf("Content-Length:");
if (index_substring_content_length >= 0)
{
string tmp = str_headers.Substring(index_substring_content_length);
string[] arr_temp = tmp.Split(new char[] { '\r', '\n' });
string sub_string_content_length = arr_temp[0];
sub_string_content_length = "\r\n" + sub_string_content_length;
Regex regex = new Regex(sub_string_content_length);
str_headers = regex.Replace(str_headers, "", 1);
}
// Establish the remote endpoint for the socket.
IPHostEntry ipHostInfo = Dns.GetHostEntry(uri.Host);
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, uri.Port);
// Create a TCP/IP socket.
Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Connect the socket to the remote endpoint. Catch any errors.
try
{
sender.Connect(remoteEP);
Console.WriteLine("Socket connected to {0}", sender.RemoteEndPoint.ToString());
string raw_request = new_str_first+"\r\n"+str_headers+ "\r\nContent-Length: "+ Encoding.UTF8.GetBytes(str_body).Length+"\r\n\r\n"+str_body;
// Encode the data string into a byte array.
byte[] msg = Encoding.UTF8.GetBytes(raw_request); // + "<Client Quit>");
// Send the data through the socket.
int bytesSent = sender.Send(msg);
Console.WriteLine("bytesSent = {0}", bytesSent.ToString());
// Receive the response from the remote device.
int bytesRec = sender.Receive(bytes);
Console.WriteLine("Echoed test = {0}", Encoding.UTF8.GetString(bytes, 0, bytesRec)); // bytesRec));
// Release the socket.
sender.Shutdown(SocketShutdown.Both);
sender.Close();
}
catch (ArgumentNullException ane)
{
Console.WriteLine("ArgumentNullException : {0}", ane.ToString());
}
catch (SocketException se)
{
Console.WriteLine("SocketException : {0}", se.ToString());
}
catch (Exception ex)
{
Console.WriteLine("Unexpected exception : {0}", ex.ToString());
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Вот результат, который было нужно получить в ответе от сервера:
Результат получен.
P.S.
Понял, почему не получалось с самого начала. Посмотрев, как Fiddler2 достаточно вольно обращается с синтаксисом запросов в разделе Composer, я и подумал, что и мне так можно. Но, скорее всего, в фоне Fiddler2 всё-таки перерабатывает запрос, стараясь максимально приблизить его к стандарту. Вот пример разницы того, что записано в Composer уменя и что показывает Inspector: