2011년7월8일_TCP Echo Server(임시제목)
● TCPEchoServer4.c 1. socket()를 통해 TCP소켓을 생성. 2. bind()를 통해 소켓에 포트번호를 할당. 3. listen()을 통해 해당포트가 연결을 받아들이도록 시스템에 알림. 4. 다음과 같은 일을 계속적으로 반복 ① accept()를 통해 각 클라이언트와 통신에 필요한 새로운 소켓을 획득. ② 새롭게 생성된 클라이언트 소켓에 send(), recv()를 호출하여 통신을 수행. ③ close()를 통해 클라이언트와 연결을 종료 |
1: // TCPEchoServer4.c 2: #include <stdio.h>
3: #include <stdlib.h>
4: #include <string.h> 5: #include <sys/types.h>
6: #include <sys/socket.h>
7: #include <netinet/in.h> 8: #include <arpa/inet.h>
9: #include "Practical.h" 10: #include "TCPServerUtility.h" 11:
12:
13: static const int MAXPENDING = 5; //연결 요청을 대기할 수 있는 최대수 14:
15: int main(int argc, char *argv[]) 16: {
17: in_port_t servPort; //첫 번째 인자: 지역 포트 18: int servSock; //서버 소켓 식별자 19: struct sockaddr_in servAddr; //지역주소 20: struct sockaddr_in clntAddr; //클라이언트 주소 21: socklen_t clntAddrLen;
22: int clntSock; 23: char clntName[INET_ADDRSTRLEN]; //클라이언트 주소를 담기 위한 문자열 24:
25: if(argc != 2) //명령어 인자의 개수 확인 26: {
27: DieWithUserMessage("Parameter(s)", "<Server Port>"); 28: }
29:
30: servPort = atoi(argv[1]); //첫 번째 인자: 지역 포트 31:
32: //연결 요청을 처리하는 소켓 생성 33: if((servSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 34: {
35: DieWithSystemMessage("socket() failed"); 36: }
37:
38: //지역 주소 구조체 할당 39: memset(&servAddr, 0, sizeof(servAddr)); //0으로 구조체 초기화 40: servAddr.sin_family = AF_INET; //IPv4 주소 패밀리 41: servAddr.sin_addr.s_addr = htonl(INADDR_ANY); //호스트의 어떠한 IP로도 연결 요청 수락 42: servAddr.sin_port = htons(servPort); //지역포트 43:
44: //지역 주소에 바인드 45: if(bind(servSock, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0) 46: {
47: DieWithSystemMessage("bind() failed"); 48: }
49:
50: //소켓에 들어오는 요청을 처리할 수 있도록 설정 51: if(listen(servSock, MAXPENDING) < 0) 52: {
53: DieWithSystemMessage("listen() failed"); 54: }
55:
56: for(;;) //무한 실행 57: {
58: //클라이언트 주소 구조체의 크기 설정 59: clntAddrLen = sizeof(clntAddr); 60:
61: //클라이언트의 연결을 기다림 62: clntSock = accept(servSock, (struct sockaddr *) &clntAddr, &clntAddrLen); 63: if(clntSock < 0) 64: {
65: DieWithSystemMessage("accept() failed"); 66: }
67:
68: //clntSock가 클라이언트와 연결됨 69: if(inet_ntop(AF_INET, &clntAddr.sin_addr.s_addr, clntName, sizeof(clntName)) != NULL) 70: {
71: printf("Handling client %s/%d\n", clntName, ntohs(clntAddr.sin_port)); 72: }
73: else 74: {
75: puts("Unable to get client address"); 76: }
77:
78: HandleTCPClient(clntSock);
79: }
80:
81: //실행되지 않음 82:
83: return 0; 84: }
|
//설명/.
● TCPServerUtility.h
1: #ifndef __TCPSERVERUTILITY_H__
2: #define __TCPSERVERUTILITY_H__ 3: void HandleTCPClient(int); 4: #define BUFSIZE 100 5: #endif //__TCPSERVERUTILITY_H__ |
● TCPServerUtility.c
1: #include <stdio.h>
2: #include <stdlib.h>
3: #include <string.h> 4: #include <sys/types.h>
5: #include <sys/socket.h>
6: #include <netinet/in.h> 7: #include <arpa/inet.h>
8: #include "Practical.h" 9: #include "TCPServerUtility.h" 10:
11: //TCPServerUtility.c 12: void HandleTCPClient(int clntSocket) 13: {
14: char buffer[BUFSIZE]; //에코 문자열을 위한 버퍼 15: ssize_t numBytesRcvd;
16: ssize_t numBytesSent;
17:
18: //클라이언트로부터 메시지 수신 19: numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0);
20: if(numBytesRcvd < 0) 21: {
22: DieWithSystemMessage("recv() failed"); 23: }
24:
25: //수신한 문자열을 전송하고 여분의 데이터를 스트림이 끝날 때까지 수신 26: while(numBytesRcvd > 0) //0일 경우 스트림의 종료를 의미 27: {
28: //클라이언트로 에코메시지를 보냄 29: numBytesSent = send(clntSocket, buffer, numBytesRcvd, 0);
30: //화면에 출력 추가(수만) 31: buffer[numBytesSent] = '\0'; 32: printf("from Client: %s\n", buffer); 33: if(numBytesSent < 0) 34: {
35: DieWithSystemMessage("send() failed"); 36: }
37: else if(numBytesSent != numBytesRcvd) 38: {
39: DieWithUserMessage("send()", "sent unexpected number of bytes"); 40: }
41:
42: //받을 수 있는 데이터가 더 남아있는지 확인 43: numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0);
44: if(numBytesRcvd < 0) 45: {
46: DieWithSystemMessage("recv() failed"); 47: }
48: }
49:
50: close(clntSocket); //클라이언트 소켓 종료 51: }
|
26행의 while문은 데이터를 모두 수신할 때까지 recv()를 반복호출한다.
만약 보낸 데이터가 20Bytes라고 현재 수신된 데이터가 10Bytes인 경우 recv()를 다시 호출하여 데이터를 더 받는다.
★ TCP의 경우 데이터의 경계가 없어 send()를 호출하면, 모든 데이터가 recv()호출시 들어오는 것이 아니다.
send()와 recv(), write()와 read()는 모두 버퍼에 데이터를 읽고 쓰는 함수이다.
send()는 송신버퍼에 데이터를 기록 (버퍼 –> 디바이스)
recv()는 수신버퍼에서 데이터를 읽어옴. (버퍼 <- 디바이스)
참조(Reference) |