반응형


- 본 내용은 Linux (Ubuntu 14.04 lts)를 기반으로 제작되었습니다. -


소캣 프로그래밍은 TCP/IP 기반으로 하였습니다.





서버 코드에 대한 설명 ::


시그널 SIGCHLD를 통해 자식 프로세스가 종료될 때까지 부모 프로세스는 종료되지 않도록 시그널을 설치한다.


waitpid(-1, &status, WNOHANG);의 자세한 의미는


http://cky5122.blog.me/80198083088 다음 사이트를 참조한다.


이렇게 이전에 배운 TCP/IP 구조대로 서버는 소켓 생성, 바인딩, 리스닝을 실행한다.


(클라이언트는 소켓 생성, 서버 접속을 실행한다.)

이제 서버측에서 어셉트를 하게되는데 클라이언트 요청에 의해 어셉트를 하게 되면 


fork()를 통해 자식 서버를 생성한다.


그리고 fork()의 반환값인 pid가 0이면 자식 서버이므로 자식 서버는 리스닝 소켓을 닫아준다.


그리고 pid가 0이 아니면 자식의 process id를 받은 부모이므로 커넥트 소켓을 닫아준다.


자식 서버는 그다음 while문을 통해 계속해서 클라이언트에서 송신해오는 값을 읽게 되고,


부모 서버는 자식 서버가 모두 닫힐 때 까지 기다린다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
//header.h
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
 
#define PORT 20162
#define BUFFER_SIZE 4096
#define BUFF_SIZE 100 
Crocus



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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//server.cpp
 
# include "header.h"
 
# define LISTEN_QUEUE_SIZE 5
 
void childHandler(int signal)
{
    
    int status;
    pid_t spid;
 
    // -1 :: 어떠한 child process모두 다 받는다. WNOHANG :: 자식 프로세스가 종료되지 않아도 부모는 자신할 일 한다.
    while((spid = waitpid(-1&status, WNOHANG)) > 0)
    {
        printf("자식프로세스 wait한 결과 \n");
        printf("================================\n");
        printf("PID         : %d\n", spid);
        printf("Exit Value  : %d\n", WEXITSTATUS(status));
        printf("Exit Stat   : %d\n", WIFEXITED(status));
    }
}
 
