티스토리 뷰


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include “lnp.h”
int
main (int argc, char **argv)
{
    int i, maxi, listenfd, connfd, sockfd;
    int nready, client[FD_SETSIZE]; //client배열은 파일디스크립터 번호를 담는 크기 256의 배열
    ssize_t n; //ssize_t == unsigned int
    fd_set rset, allset; //나중에 allset에서 rset으로 옮겨 담을것임.
/* allset과 rset???
맨 처음에 rset에는 리슨 소켓과 커넥트 소켓이 들어있다. 그런데 프로그램이 수행 되면서 리슨소켓에 온 요청을 처리하면서 기존에 있던 소켓 fd + 새롭게
만들어진 커넥트 소켓의 fd가 allset에 들어가게 된다. 새롭게 추가된 커넥트 소켓에 대해서는 다음 반복문에서 readable한지 검사를 할 것이고,
이번 반복문에서는 새롭게 추가된 커넥트 소켓이 아닌 기존에 존재하던 리슨소켓+커넥트소켓이 들어있는 rset에 대해서만 select를 호출하겠다는 의미이다.
기존에 있던 커넥트 소켓중에 클라이언트로 부터 FIN을 받은 소켓이 있다면 그 소켓은 즉시 allset에서 제외되고 다음 반복문 첫부분에서
rest= allset으로 rset은 새롭게 덮여 씌워 지게 된다.
*/
    char buf[MAXLINE]; //에코를 받을 버퍼
    socklen_t clilen;
    
    listenfd = Socket (AF_INET, SOCK_STREAM, 0);
    bzero (&servaddr, sizeof(servaddr));
    servaddr.sin_famliy = AF_INET;
    servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
    servaddr.sin_port = htons (SERV_PORT);
    Bind (listenfd, (SA *&servaddr, sizeof(servaddr)); // 리슨 소켓만드는 과정
 
    Listen (listenfd, LISTENQ);
    maxfd = listenfd; /* 처음 만들때는 리슨소켓의 fd가 가장큰 fd번호 일것이므로(3번)*/
    maxi = -1/* index into client[] array */
    for (i = 0; i < FD_SETSIZE; i++)
        client[i] = -1// client배열을 모두 -1로 초기화
    FD_ZERO (&allset);
    FD_SET (listenfd, &allset); //리슨소켓 수신 검사
/*
아래 for문의 역할
리슨 소켓이 readable해 졌는지 select로 확인해서 select가 리턴 되는 경우 complete큐에 있는 클라이언트의
연결 요청을 하나씩 accept한다. 클라이언트와의 커넥트 소켓은 client라는 배열에 커넥트소켓fd를 넣어서 관리한다.
이 배열이 필요한 이유는 서버가 통신할수 있는 최대 클라이언트의 숫자를 제한하기 위해서이다.
allset이라는 fd_set에 파일들이 추가 되다가 나중에 rset에 업데이트 된다.
첫번째 무한 반복 for문 안에서 34번라인이 시작되는데, 이 if문은 complete큐에 엔트리가 모두 사라질때까지 반복된다.
그리고 나서 아래 for문에서 커넥트 소켓에 대한 데이터 송수신처리를 해준다.
우선적으로 리슨 소켓에 들어온 클라이언트의 연결요청을 모두 처리해 주고 나서 데이터를 송수신 하는 것이다.
리슨 소켓은 rset에 넣고 커넥트 소켓은 allset에 넣었다가 나중에 allset을 rset에 덮어씌워준다.
client배열은 서버 프로세스가 관리하는 커넥트 소켓이라고 생각하면 될것 같다.
*/ 
    for ( ; ; ) {
        rset = allset; //allset을 rset에 옮겨 담기
        nready = Select (maxfd + 1&rset, NULLNULLNULL); //nready에는 readable한 파일의 개수 리턴
        if (FD_ISSET(listenfd, &rset)) {//클라이언트가 tcp3way핸드쉐이킹을 성공시켜서 complete큐에 엔트리가 옮겨진 경우 select 블락에서 깨어남.
            clilen = sizeof (cliaddr);
            connfd = Accept (listenfd, (SA *&cliaddr, &clilen); // 커넥트 소켓을 만든다. 이때 accept는 블락 되지 않는다.
            for ( i = 0; i < FD_SETSIZE; i++) {
                if (client[i] < 0) {
                    client[i] = connfd; //client배열에 -1인곳에 방금 만들어진 커넥트 소켓 번호인 fd를 덮어씌운다.
                    break;
                }
        
            if (i == FD_SETSIZE)
                err_quit (“too many clients”); //client배열은 256사이즈 이므로 너무 많은 클라이언트와 연결이 설정되면 무리가 오게 된다.
            FD_SET(connfd, &allset);
            if ( connfd > maxfd)
                maxfd = connfd; /*셀렉트의 첫번째 인자로 들어갈 maxfd값은 방금 만들어진 커넥트 소켓의 fd일것이다.*/
            if (i > maxi)
                maxi = i; /* maxi는 클라이언트 배열의 최대 인덱스값*/
            if (--nready <= 0)
                continue;/* 만약 complete큐에 3개의 엔트리가 있으면 3개를 모두 accetp시켜서 커넥트 소켓으로 만들때까지는 커넥트 소켓의 데이터를 읽지 않는다.*/
        }
/*
아래 for문 설명
client[256] 배열에 클라이언트 와의 커넥스 소켓 디스크립터들이 들어있는데, 앞에서부터 하나씩 검색하면서 readable해진 커넥트 소켓이 있는지 검사한다.
검색하다가 readable해진 커넥트 소켓이 있는 경우 데이터를 읽고 쓴다. 그 커넥트 소켓이 클라이언트로부터 FIN을 받은 소켓이었을때 read를 하면 0이 리턴되고
그런 경우 close(sockfd)를 통해서 reference count를 1 줄여 줌으로써 마찬가지로 서버소켓도 종료시켜준다. 그리고 나서 FD_CLR로 allset에서
readable검사를 빼준다. 그리고 클라이언트 배열에서 이제 그 커넥트 소켓은 없으므로 -1을 써준다.
아래 for문에 들어갈때 rset에는 위에서 생성된 커넥트 소켓들이 모두 들어있을것이고 리슨 소켓도 들어있을것이다. 그리고
client배열에는 커넥트 소켓의 fd만 들어있을것이다.
*/
 
        for (i = 0; i <= maxi; i++) {  //커넥트 소켓이 readable한 경우 이쪽에서 처리됨.
            if ( (sockfd = client[i] ) < 0
                continue;  // client배열 0번 인덱스부터 검색을 하면서 커넥트소켓fd를 찾음.
            if (FD_ISSET (sockfd, &rset) ) { //커넥트 소켓이 3개있고 각 4,5,6번 fd를 가진다할때 4번에 데이터온지 검사 5번 검사 6번검사 이런식으로 돌아간다.
                if ( (n = Read (sockfd, buf, MAXLINE)) == 0) { //클라이언트로부터 FIN메세지를 받은경우
                    Close (sockfd);    /* connection closed by client */
                    FD_CLR (sockfd, &allset);
                    client[i] = -1;
                } else
                    Writen (sockfd, buf, n); //클라이언트로부터 데이터를 받은경우 에코
                if (--nready <= 0)
                    break/*셀렉트에서 리턴 될때 nready값이 설정됨. 이 값은 몇개의 소켓이 readable해졌는지를 의미함.*/
            }
          }
    }
    
 
 
 
}








'컴퓨터 공학과 졸업 > 소켓 프로그래밍' 카테고리의 다른 글

소켓 옵션  (0) 2017.11.14
poll시스템콜  (0) 2017.11.13
shutdown과 select를 활용한 개선된 에코 클라이언트 프로그램  (0) 2017.11.10
shutdown  (0) 2017.11.10
I/O 멀티플렉싱(select)  (0) 2017.11.09
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함