Socket을 이용한 HTTP 호출
Socket을 이용한 HTTP Raw Request
C#에서 HTTP Request를 보내는 방법으로 흔히 High 레벨 클래스로서 HttpWebRequest, WebClient, HttpClient
클래스들을 사용하는데, 여기서는 Low 레벨의 HTTP 네트워크 통신을 이해하기 위해
Socket 클래스는 사용하여 HTTP 프로토콜 Raw 데이타를 보내는 방법에 대해 소개한다.
먼저 HTTP Request에 대한 Raw 데이타를 얻기 위해서는 (HTTP 프로토콜에 맞게 Raw Request 문자열을 작성할 수도 있지만)
Fiddler와 같은 네트워크 트래픽 캡쳐 도구를 사용하면 편리하다.
Fiddler를 실행한 후, 브라우져에서 자신이 원하는 웹사이트를 방문하면, Fiddler가 브라우져에서 보내는 HTTP Request를
캡쳐할 수가 있다. 아래는 http://httpbin.org/get 를 방문했을 때의 HTTP Raw Request 데이타이다.
-
HTTP Request를 보낼 때, 위의 모든 라인이 필요한 것은 아니다.
-
HTTP 프로토콜 상 HTTP 헤더의 마지막에 반드시 빈 라인 하나를 추가해야 한다.
C#에서 HTTP Raw Request를 보내기 위해서 아래 예제와 같이 먼저 TCP/IP Socket을 생성하고 해당 웹사이트(httpbin.org)에
포트 80을 써서 접속한다. 다음 HTTP Raw 데이타 문자열을 만든 후, 소켓을 통해 보낸 후 HTTP Response 결과를 받으면 된다.
HTTP GET의 경우 통상 HTTP 헤더만 보내지만, POST와 같은 다른 HTTP Verb는 통상 헤더 밑에 HTTP Body를 가지게 된다.
void SendHttpTest()
{
// TCP/IP Socket 객체 생성
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 호스트를 IP로 변경
IPHostEntry hostEntry = Dns.GetHostEntry("httpbin.org");
IPAddress ip = hostEntry.AddressList[0];
// HTTP 서버 접속
var httpEndPoint = new IPEndPoint(ip, 80);
sock.Connect(httpEndPoint);
// HTTP header should end with double newline (\r\n\r\n)
string http = @"GET http://httpbin.org/get HTTP/1.1
Host: httpbin.org
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
";
// Send
var sendBuff = Encoding.ASCII.GetBytes(http);
sock.Send(sendBuff, SocketFlags.None);
// Receive
byte[] recvBuff = new byte[sock.ReceiveBufferSize];
int nCount = sock.Receive(recvBuff);
// 파일 저장
string result = Encoding.ASCII.GetString(recvBuff, 0, nCount);
File.WriteAllText("test.out", result);
sock.Close();
}
위 예제를 각 스텝별로 살펴보면,
-
먼저 TCP/IP Socket 객체를 생성한다.
-
httpbin.org 호스트에 대한 IP Address를 구한다.
-
Socket 객체의 Connect() 메서드를 호출하여 HTTP 서버에 포트 80을 통해 접속한다.
-
HTTP 호출에 사용되는 Raw Data 문자열을 만든다. HTTP 헤더의 마지막에는 빈 라인 하는 두어야 하는데,
이는 헤더가 끝났음을 가리키며, HTTP Body가 있을 경우 Header와 Body를 구분한다.
위 예제는 @ 을 사용하여 멀티라인 문자열을 사용하였지만,
위 표현은 아래와 같이 한 라인으로 변경할 수 있다.
string http = "GET http://httpbin.org/get HTTP/1.1\r\nHost: httpbin.org\r\n...(생략)...Accept-Language: en-US,en;q=0.9\r\n\r\n";
-
소켓을 통해 데이타를 보내기 위해 Socket 객체의 Send() 메서드를 사용한다.
-
소켓에서 데이타를 수신하기 위해 Socket 객체의 Receive() 메서드를 사용한다.
수신된 데이타는 아래와 같이 HTTP Response 형식을 따르는 데이타이다.
HTTP/1.1 200 OK
Connection: keep-alive
Server: gunicorn/19.8.1
Date: Tue, 24 Jul 2018 17:48:50 GMT
Content-Type: application/json
Content-Length: 445
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Via: 1.1 vegur
{"args":{},"headers":{"Accept":"text/html,application/xhtml+xml ... (생략)...
-
수신된 데이타를 파일에 저장한 후, 마지막으로 소켓을 닫는다.