1: // TCP/IP 소켓프로그래밍 p.32의 버전업
2: // TCPEchoServer4_v2.c
3: // select:()사용
4: // 2011년 10월 24일
5: #include <stdio.h>
6: #include <stdlib.h>
7: #include <string.h>
8: #include <unistd.h>
9: #include <sys/time.h>
10: #include <sys/types.h>
11: #include <sys/socket.h>
12: #include <netinet/in.h>
13: #include <arpa/inet.h>
14:
15:
16: #define MAXCLIENT 2 // 손님 두 명까지 서비스 가능
17:
18: int main()
19: {
20: int iRet; // 리턴값 임시 저장
21: int iCNum = 0; // 현재 접속한 클라이언트수
22: int iCnt; // 반복 제어변수
23: int iDs; // 랑데뷰 소켓 디스크립터
24: int iMaxDs; // 최대 파일 디스크립터수
25: int iAccept; // 커뮤니케이션 소켓 디스크립터
26: int iaClient[MAXCLIENT]; // 커뮤니케이션 소켓 디스크립터 배열
27: int iAddrSize; // 구조체 크기
28: struct sockaddr_in stAddr; // 랑데뷰 소켓 IP주소
29: struct sockaddr_in stAccept;// 커뮤니케이션 소켓 IP주소
30: fd_set fsStatus; // 디스크립터 벡터
31: unsigned char ucBuf[256]; // 문자열 저장 버퍼
32:
33: // 1. socket() - 소켓 생성 (열기)
34: iDs = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // IP, TCP, TCP
35: if(-1 == iDs)
36: { // 소켓 생성 실패시 에러메시지 출력 후 종료
37: perror("socket() failed");
38: return -1; // 1단계 에러
39: }
40:
41: // 2. 구조체 초기화
42: bzero(&stAddr, sizeof(struct sockaddr_in)); // 구조체를 0으로 채움.
43: stAddr.sin_family = PF_INET; // IPv4 주소 패밀리
44:
45: // ----- 첫 인자 참조해 IP주소 dotted decimal 주소 -> 32bit주소
46: iRet = inet_pton(PF_INET, "127.0.0.1", &stAddr.sin_addr.s_addr);
47: if(0 >= iRet) // 0이거나 음수이면 에러 man page참조
48: {
49: perror("inet_pton() failed");
50: close(iDs);
51: return -2; // 2단계 에러
52: }
53:
54: printf("IP : %s\n", inet_ntoa(stAddr.sin_addr));
55: stAddr.sin_port = htons(3000); // 서버의 포트번호, 리틀 -> 빅 엔디안
56:
57: // 3. bind() - 지역 주소에 바인드
58: iRet = bind(iDs, (struct sockaddr *)&stAddr, sizeof(struct sockaddr_in));
59: if(-1 == iRet)
60: {
61: perror("bind() failed");
62: close(iDs);
63: return -3; // 3단계 에러
64: }
65:
66: // 4. listen() - 소켓이 들어오는 요청을 처리할 수 있도록 설정
67: iRet = listen(iDs, 5); // 5개 초과시 접속거부...확인은 불가
68: if(-1 == iRet)
69: {
70: perror("listen() failed");
71: close(iDs);
72: return -4; // 4단계 에러
73: }
74:
75: // 5. select() - 멀티플렉싱
76: iMaxDs = iDs + 1; // 검사할 파일디스크립터 개수
77:
78: while(1)
79: {
80: printf("현재 접속자수: %d 명\n", iCNum);
81: FD_ZERO(&fsStatus); // 구조체 0으로 초기화
82: FD_SET(iDs, &fsStatus); // 그룹1...3번 - 랑데뷰소켓 감시
83: FD_SET(0, &fsStatus); // 그룹2...0번 - 키보드도 감시
84: // 그룹3...접속한 커뮤니케이션 소켓디스크립터 세트
85: for(iCnt = 0 ; iCNum > iCnt ; ++iCnt)
86: {
87: FD_SET(iaClient[iCnt], &fsStatus);
88: }
89:
90: printf("TEST...select() 시작!!\n");
91: // select()는 인터럽트와 폴링방식을 혼합한 것.
92: iRet = select(iMaxDs, &fsStatus, 0, 0, 0); // 입력x시 블록
93: if(-1 == iRet)
94: {
95: perror("select() failed");
96: close(iDs);
97: return -5; // 5단계 에러
98: }
99:
100: printf("TEST...select() 끝!!\n");
101:
102: if(1 == FD_ISSET(0, &fsStatus)) // 키보드입력 있나?
103: { // 키보드는 항상 0번
104: iRet = read(0, ucBuf, sizeof(ucBuf));
105: ucBuf[iRet] = 0; // 문자열 끝을 나타냄.
106:
107: for(iCnt = 0 ; iCNum > iCnt ; ++iCnt)
108: { // 접속자 모두에게 메시지 전송
109: write(iaClient[iCnt], ucBuf, iRet);
110: }
111: continue;
112: }
113:
114: if(1 == FD_ISSET(iDs, &fsStatus)) // 랑데뷰 소켓으로 새로운 접속시
115: {
116: // 6. accept() - 클라이언트와 연결
117: iAddrSize = sizeof(struct sockaddr_in);
118: iAccept = accept(iDs, (struct sockaddr *)&stAccept, &iAddrSize);
119: if(-1 == iAccept)
120: {
121: perror("accept() failed");
122: close(iDs);
123: // 열려진 커뮤니케이션 디스크립터 닫기
124: for(iCnt = 0 ; iCNum > iCnt ; ++iCnt)
125: {
126: close(iaClient[iCnt]);
127: }
128:
129: return -6; // 6단계 에러
130: }
131:
132: if(MAXCLIENT <= iCNum) // 최대 접속자수 제한
133: {
134: write(iAccept, "Server is busy. 저리가~ \n", sizeof("Server is busy. 저리가~ \n"));
135: close(iAccept); // 클라이언트의 접속을 끊음.
136: continue;
137: }
138:
139: if(iAccept == iMaxDs) // 새로 접속한 소켓번호가 최고 번호보다 1 큰 경우
140: {
141: iMaxDs = iAccept + 1; // 최대 접속수 갱신
142: }
143:
144: iaClient[iCNum] = iAccept;
145: ++iCNum;
146: printf("Client IP : %s\n", inet_ntoa(stAccept.sin_addr));
147: continue; // 랑데뷰소켓인 경우 아래 명령 처리x
148: }
149:
150: // 랑데뷰소켓으로 접속이 아닌 경우
151: // 커뮤니케이션소켓으로 접속...데이터전송 (채팅)
152: printf("커뮤니케이션소켓 - 클라이언트 데이터전송\n");
153:
154: for(iCnt = 0 ; iCNum > iCnt ; ++iCnt)
155: { // 접속자 모두 검사해 데이터를 보내온 소켓처리 (폴링방식)
156: if(1 == FD_ISSET(iaClient[iCnt], &fsStatus))
157: { // 해당소켓번호에 데이터가 들어옴.
158: // 6. read() - 클라이언트로부터 수신
159: iRet = read(iaClient[iCnt], ucBuf, sizeof(ucBuf));
160: ucBuf[iRet] = 0; // 문자열 끝을 나타냄.
161: printf("%d: %s\n", iaClient[iCnt], ucBuf);
162:
163: for(iCnt = 0 ; iCNum > iCnt ; ++iCnt)
164: { // 접속자 모두에게 메시지 전송
165: write(iaClient[iCnt], ucBuf, iRet);
166: }
167: }
168: }
169: }
170:
171: write(iAccept, "Pong", sizeof("Pong"));
172:
173: close(iAccept); // 커뮤니케이션 소켓 소멸 (닫기)
174: close(iDs); // 랑데뷰 소켓 소멸 (닫기)
175:
176: return 0;
177: }