1 13 장소켓
2 13.1 소켓
클라이언트 - 서버모델 네트워크응용프로그램 클리이언트 - 서버모델을기반으로동작한다. 클라이언트 - 서버모델 하나의서버프로세스와여러개의클라이언트로구성된다. 서버는어떤자원을관리하고클라이언트를위해자원관련서비스를제공한다. 3
소켓의종류 소켓 네트워크에대한사용자수준의인터페이스를제공 소켓은양방향통신방법으로클라이언트 - 서버모델을기반으로프로세스사이의통신에매우적합하다. 유닉스소켓 (AF_UNIX) 같은호스트내의프로세스사이의통신방법 인터넷소켓 (AF_INET) 인터넷에연결된서로다른호스트에있는프로세스사이의통신방법 4
소켓연결 1. 서버가소켓을만든다. 2. 클라이언트가소켓을만든후서버에연결요청을한다. 3. 서버가클라이언트의연결요청을수락하여소켓연결이이루어진다. 5
소켓연결과정 서버 1. socket() 호출을이용하여소켓을만들고이름을붙인다. 클라이언트 1. socket() 호출을이용하여소켓을만든다. 2. listen() 호출을이용하여대기큐를만든다. 2. connection() 호출을이용하여서버에연결요청을한다. 3. 클라이언트로부터연결요청을 accept() 호출을이용하여수락 3. 서버가연결요청을수락하면소켓연결이만들어진다. 4. 소켓연결이이루어지면 - 자식프로세스를생성하여 - 클라이언트로부터요청처리 - 클라이언트에게응답한다. 4. 서버에서비스를요청하고 서버로부터응답을받아처리 6
소켓연결과정 7
소켓만들기 int socket(int domain, int type, int protocol) 소켓을생성하고소켓을위한파일디스크립터를리턴, 실패하면 -1 을리턴 인터넷소켓 fd = socket(af_inet, SOCK_STREAM, DEFAULT _PROTOCOL); 유닉스소켓 fd = socket(af_unix, SOCK_STREAM, DEFAULT _PROTOCOL); 8
소켓에이름 ( 주소 ) 주기 int bind(int fd, struct sockaddr* address, int addresslen) 소켓에대한이름바인딩이성공하면 0 을실패하면 -1 을리턴한다. 유닉스소켓이름 struct sockaddr_un { } unsigned short sun_family; char sun_path[108]; 인터넷소켓이름 9 } struct sockaddr_in { unsigned short sin_family; unsigned short sin_port; struct in_addr sin_addr; char sin_zero[8]; // AF_UNIX // 소켓이름 // AF_INET // 인터넷소켓의포트번호 // 32-bit IP 주소 // 사용안함
소켓큐생성 listen() 시스템호출 클라이언트로부터의연결요청을기다린다. 연결요청대기큐의길이를정한다. int listen(int fd, int queuelength) 소켓 fd 에대한연결요청을기다린다. 성공하면 0 을실패하면 -1 을리턴 listen(serverfd, 5); 10
소켓에연결요청 connect() 시스템호출 fd 가나타내는클라이언트소켓과 address 가나타내는서버소켓과의연결을요청한다. 성공하면 fd 를서버소켓과의통신에사용할수있다. int connect(int fd, struct sockaddr* address, int addresslen) 성공하면 0 을실패하면 -1 를리턴한다. 11
연결요청수락 int accept(int fd, struct sockaddr* address, int* addresslen) 성공하면새로만들어진복사본소켓의파일디스크립터, 실패하면 -1 을리턴 서버가클라이언트로부터의연결요청을수락하는내부과정 1. 서버는 fd가나타내는서버소켓을경청하고 2. 클라이언트의연결요청이올때까지기다린다. 3. 클라이언트로부터연결요청이오면원래서버소켓과같은복사본소켓을만들어이복사본소켓과클라이언트소켓을연결 4. 연결이이루어지면 address는클라이언트소켓의주소로세팅되고 addresslen는그크기로세팅 5. 새로만들어진복사본소켓의파일디스크립터를리턴 12
대문자변환서버 이프로그램 입력받은문자열을소문자를대문자로변환한다. 서버와클라이언트로구성된다. 서버 소켓을통해클라이언트로부터받은문자열을소문자를대문자로변환하여소켓을통해클라이언트에다시보낸다. 클라이언트 표준입력으로부터문자열을입력받아 이를소켓을통해서버에보낸후에 대문자로변환된문자열을다시받아표준출력에출력 13
cserver.c 1 #include <stdio.h> 2 #include <signal.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <sys/un.h> 6 #define DEFAULT_PROTOCOL 0 7 #define MAXLINE 100 9 /* 소문자를대문자로변환하는서버프로그램 */ 10 int main () 11 { 12 int listenfd, connfd, clientlen; 13 char inmsg[maxline], outmsg[maxline]; 14 struct sockaddr_un serverunixaddr, clientunixaddr; 15 16 signal(sigchld, SIG_IGN); 17 clientlen = sizeof(clientunixaddr); 18 19 listenfd = socket(af_unix, SOCK_STREAM, DEFAULT_PROTOCOL); 20 serverunixaddr.sun_family = AF_UNIX; 14
cserver.c 21 strcpy(serverunixaddr.sun_path, "convert"); 22 unlink("convert"); 23 bind(listenfd, &serverunixaddr, sizeof(serverunixaddr)); 24 25 listen(listenfd, 5); 26 27 while (1) { /* 소켓연결요청수락 */ 28 connfd = accept(listenfd, &clientunixaddr, &clientlen); 29 if (fork ( ) == 0) { 30 /* 소켓으로부터한줄을읽어대문자로변환하여보냄 */ 31 readline(connfd, inmsg); 32 toupper(inmsg, outmsg); 33 write(connfd, outmsg, strlen(outmsg)+1); 34 close(connfd); 35 exit (0); 36 } else close(connfd); 37 } 38 } 15
cserver.c 40 /* 소문자를대문자로변환 */ 41 toupper(char* in, char* out) 42 { 43 int i; 44 for (i = 0; i < strlen(in); i++) 45 if (islower(in[i])) 46 out[i] = toupper(in[i]); 47 else out[i] = in[i]; 48 out[i] = NULL; 49 } 50 51 /* 한줄읽기 */ 52 readline(int fd, char* str) 53 { 54 int n; 55 do { 56 n = read(fd, str, 1); 57 } while(n > 0 && *str++!= NULL); 58 return(n > 0); 16 59 }
cclient.c 1 #include <stdio.h> 2 #include <signal.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <sys/un.h> 6 #define DEFAULT_PROTOCOL 0 7 #define MAXLINE 100 8 9 /* 소문자 - 대문자변환 : 클라이언트프로그램 */ 10 int main ( ) 11 { 12 int clientfd, result; 13 char inmsg[maxline], outmsg[maxline]; 14 struct sockaddr_un serverunixaddr; 15 16 clientfd = socket(af_unix, SOCK_STREAM, DEFAULT_PROTOCOL); 17 serverunixaddr.sun_family = AF_UNIX; 18 strcpy(serverunixaddr.sun_path, "convert"); 17
cclient.c 20 do { /* 연결요청 */ 21 result = connect(clientfd, &serverunixaddr, sizeof(serverunixaddr)); 22 if (result == -1) sleep(1); 23 } while (result == -1); 24 25 printf(" 변환할문자열입력 :\n"); 26 fgets(inmsg, MAXLINE, stdin); 27 write(clientfd,inmsg,strlen(inmsg)+1); // 변환할문자열보내기 28 29 /* 소켓으로부터변환된문자열을한줄읽어서프린트 */ 30 readline(clientfd,outmsg); 31 printf("%s --> \n%s", inmsg, outmsg); 32 close(clientfd); 33 exit(0); 34 } 18
19 13.2 인터넷소켓
인터넷상의호스트 인터넷상의호스트는 32 비트 IP 주소를갖는다 예 : 203.252.201.8 IP 주소는대응하는도메인이름을갖는다. 예 : 203.252.201.8 --> www.sookmyung.ac.kr 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 관련함수 호스트의 IP 주소혹은도메인이름을이용하여 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 1 #include <stdio.h> 2 #include <signal.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <netinet/in.h> 6 #include <arpa/inet.h> 7 #include <netdb.h> 8 #define DEFAULT_PROTOCOL 0 9 #define MAXLINE 100 10 11 /* 파일서버프로그램 */ 12 int main (int argc, char* argv[]) 13 { 14 int listenfd, connfd, port, clientlen; 15 FILE *fp; 16 char inmsg[maxline], outmsg[maxline]; 17 struct sockaddr_in serveraddr, clientaddr; 18 struct hostent *hp; 1929 char *haddrp;
fserver.c 21 signal(sigchld, SIG_IGN); 22 23 if (argc!= 2) { 24 fprintf(stderr, " 사용법 : %s <port>\n", argv[0]); 25 exit(0); 26 } 27 port = atoi(argv[1]); 28 29 listenfd = socket(af_inet, SOCK_STREAM, DEFAULT_PROTOCOL); 30 31 bzero((char *) &serveraddr, sizeof(serveraddr)); 32 serveraddr.sin_family = AF_INET; 33 serveraddr.sin_addr.s_addr = htonl(inaddr_any); 34 serveraddr.sin_port = htons((unsigned short)port); 35 bind(listenfd, &serveraddr, sizeof(serveraddr)); 36 listen(listenfd, 5); 30
fserver.c 38 while (1) { 39 clientlen = sizeof(clientaddr); 40 connfd = accept(listenfd, &clientaddr, &clientlen); 41 42 /* 클라이언트의도메인이름과 IP 주소결정 */ 43 hp = gethostbyaddr((char *)&clientaddr.sin_addr.s_addr, 44 sizeof(clientaddr.sin_addr.s_addr), AF_INET); 45 haddrp = inet_ntoa(clientaddr.sin_addr); 46 printf(" 서버 : %s (%s) %d에연결됨 \n", 47 hp->h_name, haddrp, clientaddr.sin_port); 48 31
fserver.c 49 if (fork ( ) == 0) { 50 readline(connfd, inmsg); /* 소켓에서파일이름을읽는다 */ 51 fp = fopen(inmsg, "r"); 52 if (fp == NULL) { 53 write(connfd, " 해당파일없음 ", 10); 54 } else { /* 파일에서한줄씩읽어소켓을통해보낸다 */ 55 while(fgets(outmsg, MAXLINE, fp)!= NULL) 56 write(connfd, outmsg, strlen(outmsg)+1); 57 } 58 close(connfd); 59 exit (0); 60 } else close(connfd); 61 } // while 62 } // main 32
fclient.c 1 #include <stdio.h> 2 #include <signal.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <netinet/in.h> 6 #include <arpa/inet.h> 7 #include <netdb.h> 8 #define DEFAULT_PROTOCOL 0 9 #define MAXLINE 100 10 11 /* 파일클라이언트프로그램 */ 12 int main (int argc, char* argv[]) 13 { 14 int clientfd, port, result; 15 char *host, inmsg[maxline], outmsg[maxline]; 16 struct sockaddr_in serveraddr; 17 struct hostent *hp; 33
fclient.c 19 if (argc!= 3) { 20 fprintf(stderr, " 사용법 : %s <host> <port>\n", argv[0]); 21 exit(0); 22 } 23 24 host = argv[1]; 25 port = atoi(argv[2]); 27 clientfd = socket(af_inet, SOCK_STREAM, DEFAULT_PROTOCOL); 28 29 /* 서버의 IP 주소와포트번호를채운다. */ 30 if ((hp = gethostbyname(host)) == NULL) 31 perror("gethostbyname error"); // 호스트찾기오류 32 bzero((char *) &serveraddr, sizeof(serveraddr)); 33 serveraddr.sin_family = AF_INET; 34 bcopy((char *)hp->h_addr_list[0], 35 (char *)&serveraddr.sin_addr.s_addr, hp->h_length); 36 serveraddr.sin_port = htons(port); 34
fclient.c 38 do { /* 연결요청 */ 39 result = connect(clientfd, &serveraddr, sizeof(serveraddr)); 40 if (result == -1) sleep(1); 41 } while (result == -1); 42 43 printf(" 파일이름입력 :"); 44 scanf("%s", inmsg); 45 write(clientfd,inmsg,strlen(inmsg)+1); 46 47 /* 소켓으로부터파일내용읽어서프린트 */ 48 while (readline(clientfd,outmsg)) 49 printf("%s", outmsg); 50 close(clientfd); 51 exit(0); 52 } 35
핵심개념 소켓은양방향통신방법으로클라이언트 - 서버모델을기반으로프로세스사이의통신에매우적합하다. 소켓에는같은호스트내의프로세스사이의통신을위한유닉스소켓과다른호스트에있는프로세스사이의통신을위한인터넷소켓이있다. 36