-
Notifications
You must be signed in to change notification settings - Fork 1
소켓 프로그래밍
jeongmin edited this page Feb 2, 2024
·
1 revision
I/O의 Sync와 Blocking 그리고 Multiplexing
[네이버클라우드 기술&경험] IO Multiplexing (IO 멀티플렉싱) 기본 개념부터 심화까지 -1부-
[네이버클라우드 기술&경험] IO Multiplexing (IO 멀티플렉싱) 기본 개념부터 심화까지 -2부-
#include <arpa/inet.h> // 네트워크 주소 변환 함수를 위한 헤더
#include <netdb.h> // 네트워크 데이터베이스 작업을 위한 헤더
#include <stdio.h> // 표준 입출력 함수를 위한 헤더
#include <stdlib.h> // 표준 라이브러리 함수를 위한 헤더
#include <string.h> // 문자열 관련 함수를 위한 헤더
#include <sys/socket.h> // 소켓 프로그래밍을 위한 헤더
#include <sys/types.h> // 데이터 타입 정의를 위한 헤더
#include <unistd.h> // 유닉스 표준 함수를 위한 헤더
int main(void) {
struct addrinfo hints, *res;
int status;
// hints 구조체 초기화
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // IPv4와 IPv6 둘 다 허용
hints.ai_socktype = SOCK_STREAM; // TCP 소켓
// 주소 정보 가져오기
if ((status = getaddrinfo("localhost", "http", &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
return 1;
}
// 생략된 주소 정보 처리 및 출력 부분
for (struct addrinfo *p = res; p != NULL; p = p->ai_next) {
void *addr;
// IPv4와 IPv6 주소를 다루기 위한 로직
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
}
// 주소를 문자열로 변환
char ipstr[INET6_ADDRSTRLEN];
inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr));
printf("%s\n", ipstr);
}
freeaddrinfo(res); // 동적으로 할당된 메모리 해제
return 0;
}
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <unistd.h>
int main() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
char buffer[256];
while (1) {
std::cin >> buffer;
write(sock, buffer, sizeof(buffer));
}
close(sock);
return 0;
}
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
#include <unistd.h>
int main() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(8080);
bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
listen(sock, 1);
sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_sock = accept(sock, (struct sockaddr *)&client_addr, &client_len);
char buffer[256] = {0};
while (1) {
read(client_sock, buffer, 256);
std::cout << "Message from client: " << buffer << std::endl;
}
close(client_sock);
close(sock);
return 0;
}
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include <iostream>
#define MAX_CLIENTS 30
int main() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(8080);
bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
listen(sock, MAX_CLIENTS);
fd_set master_set; // 모든 소켓을 관리하는 세트
FD_ZERO(&master_set);
FD_SET(sock, &master_set); // 서버 소켓을 세트에 추가
int max_sd = sock; // 가장 높은 소켓 디스크립터 번호를 저장
while (true) {
fd_set copy_set = master_set; // select 함수가 세트를 수정할 수 있으므로
// 복사본을 만들어 사용
// 클라이언트의 연결 요청이나 데이터 수신을 기다림
if (select(max_sd + 1, ©_set, nullptr, nullptr, nullptr) == -1) {
perror("select");
exit(EXIT_FAILURE);
}
// 모든 소켓을 확인하며 수신한 데이터가 있는지 확인
for (int i = 0; i <= max_sd; ++i) {
if (FD_ISSET(i, ©_set)) {
if (i == sock) { // 새로운 클라이언트의 연결 요청이 있는 경우
sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_sock =
accept(sock, (struct sockaddr *)&client_addr, &client_len);
FD_SET(client_sock, &master_set); // 새 클라이언트 소켓을 세트에 추가
if (client_sock > max_sd) {
max_sd = client_sock;
}
std::cout << "New client connected: " << client_sock << std::endl;
} else { // 기존 클라이언트로부터 데이터를 수신한 경우
char buffer[256] = {0};
int nbytes = read(i, buffer, 256);
if (nbytes <= 0) { // 클라이언트가 연결을 종료한 경우
close(i);
FD_CLR(i, &master_set); // 클라이언트 소켓을 세트에서 제거
std::cout << "Client disconnected: " << i << std::endl;
} else { // 데이터를 수신한 경우
std::cout << "Message from client " << i << ": " << buffer
<< std::endl;
}
}
}
}
}
close(sock);
return 0;
}
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/event.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include <iostream>
#define MAX_CLIENTS 30
int main() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(8080);
bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
listen(sock, MAX_CLIENTS);
int kq = kqueue();
struct kevent evSet;
EV_SET(&evSet, sock, EVFILT_READ, EV_ADD, 0, 0, NULL);
kevent(kq, &evSet, 1, NULL, 0, NULL);
while (true) {
struct kevent evList[MAX_CLIENTS];
int nev = kevent(kq, NULL, 0, evList, MAX_CLIENTS, NULL);
if (nev < 1) {
perror("kevent");
exit(EXIT_FAILURE);
}
for (int i = 0; i < nev; ++i) {
if (evList[i].ident == sock) {
sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_sock =
accept(sock, (struct sockaddr *)&client_addr, &client_len);
// 소켓을 non-blocking 모드로 설정
int flags = fcntl(client_sock, F_GETFL, 0);
fcntl(client_sock, F_SETFL, flags | O_NONBLOCK);
EV_SET(&evSet, client_sock, EVFILT_READ, EV_ADD, 0, 0, NULL);
kevent(kq, &evSet, 1, NULL, 0, NULL);
std::cout << "New client connected: " << client_sock << std::endl;
} else {
char buffer[256] = {0};
int nbytes = read(evList[i].ident, buffer, 256);
if (nbytes <= 0) {
close(evList[i].ident);
EV_SET(&evSet, evList[i].ident, EVFILT_READ, EV_DELETE, 0, 0, NULL);
kevent(kq, &evSet, 1, NULL, 0, NULL);
std::cout << "Client disconnected: " << evList[i].ident << std::endl;
} else {
std::cout << "Message from client " << evList[i].ident << ": "
<< buffer << std::endl;
}
}
}
}
close(sock);
return 0;
}
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/event.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include <iostream>
#include <unordered_map>
#define MAX_CLIENTS 30
#include <fstream>
std::string read_file(const std::string& path) {
std::ifstream file(path, std::ios::binary | std::ios::ate);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::string buffer(size, '\0');
if (!file.read(&buffer[0], size)) {
return "";
}
return buffer;
}
std::string extract_url(const std::string& request) {
std::size_t start = request.find(' ') + 1;
std::size_t end = request.find(' ', start);
return request.substr(start, end - start);
}
int main() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(8080);
bind(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(sock, MAX_CLIENTS);
int kq = kqueue();
struct kevent evSet;
EV_SET(&evSet, sock, EVFILT_READ, EV_ADD, 0, 0, NULL);
kevent(kq, &evSet, 1, NULL, 0, NULL);
// 각 클라이언트에서 수신한 데이터를 저장하는 버퍼
std::unordered_map<int, std::string> buffers;
while (true) {
struct kevent evList[MAX_CLIENTS];
int nev = kevent(kq, NULL, 0, evList, MAX_CLIENTS, NULL);
if (nev < 1) {
perror("kevent");
exit(EXIT_FAILURE);
}
for (int i = 0; i < nev; ++i) {
if (evList[i].ident == sock) {
sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_sock =
accept(sock, (struct sockaddr*)&client_addr, &client_len);
// 소켓을 non-blocking 모드로 설정
int flags = fcntl(client_sock, F_GETFL, 0);
fcntl(client_sock, F_SETFL, flags | O_NONBLOCK);
EV_SET(&evSet, client_sock, EVFILT_READ, EV_ADD, 0, 0, NULL);
kevent(kq, &evSet, 1, NULL, 0, NULL);
std::cout << "New client connected: " << client_sock << std::endl;
} else {
char buffer[256] = {0};
int nbytes = read(evList[i].ident, buffer, 256);
if (nbytes <= 0) {
close(evList[i].ident);
EV_SET(&evSet, evList[i].ident, EVFILT_READ, EV_DELETE, 0, 0, NULL);
kevent(kq, &evSet, 1, NULL, 0, NULL);
std::cout << "Client disconnected: " << evList[i].ident << std::endl;
} else {
buffers[evList[i].ident] += buffer;
// HTTP 헤더의 끝 확인
if (buffers[evList[i].ident].find("\r\n\r\n") != std::string::npos) {
std::string url = extract_url(buffers[evList[i].ident]);
if (url == "/favicon.ico") {
std::string body = read_file("favicon.ico");
std::string response =
"HTTP/1.1 200 OK\r\n" + "Content-Type: image/x-icon\r\n" +
"Content-Length: " + std::to_string(body.size()) +
"\r\n\r\n" + body;
write(evList[i].ident, response.c_str(), response.size());
} else {
// HTTP 응답을 보내는 부분
std::string body =
"<html><body><h1>Hello, World!</h1></body></html>";
std::string response =
"HTTP/1.1 200 OK\r\nContent-Type: "
"text/html\r\nContent-Length: " +
std::to_string(body.size()) + "\r\n\r\n" + body;
write(evList[i].ident, response.c_str(), response.size());
}
// 버퍼를 비웁니다.
buffers[evList[i].ident].clear();
}
}
}
}
}
close(sock);
return 0;
}
- 허용 함수 정리
- 소켓 프로그래밍
- CGI
- 가상 호스트
- NGINX autoindex 동작 정리
- HTTP Request 파싱
- HTTP Request 값 유효성 검사
- Config 파일 Parsing