시간이 얼마나 있는진 상관없어
어떻게 쓰느냐가 중요하지
오늘은 에코 서버다
ㅇㅇ
가장 기본중의 기본 기능만 들어간 웹서버다
송수신 기능만 확인하는 정도로 말이다
그리고 이거 구현할 거다
교재를 참고하면서 했다
깡으로 전부 짜고 싶었지만…
그건 쉽지 않아 포기했다
일단 에코 서버 클라이언트부터 시작했다
#include "csapp.h"
int main(int argc, char **argv)
{
int clientfd;
char *host, *port, buf[MAXLINE];
rio_t rio;
}
시작 인자부터 정해줬다
csapp.h
에는 교재에서 준비해준 유용한 함수랑 사용할 것들이 몇 있어서 필수다
이거 안쓰면 코드 좀 어려워진다
참고로 argc
는 argument count로 실행 시 전달된 인자 개수이고
argc
는 argument vector로 인자들을 가르키는 문자열 포인터 배열이다
argv[0]
: 프로그램 이름(또는 경로)argv[1]
: 첫 번째 사용자 인자NULL
포인터가 온다 (argv[argc] == NULL
)if (argc != 3) {
fprintf(stderr, "usage: %s <host> <port>\n", argv[0]);
exit(0);
}
host = argv[1];
port = argv[2];
if
문으로 인자가 3개가 아니라면 종료되게 짜준다
이름, 호스트, 포트가 들어와야 하니 말이다
입력 들어온 것 중에서 argv[0]
은 프로그램 이름
argv[1]
은 호스트
argv[2]
은 포트로 정해준다
clientfd = Open_clientfd(host, port);
Rio_readinitb(&rio, clientfd);
csapp에서 구현해준 함수인 Open_clientfd
로 연결된 소켓 디스크립터까지 나오게 한다
그리고 Rio_readinitb
로 구조체 초기화해서 값 받아줄 준비한다
while (Fgets(buf, MAXLINE, stdin) != NULL) {
Rio_writen(clientfd, buf, strlen(buf));
Rio_readlineb(&rio, buf, MAXLINE);
Fputs(buf, stdout);
}
Close(clientfd);
exit(0);
그리고 while
문으로 값 받고 보내는 걸 반복한다
whil
문으로 하는 건 EOF가 아닐때까지 반복하며 줄을 입력 받는거다
/* 성공 시 buf포인터, EOF면 NULL, 에러면 내부 메시지 출력 */
Fgets(사용하는 버퍼, 최대 읽을 바이트 수, stream(보통 stdin))
/* buf의 문자열 표준출력으로 출력, 에러면 내부 메시지 출력 */
Fputs(출력할 C 문자열, stream(보통 stdout))
stream: 바이트 흐름을 다루는 추상화
난 여기서 앞에 F
가 붙는 것들에 대해 살짝 이해가 안갔다
CSAPP에서 제공하는 에러-체크용 래퍼라고 한다
그냥 표준 C의 fgets
, fputs
지만 실패 시 메시지 찍고 종료하게 만든거라고 한다
먼저 에코 서버측에서 사용할 에코 함수 부터 만들어준다
에코라는 이름에서 알 수 있듯이
메아리다
받은 정보 그대로 반환해서 연결되었다는 거 확인한다
#include "csapp.h"
void echo(int connfd)
{
size_t n;
char buf[MAXLINE];
rio_t rio;
Rio_readinitb(&rio, connfd);
while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {
printf("server received %d bytes\n", (int)n);
Rio_writen(connfd, buf, n);
}
}
별 거 없다
RIO에 대해서 알면 그냥 할 수 있다
연결된 소켓 파일디스크립터 입력 보내면
Rio 구조체 초기화 하고 읽기 멈출때까지 인풋 받고 반환하는 거 반복한다
#include "csapp.h"
void echo(int connfd);
int main(int argc, char **argv)
{
int listenfd, connfd;
socklen_t clientlen;
struct sockaddr_storage clientaddr;
char client_hostname[MAXLINE], client_port[MAXLINE];
}
일단 그전에 만들어둔 echo함수 사용해야 하니까 미리 선언해준다
이후, 사용할 인자들을 지정해주고 말이다
길이, 소켓fd, 주소, 이름, 포트 들을 선언해준다
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(0);
}
그 다음에는 클라측과 비슷하게 가드 만들어준다
인자 2개 아니면 반환하게 말이다
listenfd = Open_listenfd(argv[1]);
while (1) {
clientlen = sizeof(struct sockaddr_storage);
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
Getnameinfo((SA *)&clientaddr, clientlen, client_hostname, MAXLINE, client_port, MAXLINE, 0);
printf("Connected to (%s, %s)\n", client_hostname, client_port);
echo(connfd);
close(connfd);
}
exit(0);
그 다음은 서버 동작 넣어주면 된다
listen 파일 디스크립터 만들어주고
길이랑 연결 소켓 만들어준다
getnameinfo
에 알맞게 값을 넣어 주소를 문자열로 변환해준다
그리고 echo
실행하게 연결 소켓 넣어주면 완성이다
#include "csapp.h"
int main(int argc, char **argv)
{
int clientfd;
char *host, *port, buf[MAXLINE];
rio_t rio;
if ( argc != 3) {
fprintf(stderr, "usage: %s <host> <port>\n", argv[0]);
exit(0);
}
host = argv[1];
port = argv[2];
clientfd = Open_clientfd(host, port);
Rio_readinitb(&rio, clientfd);
while (Fgets(buf, MAXLINE, stdin) != NULL) {
Rio_writen(clientfd, buf, strlen(buf));
Rio_readlineb(&rio, buf, MAXLINE);
Fputs(buf, stdout);
}
Close(clientfd);
exit(0);
}
#include "csapp.h"
void echo(int connfd)
{
size_t n;
char buf[MAXLINE];
rio_t rio;
Rio_readinitb(&rio, connfd);
while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {
printf("server received %d bytes\n", (int)n);
Rio_writen(connfd, buf, n);
}
}
#include "csapp.h"
void echo(int connfd);
int main(int argc, char **argv)
{
int listenfd, connfd;
socklen_t clientlen;
struct sockaddr_storage clientaddr;
char client_hostname[MAXLINE], client_port[MAXLINE];
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(0);
}
listenfd = Open_listenfd(argv[1]);
while (1) {
clientlen = sizeof(struct sockaddr_storage);
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
Getnameinfo((SA *)&clientaddr, clientlen, client_hostname, MAXLINE, client_port, MAXLINE, 0);
printf("Connected to (%s, %s)\n", client_hostname, client_port);
echo(connfd);
close(connfd);
}
exit(0);
}