반응형


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


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




이전 게시물에서 확인한 내용을 토대로 alarm과 UDP를 이용하여 UDP에서 무작정 데이터 그램을 보내는 것이 아닌


서버가 종료되었을 때, 클라이언트에서 이를 감지한 후 반응하는 코드를 제작해 본다.


이전에 알던 UDP는 서버가 열리지 않아도 클라이언트 혼자 메시지를 전송 할 수는 있었지만, 


그 메시지가 어디로 가는지, 서버가 열린지는 확인 할 수 없었다.


하지만, 이 코드에서는 서버가 열리지 않아도 클라이언트가 메시지를 보낼 순 있지만,


서버가 제시간 내에 응답하지 않으면 종료되는 방식을 알아본다.



헤더 파일


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//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 
 
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus



서버 코드에 대한 설명 ::


일반 UDP 서버 코드와 동일하므로 생략한다. (이전 게시물을 참조하면 된다.)


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
//server.cpp
 
# include "header.h"
 
int main() {
 
    struct sockaddr_in serverSocket, clientSocket;
 
    memset(&serverSocket, 0sizeof(serverSocket));
    memset(&clientSocket, 0sizeof(clientSocket));
 
    serverSocket.sin_family = AF_INET;
    serverSocket.sin_addr.s_addr = htonl(INADDR_ANY);
    serverSocket.sin_port = htons(PORT);
 
    int serverSocketFD;
    int client_addr_size;
    ssize_t receivedBytes;
 
    printf("Waiting for clients...\n");
 
    // 서버 소켓 생성(UDP니 SOCK_DGRAM이용)
    if((serverSocketFD = socket(AF_INET, SOCK_DGRAM, 0)) == -1// SOCK_DGRAM : UDP
    {
        printf("Sever : can not Open Socket\n");
        exit(0);
    }
 
 
    if (bind(serverSocketFD, (struct sockaddr *&serverSocket, sizeof(serverSocket)) == -1) {
        printf("Can not bind.\n");
        return -1;
    }
 
 
    char sendBuffer[BUFFER_SIZE];
    char readBuffer[BUFFER_SIZE];
 
    while (1
    {
        // 클라이언트 주소크기를 받아낸다.
        client_addr_size = sizeof(clientSocket);
 
        
        receivedBytes = recvfrom(serverSocketFD, readBuffer, BUFF_SIZE, 0, (struct sockaddr *)&clientSocket, &client_addr_size);
 
        readBuffer[receivedBytes] = '\0';
        printf("%s",readBuffer);
        fflush(stdout);
 
        sprintf(sendBuffer,"%s",readBuffer);
 
        sendto(serverSocketFD, sendBuffer, strlen(readBuffer), 0, (struct sockaddr*)&clientSocket, sizeof(clientSocket));
 
    }
 
    return 0;
}
 
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus





클라이언트 코드에 대한 설명 ::


alarm 시그널은 다음과 같은 코드로 세팅 후 설치한다.


// alarm 세팅

struct sigaction alarmAction;

alarmAction.sa_handler = alarmHandler;

alarmAction.sa_flags = 0;

sigaction(SIGALRM, &alarmAction, NULL);


// alarm 시그널 설치

signal(SIGALRM, alarmHandler);


그 후, while문에서 서버에 문자열을 전송 한 후, alarm(1)을 통해 시그널을 보낸다.


만약 서버에서 답이 없다면 alarm 시그널에 의해 5초가 지나면 타임 아웃과 함께 클라이언트가 종료된다.


서버에서 echo로 보낸 메시지를 수신하게되면 alarm(0)에 의해 시그널이 취소되고 다음 메시지를 보낼 수 있다.


이 과정이 계속반복되어 서버가 열려있는지 닫혀있는지를 판별 할 수 있다.



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
//client.cpp
 
# include "header.h"
 
# define TIMEOUT 5
 
int count = 0;
char sendBuffer[BUFFER_SIZE];
char receiveBuffer[BUFFER_SIZE];
int receiveBytes;
 
void alarmHandler(int sig)
{
    count++;
    if(count == TIMEOUT)
    {
        printf("\ncount :: %d\n",count);
        printf("Time Out\n");
        exit(0);
    }    
    
    // 받은 값이 없으면 alarm 시그널 호출
    if(receiveBytes == 0)
    {
        printf("\ncount :: %d\n",count);
        alarm(1);
    }
 
    else
    {
        //응답을 받았으면 alarm 시그널을 취소.
        alarm(0);
        count = 0;
        receiveBytes = 0;
    }    
}
 
int main(int argc, char** argv) 
{
    // alarm 세팅
    struct sigaction alarmAction;
    alarmAction.sa_handler = alarmHandler;
    alarmAction.sa_flags = 0;
    sigaction(SIGALRM, &alarmAction, NULL);
 
    // alarm 시그널 설치
    signal(SIGALRM, alarmHandler);
 
    if (argc != 2) {
        printf("Usage: %s IPv4-address\n", argv[0]);
        return -1;
    }
 
    struct sockaddr_in serverSocket;
 
    memset(&serverSocket, 0sizeof(serverSocket));
    serverSocket.sin_family = AF_INET;
    inet_aton(argv[1], (struct in_addr*&serverSocket.sin_addr.s_addr);
    serverSocket.sin_port = htons(PORT);
 
    int clientSocketFD = socket(AF_INET, SOCK_DGRAM, 0);
 
    while (1
    {
        //사용자로부터 문자열을 입력을 받는다.
        printf("서버에 보낼 말을 입력하세요 :: ");
        fgets(sendBuffer,BUFF_SIZE,stdin);
 
        //서버에 입력받은 문자열을 전송한다.
        sendto(clientSocketFD, sendBuffer, strlen(sendBuffer), 0, (struct sockaddr*)&serverSocket, sizeof(serverSocket));
 
 
        //alarm 시그널을 이용해 5초 동안 응답이 없으면 에러 메시지를 출력.
        alarm(1);
        
 
        //서버로부터 입력받은 문자열을 출력한다.
        int server_addr_size = sizeof(serverSocket);
 
        receiveBytes = recvfrom(clientSocketFD, receiveBuffer, BUFF_SIZE, 0, (struct sockaddr *)&serverSocket, &server_addr_size);
 
        receiveBuffer[receiveBytes] = '\0';
        printf("%s",receiveBuffer);
        fflush(stdout);
 
    }
    return 0;
}
 
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus









UDP 서버만 열린 상황




클라이언트 각자 서버와 메시지 송수신을 하는 과정





서버가 종료되었을 때 클라이언트가 메시지를 보내면 alarm시그널이 발동하는 과정





TIME OUT 되는 화면. 클라이언트가 종료되었다.





나머지 클라이언트 모두 종료되는 과정






반응형