int main() {
    
    //childHandler 함수가 SIGCHLD 시그널을 처리할 수 있도록 시그널 설치
 
    signal(SIGCHLD, (void *)childHandler);    
 
    struct sockaddr_in listenSocket;
 
    memset(&listenSocket, 0sizeof(listenSocket));
 
    listenSocket.sin_family = AF_INET;
    listenSocket.sin_addr.s_addr = htonl(INADDR_ANY);
    listenSocket.sin_port = htons(PORT);
 
    int listenFD = socket(AF_INET, SOCK_STREAM, 0);
    int connectFD;
 
    ssize_t receivedBytes;
    char readBuff[BUFFER_SIZE];
    char sendBuff[BUFFER_SIZE];
    pid_t pid;
 
 
    if (bind(listenFD, (struct sockaddr *&listenSocket, sizeof(listenSocket)) == -1) {
        printf("Can not bind.\n");
        return -1;
    }
 
    if (listen(listenFD, LISTEN_QUEUE_SIZE) == -1) {
        printf("Listen fail.\n");
        return -1;
    }
 
    printf("Waiting for clients...\n");
 
    while (1
    {
        struct sockaddr_in connectSocket, peerSocket;
 
        socklen_t connectSocketLength = sizeof(connectSocket);
 
        while((connectFD = accept(listenFD, (struct sockaddr*)&connectSocket, (socklen_t *)&connectSocketLength)) >= 0)
        {
            getpeername(connectFD, (struct sockaddr*)&peerSocket, &connectSocketLength);
 
            char peerName[sizeof(peerSocket.sin_addr) + 1= { };
            sprintf(peerName, "%s", inet_ntoa(peerSocket.sin_addr));
 
            // 접속이 안되었을 때는 출력 x
            if(strcmp(peerName,"0.0.0.0"!= 0)
                printf("Client : %s\n", peerName);
        
 
            if (connectFD < 0)
            {
                printf("Server: accept failed\n");
                exit(0);
            }
            // 클라이언트가 접속할 때마다 fork를 통해 child process를 생성해 echo를 발생.
            pid = fork();
 
            // 자식 서버일 때
            if(pid == 0)
            {    
                // 리스닝 소켓은 닫아준다.
                close(listenFD);
 
                ssize_t receivedBytes;
 
                // read할 값이 있다면 계속 읽어들인다.
                while((receivedBytes = read(connectFD, readBuff, BUFF_SIZE)) > 0)
                {                
 
                    printf("%lu bytes read\n", receivedBytes);
                    readBuff[receivedBytes] = '\0';
                    fputs(readBuff, stdout);
                    fflush(stdout);
    
                    sprintf(sendBuff,"%s",readBuff);
                    write(connectFD, sendBuff, strlen(sendBuff));
                }
                
                // 클라이언트가 종료되면 해당 자식 프로세스의 커넥팅 소켓도 종료
                close(connectFD); 
                return 0// 혹은 exit(0); 이용
    
            }
 
            // 부모 서버라면 커넥팅 소켓을 닫아준다. 
            else
                close(connectFD);
        }
        
    }
    close(listenFD);
 
    return 0;
}
    
 
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus









클라이언트 소스 코드 설명 ::


이전에 제작하던 클라이언트와 같으므로 설명을 생략한다.



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
//client.cpp
 
# include "header.h"
 
int main(int argc, char** argv)
{
    if (argc != 2
    {
        printf("Usage: %s IPv4-address\n", argv[0]);
        return -1;
    }
 
    struct sockaddr_in connectSocket;
 
    memset(&connectSocket, 0sizeof(connectSocket));
 
    connectSocket.sin_family = AF_INET;
    inet_aton(argv[1], (struct in_addr*&connectSocket.sin_addr.s_addr);
    connectSocket.sin_port = htons(PORT);
 
    int connectFD = socket(AF_INET, SOCK_STREAM, 0);
 
    if (connect(connectFD, (struct sockaddr*&connectSocket, sizeof(connectSocket)) == -1
    {
        printf("Can not connect.\n");
        return -1;
    }
 
    else 
    {
        int readBytes, writtenBytes;
        char sendBuffer[BUFFER_SIZE];
        char receiveBuffer[BUFFER_SIZE];
 
        while (1
        {
            //서버에 문자열을 보낸 뒤 서버가 보낸 echo를 받아 출력.
            printf("서버에 보낼 말을 입력하세요 :: ");
 
            fgets(sendBuffer,BUFF_SIZE,stdin);
 
            write(connectFD, sendBuffer, strlen(sendBuffer));
            
            readBytes = read(connectFD, receiveBuffer, BUFF_SIZE);
            printf("%d bytes read\n", readBytes);
            receiveBuffer[readBytes] = '\0';
            fputs(receiveBuffer, stdout);
            fflush(stdout);
 
        }
    }
 
    close(connectFD);
 
    return 0;
}    
    
                    
    
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus





처음 서버만 실행된 장면




서버에 클라이언트 하나가 접속 후, 메시지를 보내면 echo로 다시 수신받는 상황




나머지 클라이언트 모두 연결




나머지 클라이언트도 모두 따로 통신됨을 알 수 있다.(하나의 서버 터미널로 통해(자식 서버가 3개 생성된 상태))




처음으로 클라이언트를 하나 종료시켰을 때 상황




나머지 클라이언트 모두 종료한 상황





클라이언트 2개 접속 시 pstree 화면, tc(클라이언트)는 2개가 생성되었고


ts(서버)는 ts1개에 자식 프로세스 ts가 fork()되어 2개 생성됨을 볼 수 있다.







반응형