[Rust] 26. 싱글 스레드 웹서버
싱글 스레드 기반에서 작동되는 웹서버를 간단히 작성하여 테스트 해보는 시간을 갖겠습니다.
웹서버에 구동되는 주요 프로토콜은 HTTP(Hypertext Transfer Protocol) 과 TCP(Transmission Control Protocol) 입니다.
TCP 프로토콜을 통하여 네트워크 접속 및 송수신에 대한 기본적인 하위레벨로 작동되며, HTTP 가 사용되는 코드로 웹브라우저가 출력되는 방식으로 구동됩니다.
이번 시간에는 TCP를 사용하기 위해서 std::net 모듈을 이용하여 해당 모듈내에 있는 TcpListener 를 사용하여 접속 및 송수신을 작성합니다.
Tcp 연결 처리
use std::net::TcpListener;
fn main()
{
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
for stream in listener.incoming()
{
let stream = stream.unwrap();
println!("연결 완료!");
}
}
간단히 tcp 사용법에 대한 샘플 코드입니다. tcp 요청에 의해 접속 요청이 들어오면 "연결완료!" 메시지를 확인할 수 있습니다.
요청 데이터 읽기
이번에는 직접 브라우저로 "127.0.0.1::7878" 을 치고 접속하여 요청되는 메시지를 확인해봅니다. 메시지를 받을 버퍼를 하나 만들고 클라이언트에서 요청된 메시지를 읽어서 출력해보면 아래와 같이 나옵니다.
use std::io::prelude::*;
use std::net::TcpStream;
use std::net::TcpListener;
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream);
}
}
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 512];
stream.read(&mut buffer).unwrap();
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
}
// 출력 결과
Request: GET / HTTP/1.1
Host: 127.0.0.1:7878
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
...
생략
http 프로토콜은 텍스트 기반으로 데이터 전송이 이루어지며 위와 같이 요청을 한다고 볼 수 있습니다.
응답 성공 메시지 전송
여기서 우리는 서버가 클라이언트에게 데이터를 잘 받았다는 성공 메시지를 돌려줘 봅시다.
아래는 HTTP 1.1 버전의 응답 예제로서 상태코드는 200, 설명문구는 OK, 헤더와 바디는 없습니다.
HTTP/1.1 200 OK \r\n\r\n
자 그럼 이 응답 성공 메시지를 돌려주는 코드를 작성합니다.
use std::io::prelude::*;
use std::net::TcpStream;
use std::net::TcpListener;
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream);
}
}
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 512];
stream.read(&mut buffer).unwrap();
let response = "HTTP/1.1 200 OK\r\n\r\n";
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
HTML 로 응답하기
실제로 웹서버처럼 html 파일을 작성하여 응답하는 기능을 만들어 봅시다. 메모장을 열고 아래와 같이 hello.html 을 작성하여 소스파일 루트 디렉토리에 저장합니다.
hello.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Hello Rust</title>
</head>
<body>
<h1>Hello Rust!</h1>
<p>This is rust web server test source.</p>
</body>
</html>
그 다음 응답 성공 메시지 뒤에 헤더와 바디 부분을 위 html 파일로 채워서 클라이언트에게 날려주면 끝입니다.
use std::fs::File;
use std::io::prelude::*;
use std::net::TcpStream;
use std::net::TcpListener;
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream);
}
}
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 512];
stream.read(&mut buffer).unwrap();
let mut file = File::open("hello.html").unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",
contents.len(),
contents
);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
웹 브라우저를 열고 아까와 같이 127.0.0.1:7878 로 접속해보세요, 여러분이 작성한 hello.html이 브라우저에 나타날 것입니다.!
'프로그래밍 언어 > Rust' 카테고리의 다른 글
[Rust] 25. 스레드 동기화 (0) | 2023.03.24 |
---|---|
[Rust] 24. 스레드 (0) | 2023.03.14 |
[Rust] 23. 파일 입출력 (0) | 2023.03.10 |
[Rust] 22. 커멘드라인 아규먼트 (0) | 2023.03.09 |
[Rust] 21. 테스트 코드 (0) | 2023.03.06 |