반응형


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


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





이번에는 UDP를 통한 서버와 클라이언트 메시지 교환에 대한 프로그래밍이다.


우선 TCP와 UDP의 차이를 몇가지 살펴본다.


1. udp는 커넥션이 없고 메세지 하나로 왔다갔다한다. 즉, tcp는 전화 udp는 편지 같은 역할이다.


2. tcp는 전화를 하기 위해 거는 사람 받는사람을 정하고 하지만 udp는 편지라는 것은 누군가 없어도 그냥 보낼 수 있다.


따라서 udp는 신뢰성이 떨어진다( 데이터그램이 전송되는지 보장 할 수 없고, 데이터그램이 중간에 없어져도 알 수 없다. )


반면에 tcp는 받을 때 까지 신호를 보내기에 신뢰성이 높다.


3. udp는 메세지 자체가 어드레스와 포트를 포함한다.


4. tcp는 바이트 스트림을 주고받지만 udp는 데이터그램을 준다.


datagram : 

basic transfer unit :: 메세지 하나이다. 

패킷을 주고 받는 네트워크에서 데이터 그램이라는 것을 주고받는다.

데이터그램은 일반적으로 헤더payload(내용물)로 나뉘어진다.


5. tcp는 listen(), accept(), connect() 등등이 있는데

udp는 bind()만하면 주고받기가 가능하다(왜냐면 udp는 커넥션이 필요없기 때문)


udp는 한번 바인드하면 클라이언트가 한번 왔을 때 주고받고 끝내고 또 클라이언트 2가 왔을 때

주고받고 할 수 있는 반복적인 구조를 가진다.



이와같이 UDP와 TCP는 조금 차이가 존재한다.


우선 UDP는 데이터 그램을 이용하기 때문에 소켓 생성 시 


socket(AF_INET, SOCK_DGRAM,0); 이라고 제작한다.


AF_INET : IPv4 인터넷 프로토콜.

SOCK_DGRAM : 데이터 그램 형태의 데이터를 받아들인다.



그 외에는 TCP와 같게 작성하면 된다. 물론 listen, accept같은 것들은 없다.



UDP에서는 메시지를 보낼 때, sendto()를 쓰고, 메시지를 받을 때 recvfrom()을 쓴다.


형식은 코드를 보며 확인하도록 한다.


sendto()와 recvfrom()의 인자들에 설명은 아래 사이트에서 참조한다.


sendto() : http://downman.tistory.com/53


recvfrom() : http://downman.tistory.com/51


< header.h >

(헤더의 define 부분은 임의로 작성한것이고, 다른 방식으로 이용해도 된다.)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//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>
 
# 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




< server.c >


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
//server.cpp
 
# include "header.h"
 
int main(int argc, char *argv[])
{
    char readBuff[BUFFER_SIZE];
    char sendBuff[BUFFER_SIZE];
    struct sockaddr_in serverAddress, clientAddress;
    int server_fd, client_fd;
    int client_addr_size;
    ssize_t receivedBytes;
    ssize_t sentBytes;
 
    /*
    if (argc != 2)
    {
    printf("사용법 : ./filename 포트번호 \n");
    exit(0);
    }
    */
 
    socklen_t clientAddressLength = 0;
 
    memset(&serverAddress, 0sizeof(serverAddress));
    memset(&clientAddress, 0sizeof(clientAddress));
 
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddress.sin_port = htons(20162);
 
 
    // 서버 소켓 생성 및 서버 주소와 bind
 
    // 서버 소켓 생성(UDP니 SOCK_DGRAM이용)
    if ((server_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1// SOCK_DGRAM : UDP
    {
        printf("Sever : can not Open Socket\n");
        exit(0);
    }
 
    // bind 과정
    if (bind(server_fd, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) < 0)
    {
        printf("Server : can not bind local address");
        exit(0);
    }
 
 
    printf("Server: waiting connection request.\n");
 
    while (1)
    {
 
        // 클라이언트 IP 확인
        struct sockaddr_in connectSocket;
        socklen_t connectSocketLength = sizeof(connectSocket);
        getpeername(client_fd, (struct sockaddr*)&clientAddress, &connectSocketLength);
        char clientIP[sizeof(clientAddress.sin_addr) + 1= { };
        sprintf(clientIP, "%s", inet_ntoa(clientAddress.sin_addr));
        // 접속이 안되었을 때는 while에서 출력 x
        if(strcmp(clientIP,"0.0.0.0"!= 0)
            printf("Client : %s\n", clientIP);
 
 
        //채팅 프로그램 제작
        client_addr_size = sizeof(clientAddress);
 
        receivedBytes = recvfrom(server_fd, readBuff, BUFF_SIZE, 0, (struct sockaddr*)&clientAddress, &client_addr_size);
        printf("%lu bytes read\n", receivedBytes);
        readBuff[receivedBytes] = '\0';
        fputs(readBuff, stdout);
        fflush(stdout);
 
        sprintf(sendBuff, "%s", readBuff);
        sentBytes = sendto(server_fd, sendBuff, strlen(sendBuff), 0, (struct sockaddr*)&clientAddress, sizeof(clientAddress));
    }
 
    // 서버 소켓 close
    close(server_fd);
 
    return 0;
}
 
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus







< client.c >


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
//client.cpp
 
# include "header.h"
 
int main(int argc, char* argv[])
{
    /*
    if(argc != 2)
    {    // argv[0]에 ./client가 들어간다.
    printf("사용법 : %s IPv4-address\n", argv[0]);
    return -1;
    }
    */
 
    int client_socket;
    struct sockaddr_in serverAddress;
    int server_addr_size;
    char sendBuff[BUFF_SIZE];
    char readBuff[BUFF_SIZE];
 
 
    ssize_t receivedBytes;
    ssize_t sentBytes;
 
 
    memset(&serverAddress, 0sizeof(serverAddress));
 
    serverAddress.sin_family = AF_INET;
    inet_aton("127.0.0.1", (struct in_addr*&serverAddress.sin_addr.s_addr);
    serverAddress.sin_port = htons(20162);
 
    // 소켓 생성
    if ((client_socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
    {
        printf("socket 생성 실패\n");
        exit(0);
    }
 
    while (1)
    {
        // 채팅 프로그램 제작
 
        server_addr_size = sizeof(serverAddress);
        //클라이언트에서 메세지 전송
        printf("클라이언트에서 보낼 말을 입력하세요 :: ");
 
        char msg[BUFF_SIZE];
        fgets(msg, BUFF_SIZE, stdin);
 
        sprintf(sendBuff, "%s", msg);
 
        sentBytes = sendto(client_socket, sendBuff, strlen(sendBuff), 0, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
 
 
        receivedBytes = recvfrom(client_socket, readBuff, BUFF_SIZE, 0, (struct sockaddr*)&serverAddress, &server_addr_size);
        printf("%lu bytes read\n", receivedBytes);
        readBuff[receivedBytes] = '\0';
        fputs(readBuff, stdout);
        fflush(stdout);
 
    }
 
    // 소켓 close
    close(client_socket);
    return 0;
 
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus





< 결과 화면 >


서버가 연결된 상태이다.



서버와 클라이언트가 연결되어 메시지를 클라이언트가 보냈더니 서버가 다시 읽고 보내주는 상태이다.









listen(), accept()같은 과정이 없기에 서버가 끊겨있어도 클라이언트에서 메시지를 보낼 수 있다.

하지만, 저 메시지는 어디로 가는지 모르는 데이터이고, UDP의 낮은 신뢰성을 보여주는 하나의 예이다.


반응형