Download presentation
Presentation is loading. Please wait.
1
14장 소켓
2
14.1 소켓
3
클라이언트-서버 모델 네트워크 응용 프로그램 클라이언트-서버 모델 클리이언트-서버 모델을 기반으로 동작한다
하나의 서버 프로세스와 여러 개의 클라이언트로 구성된다 서버는 어떤 자원을 관리하고 클라이언트를 위해 자원 관련 서 비스를 제공한다
4
소켓의 종류 소켓 유닉스 소켓(AF_UNIX) 인터넷 소켓(AF_INET) 네트워크에 대한 사용자 수준의 인터페이스를 제공
소켓은 양방향 통신 방법으로 클라이언트-서버 모델을 기반으로 프로세스 사이의 통신에 매우 적합하다 유닉스 소켓(AF_UNIX) 같은 호스트 내의 프로세스 사이의 통신 방법 인터넷 소켓(AF_INET) 인터넷에 연결된 서로 다른 호스트에 있는 프로세스 사이의 통 신 방법
5
소켓 연결 1. 서버가 소켓을 만든다 2. 클라이언트가 소켓을 만든 후 서버에 연결 요청을 한다
3. 서버가 클라이언트의 연결 요청을 수락하여 소켓 연결이 이루어진다
6
소켓 연결 과정 서버 클라이언트 1. socket() 호출을 이용하여 소켓을 만들고 이름을 붙인다
2. listen() 호출을 이용하여 대기 큐를 만든다 3. 클라이언트로부터 연결 요청을 accept() 호출을 이용하여 수락한다 4. 소켓 연결이 이루어지면 - 자식 프로세스를 생성하여 - 클라이언트로부터 요청 처리 - 클라이언트에게 응답한다 클라이언트 1. socket() 호출을 이용하여 소켓 을 만든다 2. connection() 호출을 이용하여 서버에 연결 요청을 한다 3. 서버가 연결 요청을 수락하면 소켓 연결이 만들어진다 4. 서버에 서비스를 요청하고 서버 로부터 응답을 받아 처리한다
7
소켓 연결 과정
8
소켓 만들기 인터넷 소켓 fd = socket(AF_INET, SOCK_STREAM, DEFAULT _PROTOCOL); 유닉스 소켓 fd = socket(AF_UNIX, SOCK_STREAM, DEFAULT _PROTOCOL); int socket(int domain, int type, int protocol) 소켓을 생성하고 소켓을 위한 파일 디스크립터를 리턴, 실패하면 -1을 리턴한다.
9
소켓에 이름(주소) 주기 유닉스 소켓 이름 인터넷 소켓 이름
struct sockaddr_un { unsigned short sun_family; // AF_UNIX char sun_path[108]; // 소켓 이름 } 인터넷 소켓 이름 struct sockaddr_in { unsigned short sin_family; // AF_INET unsigned short sin_port; // 인터넷 소켓의 포트 번호 struct in_addr sin_addr; // 32-bit IP 주소 char sin_zero[8]; // 사용 안 함 int bind(int fd, struct sockaddr* address, int addressLen) 소켓에 대한 이름 바인딩이 성공하면 0을 실패하면 -1을 리턴한다.
10
소켓 큐 생성 listen() 시스템 호출 listen(serverFd, 5); 클라이언트로부터의 연결 요청을 기다린다.
연결 요청 대기 큐의 길이를 정한다. listen(serverFd, 5); int listen(int fd, int queueLength) 소켓 fd에 대한 연결 요청을 기다린다. 성공하면 0을 실패하면 -1을 리턴한다.
11
소켓에 연결 요청 connect() 시스템 호출
fd가 나타내는 클라이언트 소켓과 address가 나타내는 서버 소 켓과의 연결을 요청한다. 성공하면 fd를 서버 소켓과의 통신에 사용할 수 있다. int connect(int fd, struct sockaddr* address, int addressLen) 성공하면 0을 실패하면 -1를 리턴한다.
12
연결 요청 수락 서버가 클라이언트로부터의 연결요청을 수락하는 내부 과정 1. 서버는 fd가 나타내는 서버 소켓을 경청하고
2. 클라이언트의 연결 요청이 올 때까지 기다린다. 3. 클라이언트로부터 연결 요청이 오면 원래 서버 소켓과 같은 복사본 소켓을 만들어 이 복사본 소켓과 클라이언트 소켓을 연결 4. 연결이 이루어지면 address는 클라이언트 소켓의 주소로 세팅되고 addressLen는 그 크기로 세팅 5. 새로 만들어진 복사본 소켓의 파일 디스크립터를 리턴 int accept(int fd, struct sockaddr* address, int* addressLen) 성공하면 새로 만들어진 복사본 소켓의 파일 디스크립터, 실패하면 -1을 리턴한다.
13
대문자 변환 서버 이 프로그램 서버 클라이언트 입력받은 문자열을 소문자를 대문자로 변환한다 서버와 클라이언트로 구성된다
소켓을 통해 클라이언트로부터 받은 문자열을 소문자를 대문자 로 변환하여 소켓을 통해 클라이언트에 다시 보낸다 클라이언트 표준입력으로부터 문자열을 입력받아 이를 소켓을 통해 서버에 보낸 후에 대문자로 변환된 문자열을 다시 받아 표준출력에 출력한다
14
cserver.c #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #define DEFAULT_PROTOCOL 0 #define MAXLINE 100 /* 소문자를 대문자로 변환하는 서버 프로그램 */ int main () { int listenfd, connfd, clientlen; char inmsg[MAXLINE], outmsg[MAXLINE]; struct sockaddr_un serverUNIXaddr, clientUNIXaddr; signal(SIGCHLD, SIG_IGN); clientlen = sizeof(clientUNIXaddr); listenfd = socket(AF_UNIX, SOCK_STREAM, DEFAULT_PROTOCOL); serverUNIXaddr.sun_family = AF_UNIX; unlink("convert"); bind(listenfd, (const struct sockaddr *)&serverUNIXaddr, sizeof(serverUNIXaddr)); listen(listenfd, 5);
15
cserver.c while (1) { /* 소켓 연결 요청 수락 */ connfd = accept(listenfd, (struct sockaddr *)&clientUNIXaddr, &clientlen); if (fork ( ) == 0) { /* 소켓으로부터 한 줄을 읽어 대문자로 변환하여 보냄 */ readLine(connfd, inmsg); toUpper(inmsg, outmsg); write(connfd, outmsg, strlen(outmsg)+1); close(connfd); exit (0); } else close(connfd); } /* 소문자를 대문자로 변환 */ toUpper(char* in, char* out) { int i; for (i = 0; i < strlen(in); i++) if (islower(in[i])) out[i] = toupper(in[i]); else out[i] = in[i]; out[i] = (int)NULL; } /* 한 줄 읽기 */ readLine(int fd, char* str) int n; do { n = read(fd, str, 1); } while(n > 0 && *str++ != (int)NULL); return(n > 0);
16
cclient.c #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #define DEFAULT_PROTOCOL 0 #define MAXLINE 100 /* 소문자-대문자 변환: 클라이언트 프로그램 */ int main ( ) { int clientfd, result; char inmsg[MAXLINE], outmsg[MAXLINE]; struct sockaddr_un serverUNIXaddr; clientfd = socket(AF_UNIX, SOCK_STREAM, DEFAULT_PROTOCOL); serverUNIXaddr.sun_family = AF_UNIX; strcpy(serverUNIXaddr.sun_path, "convert"); do { /* 연결 요청 */ result = connect(clientfd, &serverUNIXaddr, sizeof(serverUNIXaddr)); if (result == -1) sleep(1); } while (result == -1);
17
cclient.c printf("변환할 문자열 입력:\n"); fgets(inmsg, MAXLINE, stdin); write(clientfd,inmsg,strlen(inmsg)+1); // 변환할 문자열 보내기 /* 소켓으로부터 변환된 문자열을 한 줄 읽어서 프린트 */ readLine(clientfd,outmsg); printf("%s --> \n%s", inmsg, outmsg); close(clientfd); exit(0); } /* 한 줄 읽기 */ readLine(int fd, char* str) { int n; do { n = read(fd, str, 1); } while(n > 0 && *str++ != (int)NULL); return(n > 0);
18
14.2 인터넷 소켓
19
인터넷 상의 호스트 인터넷 상의 호스트는 32 비트 IP 주소를 갖는다 IP 주소는 대응하는 도메인 이름을 갖는다.
예: IP 주소는 대응하는 도메인 이름을 갖는다. 예: > 32 비트 IP 주소는 저장 /* 인터넷 주소 구조체 */ struct in_addr { unsigned int s_addr; // 네트워크 바이트 순서(big-endian) };
20
DNS(Domain Name System)
인터넷은 IP 주소와 도메인 이름 사이의 맵핑을 DNS라 부르 는 전세계적인 분산 데이터베이스에 유지한다 /* DNS 호스트 엔트리 구조체 */ struct hostent { char *h_name; // 호스트의 공식 도메인 이름 char **h_aliases; // null로 끝나는 도메인 이름의 배열 int h_addrtype; // 호스트 주소 타입(AF_INET) int h_length; // 주소의 길이 char **h_addr_list; // null로 끝나는 in_addr 구조체의 배열 };
21
DNS 관련 함수 호스트의 주소 혹은 이름을 이용하여 DNS로부터 호스트 엔 트리를 검색할 수 있다
struct hostent *gethostbyaddr(const char* addr, int len, int type); 길이가 len이고 주소 타입 type인 호스트 주소 addr에 해당하는 hostent 구조체를 리턴한다. struct hostent* gethostbyname(char* name); 도메인 이름에 대응하는 hostent 구조체에 대한 포인터를 리턴한다.
22
DNS 관련 함수 in_addr 구조체 형식으로 된 IP 주소를 프린트 할 수 있는 스트링으로 변환한다
char* inet_ntoa(struct in_addr address); IP 주소 address에 대응하는 A.B.C.D 포맷의 스트링을 리턴한다. unsigned long inet_addr(char* string); A.B.C.D 포맷의 IP 주소를 네트워크 바이트 순서로 된 이진 데이터로 변환하여 리턴한다.
23
인터넷 소켓 인터넷 소켓 인터넷 소켓 연결 예 서로 다른 호스트에서 실행되는 클라이언트-서버 사이의 통신
양방향(2-way) 통신 소켓을 식별하기 위해 호스트의 IP 주소와 포트 번호를 사용 인터넷 소켓 연결 예
24
인터넷 소켓 인터넷 소켓 통신을 사용하는 SW 잘 알려진 서비스의 포트 번호 웹 브라우저 ftp telnet ssh
시간 서버: 13번 포트 ftp 서버: 20,21번 포트 텔넷 서버: 23번 포트 메일 서버: 25번 포트 웹 서버: 80번 포트
25
클라이언트-서버 인터넷 소켓 연결 과정
26
인터넷 소켓 이름(주소) 인터넷 소켓 이름(주소) 소켓 이름을 위한 포괄적 구조체 struct sockaddr_in {
unsigned short sin_family; // AF_INET unsigned short sin_port; // 인터넷 소켓의 포트 번호 struct in_addr sin_addr; // 32-bit IP 주소 char sin_zero[8]; // 사용 안 함 } 소켓 이름을 위한 포괄적 구조체 struct sockaddr { unsigned short sa_family; // 프로토콜 패밀리 char sa_data[14]; // 주소 데이터 };
27
파일 서버-클라이언트 서버 클라이언트 파일 이름을 받아 해당 파일을 찾아 그 내용을 보내주는 서비스
명령줄 인수로 포트 번호를 받아 해당 소켓을 만든다 이 소켓을 통해 클라이언트로부터 파일 이름을 받아 해당 파일을 열고 그 내용을 이 소켓을 통해 클라이언트에게 보 낸다 클라이언트 명령줄 인수로 연결할 서버의 이름과 포트 번호를 받아 해당 서 버에 소켓 연결을 한다 이 연결을 통해 서버에 원하는 파일 이름을 보낸 후 서버로부터 해당 파일 내용을 받아 사용자에게 출력한다
28
fserver.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #define DEFAULT_PROTOCOL 0 #define MAXLINE 100 /* 파일 서버 프로그램 */ int main (int argc, char* argv[]) { int listenfd, connfd, port, clientlen; FILE *fp; char inmsg[MAXLINE], outmsg[MAXLINE]; struct sockaddr_in serveraddr, clientaddr; struct hostent *hp; char *haddrp; signal(SIGCHLD, SIG_IGN); if (argc != 2) { fprintf(stderr, "사용법: %s <port>\n", argv[0]); exit(0); } port = atoi(argv[1]); listenfd = socket(AF_INET, SOCK_STREAM, DEFAULT_PROTOCOL); bzero((char *) &serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); serveraddr.sin_port = htons((unsigned short)port); bind(listenfd, (const struct sockaddr *)&serveraddr, sizeof(serveraddr)); listen(listenfd, 5);
29
fserver.c while (1) { clientlen = sizeof(clientaddr); connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientlen); /* 클라이언트의 도메인 이름과 IP 주소 결정 */ hp = gethostbyaddr((char *)&clientaddr.sin_addr.s_addr, sizeof(clientaddr.sin_addr.s_addr), AF_INET); haddrp = inet_ntoa(clientaddr.sin_addr); printf("클라이언트: %s 포트: %d 연결됨\n", haddrp, clientaddr.sin_port); if (fork () == 0) { readLine(connfd, inmsg); /* 소켓에서 파일 이름을 읽는다 */ fp = fopen(inmsg, "r"); if (fp == NULL) { write(connfd, "해당 파일 없음", 10); } else { /* 파일에서 한 줄씩 읽어 소켓을 통해 보낸다 */ while(fgets(outmsg, MAXLINE, fp) != NULL) write(connfd, outmsg, strlen(outmsg)+1); } close(connfd); exit (0); } else close(connfd); } // while } // main readLine(int fd, char* str) { int n; do { n = read(fd, str, 1); } while(n > 0 && *str++ != (int)NULL); return(n > 0);
30
fclient.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #define DEFAULT_PROTOCOL 0 #define MAXLINE 100 /* 파일 클라이언트 프로그램 */ int main (int argc, char* argv[]) { int clientFd, port, result; char *host, inmsg[MAXLINE], outmsg[MAXLINE]; struct sockaddr_in serverAddr; struct hostent *hp; struct in_addr *hostnode; char *hostaddress; if (argc != 3) { fprintf(stderr, "사용법 : %s <host> <port>\n", argv[0]); exit(0); } host = argv[1]; port = atoi(argv[2]); clientFd = socket(AF_INET, SOCK_STREAM, DEFAULT_PROTOCOL); /* 서버의 IP 주소와 포트 번호를 채운다 */ if ((hp = gethostbyname(host)) == NULL) perror("gethostbyname error"); // 호스트 찾기 오류 hostnode = (struct in_addr *)hp->h_addr; hostaddress = inet_ntoa(*hostnode); printf("서버: %s 포트: %d 연결 요청\n", hostaddress, port);
31
fclient.c bzero((struct sockaddr *)&serverAddr, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = hostnode->s_addr; serverAddr.sin_port = htons(port); do { /* 연결 요청 */ result = connect(clientFd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); if (result == -1) sleep(1); } while (result == -1); sleep(1); // 서버 메시지 출력 후 입력 유도 printf("파일 이름 입력: "); scanf("%s", inmsg); write(clientFd,inmsg,strlen(inmsg)+1); /* 소켓으로부터 파일 내용 읽어서 프린트 */ while (readLine(clientFd, outmsg)) printf("%s", outmsg); close(clientFd); exit(0); } readLine(int fd, char* str) { int n; do { n = read(fd, str, 1); } while(n > 0 && *str++ != (int)NULL); return(n > 0);
32
핵심 개념 소켓은 양방향 통신 방법으로 클라이언트-서버 모델을 기반으로 프로세스 사이의 통신에 매우 적합하다
소켓은 양방향 통신 방법으로 클라이언트-서버 모델을 기반으로 프로세스 사이의 통신에 매우 적합하다 소켓에는 같은 호스트 내의 프로세스 사이의 통신을 위한 유닉스 소켓과 다른 호스트에 있는 프로세스 사이의 통신을 위한 인터넷 소켓이 있다
Similar presentations