728x90
RIO(Robust I/O)
RIO 버퍼 없는 입력 및 출력 함수
메모리와 파일 간에 직접 데이터를 전송할 수 있게 한다.
rio_readn
현재 파일 식별자 fd에서 n만큼의 데이터를 버퍼 usrbuf로 보낸다.
#include "csapp.h"
ssize_t rio_readn(int fd, void* usrbuf, size_t n)
- int fd : 내가 보낼 바이트가 저장된 현재 파일의 위치
- void *usrbuf : 파일에서 전송할 대상 메모리 버퍼의 위치
- size_t n : 전송할 바이트의 수
- 리턴 값 : 성공하면 전송한 바이트의 수를 반환하고, EOF를 읽기 중에 만나면 0, 에러가 나면 -1을 반환한다.
rio_writen
현재 메모리의 버퍼 usrbuf에서 n만큼의 데이터를 파일 식별자 fd로 보낸다.
#include "csapp.h"
ssize_t rio_writen(int fd, void* usrbuf, size_t n);
- int fd : 내가 n 바이트를 쓰고 싶은 현재 파일의 위치
- void *usrbuf : n바이트가 있는 메모리 버퍼의 위치
- size_t : 전송할 바이트의 수
- 리턴 값 : 성공하면 전송한 바이트의 수를 반환하고, 에러가 나면 -1을 반환한다.
RIO 버퍼를 통한 입력 함수
텍스트 라인 전체를 내부 읽기 버퍼에서 복사하는 rio_readlineb와 텍스트 라인과 바이너리 데이터 모두를 읽을 수 있는 rio_readnb가 있다.
rio_t 구조체
#define RIO_BUFSIZE 8192
typedef struct {
int rio_fd; /* Descriptor for this internal buf */
int rio_cnt; /* Unread bytes in internal buf */
char *rio_bufptr; /* Next unread byte in internal buf */
char rio_buf[RIO_BUFSIZE]; /* Internal buffer */
} rio_t;
rio_readinitb
읽고 싶은 파일 식별자 fd와 읽기 버퍼 rp를 연결한다.
#include "csapp.h"
void rio_readinitb(rio_t *rp, int fd)
{
rp->rio_fd = fd;
rp->rio_cnt = 0; // unread size 라고는 하는데 사용하는 걸 보면 read size 라고 봐야 된다
rp->rio_bufptr = rp->rio_buf; // 내부 버퍼 포인터
}
rio_readlineb
텍스트 라인 전체를 내부 읽기 버퍼 rp에서 읽은 후, 메모리 버퍼 usrbuf으로 복사하고, \\0(NULL)로 텍스트 라인을 종료시킨다.
최대 maxlen-1 바이트 만큼 읽고 마지막 1바이트는 \\0을 넣어준다.
ssize_t rio_readlineb(rio_t* rp, void* usrbuf, size_t maxlen)
{
int n, rc;
char c, *bufp = usrbuf;
for (n = 1; n < maxlen; n++) {
if ((rc = rio_read(rp, &c, 1)) == 1) {
*bufp++ = c;
if (c == '\n') {
n++;
break;
}
} else if (rc == 0) {
if (n == 1)
return 0; /* EOF, no data read */
else
break; /* EOF, some data was read */
} else
return -1; /* Error */
}
*bufp = 0;
return n-1;
}
rio_readnb
텍스트 라인과 바이너리 데이터 모두를 읽을 수 있다. 이때, n 바이트씩 가져온다.
ssize_t rio_readnb(rio_t* rp, void* usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nread = rio_read(rp, bufp, nleft)) < 0)
return -1; /* errno set by read() */
else if (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* return >= 0 */
}
echoclient.c
서버와의 연결 후 클라이언트와 통신하는 역할이다.
#include "csapp.h"
/* 0번째 인자로 실행 파일, 1번째로 호스트네임, 2번째로 포트 넘버를 받는다.*/
int main(int argc, char **argv)
{
int clientfd;
char *host, *port, buf[MAXLINE];
rio_t rio;
host = argv[1];
port = argv[2];
clientfd = Open_clientfd(host, port); // 서버와의 연결 성공(connect까지)
/* 1. 클라이언트 소켓 파일 식별자와 읽기 버퍼 rio를 연결한다.*/
Rio_readinitb(&rio, clientfd);
/* 표준 입력에서 텍스트 줄을 반복적으로 읽는다. */
/* 2. 표준 입력sdtin에서 MAXLINE만큼 바이트를 가져와 buf에 저장한다. */
while (Fgets(buf, MAXLINE, stdin) != NULL) { // 6. EOF 표준 입력을 만나면 종료한다.
// 3. buf 메모리 안의 strlen(buf) 바이트 만큼의(사실상 모두)를 clientfd로 보낸다.
Rio_writen(clientfd, buf, strlen(buf));
// 4. 서버가 rio에 echo줄을 쓰면 그 rio를 읽어서 읽기 버퍼 buf에 쓴다.
Rio_readlineb(&rio, buf, MAXLINE);
// 5. buf에 받아온 값을 표준 출력으로 인쇄한다.
Fputs(buf, stdout);
}
Close(clientfd); // 루프가 종료되면 클라이언트 식별자를 닫는다. 서버에 EOF 통지가 전송된다.
exit(0); // 클라이언트가 종료된다.
}
echoserver.c
서버가 클라이언트의 연결 요청을 받아 연결 소켓 식별자를 만든다. 그리고 echo 함수를 호출한다.
#include "csapp.h"
void echo(int connfd);
/* 서버의 포트 번호를 1번째 인자로 받는다. */
int main(int argc, char **argv)
{
int listenfd, connfd;
socklen_t clientlen;
/* Accept로 보내지는 client 소켓 구조체. */
struct sockaddr_storage clientaddr; /* sockaddr_storage 구조체: 모든 프로토콜의 주소에 대해 Enough room for any addr */
char client_hostname[MAXLINE], client_port[MAXLINE];
// 인자 2개 다 받아야 함.
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); /* Important! 길이가 충분히 커서 프로토콜-독립적!*/
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);
}
echo.c
클라이언트와 연결된 읽기 버퍼에 서버 연결 소켓도 연결해준다. 클라이언트가 보낸 데이터를 그대로 읽기 버퍼에 다시 써 준다.
#include "csapp.h"
void echo(int connfd)
{
size_t n;
char buf[MAXLINE];
rio_t rio;
/* 읽기 버퍼 rio와 서버의 연결 소켓 식별자를 연결해준다. clientfd도 연결되어 있다. */
Rio_readinitb(&rio, connfd);
/* 읽기 버퍼 rio에서 클라이언트가 보낸 데이터를 읽고, rio에 그 데이터를 고대로 쓴다.*/
/* 읽기 버퍼 rio에서 MAXLINE만큼의 데이터를 읽어 와 buf에 넣는다. */
while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) { // 0이면 클라이언트 식별자가 닫혔다는 소리다.
printf("server received %d bytes\n", (int)n);
/* buf 안에는 클라이언트가 보낸 데이터 그대로 있다. */
/* buf 메모리 안의 클라이언트가 보낸 바이트 만큼의(사실상 모두)를 clientfd로 보낸다. */
Rio_writen(connfd, buf, n);
}
/* 클라이언트 식별자가 닫히면 루프 종료 및 함수도 종료. */
}
Makefile
CC = gcc
CFLAGS = -O2 -Wall -I .
# This flag includes the Pthreads library on a Linux box.
# Others systems will probably require something different.
LIB = -lpthread
all: echoclient echoserver
echoclient: echoclient.c csapp.o
$(CC) $(CFLAGS) -o echoclient echoclient.c csapp.o $(LIB)
echoserver: echoserver.c csapp.o echo.o
$(CC) $(CFLAGS) -o echoserver echoserver.c csapp.o echo.o $(LIB)
csapp.o: csapp.c
$(CC) $(CFLAGS) -c csapp.c
echo.o: echo.c
$(CC) $(CFLAGS) -c echo.c
728x90
'컴퓨터 사이언스 > Network' 카테고리의 다른 글
웹 서버 기초 (0) | 2023.11.19 |
---|---|
호스트와 서비스 변환 (0) | 2023.11.19 |
소켓 인터페이스를 위해 도움을 주는 함수들 (0) | 2023.11.19 |
소켓 인터페이스 (0) | 2023.11.18 |
Client Server Architecture (0) | 2023.11.17 |