반응형



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


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





이전까지는 헤더의 내용 없이 그냥 보내고 받는 것만 보았는데,


이번에는 writeHeader, readHeader을 두어


writeHeader에서는 클라이언트가 접속하면 클라이언트에게 문자열의 길이를 헤더에 포함하여 문자열을 전송하고,


readHeader에서는 메시지의 헤더에 포함된 문자열의 길이를 이용해 메시지를 해독하도록 한다.



< server.c >에서 핵심 내용은 다음과 같다.


writeHeader은 먼저 길이를 보내고 난 뒤, 그다음 문자열을 보내는 역할을 한다.


이때 writeBytes = writen(sockFD, (char*)&messageLength, sizeof(size_t));에서 문자의 길이를 보내고,


writeBytes = writen(socketFD, buffer, length);를 통해 size_t 크기만큼 writen을 통해 계속해서 buffer에 write로 문자를 작성한다.




< client.c >에서 핵심 내용은 다음과 같다.


readHeader은 먼저 길이를 수신하고 난 뒤, 그다음 문자열을 수신하는 역할을 한다.


이때 readBytes = readn(socketFD, (char*)&messageLength, sizeof(size_t));에서 문자의 길이를 받아들이고, 


readBytes = readn(socketFD, buffer, max);와 readBytes = readn(socketFD, buffer, messageLength);를 통해 문자열을 수신한다.





<header.h>


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
//header.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <errno.h>
 
#define PORT 10001
#define BUFFER_LEN 100
#define CHAT_SIZE 1024
#define BUFF_SIZE 1024
#define LISTEN_QUEUE_SIZE 5
 
 
ssize_t writen(int socketFD, const char *buffer, size_t fixedLength);
 
ssize_t readn(int socketFD, char* buffer, size_t fixedLength);
 
ssize_t readLine(int socketFD, char *buffer, size_t max);
 
ssize_t readHeader(int socketFD, char *buffer, size_t max);
ssize_t writeHeader(int socketFD, const char* buffer, size_t length);
 
//                                                       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
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//server.cpp
# include "header.h"
 
ssize_t writen(int socketFD, const char *buffer, size_t fixedLength)
{
    size_t leftBytes = fixedLength;
    ssize_t writenBytes = 0;
    const char *readingPointer = buffer;
 
    while(leftBytes > 0)
    {
        if((writenBytes = write(socketFD, readingPointer, leftBytes)) <= 0)
        {
            if(errno == EINTR)
                writenBytes = 0// Write again
            else
                return -1;
        }
        else
        {
            leftBytes -= writenBytes;
            readingPointer += writenBytes;
            // Write n bytes
        }
    }
 
    return fixedLength - leftBytes;
}
 
ssize_t writeHeader(int socketFD, const char* buffer, size_t length)
{
        size_t messageLength = htonl(length);
        ssize_t writeBytes;
 
    // 길이를 먼저 보낸다.
        writeBytes = writen(socketFD , (char*)&messageLength, sizeof(size_t));
 
        if(writeBytes != sizeof(size_t))
               return writeBytes < -0;       
 
    // 그다음 문자열을 보낸다.
        writeBytes = writen(socketFD, buffer, length);
 
        if(writeBytes != length)
               return writeBytes < -0;
 
        return writeBytes;
}
    
int main(int argc, char *argv[]) 
{
 
 
    //서버의 listen 소켓 데이터 구조 생성과정
    char buffer[BUFFER_LEN];
    struct sockaddr_in server_addr, client_addr;
    char temp[20];
    char chat_data[CHAT_SIZE];
    int server_fd, client_fd;
    int len, msg_size;
    char test[20];
    char Quit[5= "quit";
 
    if (argc != 2)
    {
        printf("사용법 : ./filename 포트번호 \n");
        exit(0);
    }
 
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        printf("Server: can not Open Socket\n");
        exit(0);
    }
 
    //listen file descriptor 선언
 
    // memset은 모든 값을 0으로 초기화 해주기위해 서버 실행 시 이용한다.
    memset(&server_addr, 0x00sizeof(server_addr));
 
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(atoi(argv[1]));
 
    //bind 과정
 
    if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        printf("Server: can not bind local address\n");
        exit(0);
    }
    printf("Waiting for clients...\n");
 
    //listen 과정
    if (listen(server_fd, 5< 0
    {
        printf("Server: can not listen connect. \n");
        exit(0);
    }
 
    memset(buffer, 0x00sizeof(buffer));
    len = sizeof(client_addr);
 
    printf("=====[PORT] : %d =====\n", atoi(argv[1]));
    printf("Server: waiting connection request.\n");
 
 
 
 
    // accept 기다리는 과정
    while (1
    {
 
        struct sockaddr_in connectSocket, peerSocket;
        socklen_t connectSocketLength = sizeof(connectSocket);
        //클라이언트의 접속을 허가하여 소켓 ID(connectFD)를 set 하시오
        getpeername(client_fd, (struct sockaddr*)&peerSocket, &connectSocketLength);
        char peerName[sizeof(peerSocket.sin_addr) + 1= {0};
        sprintf(peerName, "%s", inet_ntoa(peerSocket.sin_addr));
        printf("Client : %s\n", peerName);
 
 
 
        //클라이언트를 accept하는 과정
        client_fd = accept(server_fd, (struct sockaddr *)&client_addr, (socklen_t *)&len);
 
        if (client_fd < 0)
        {
            printf("Server: accept failed\n");
            exit(0);
        }
 
        
        inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, temp, sizeof(temp));
        printf("Server: %s client connect,\n", temp);
        
        
        //서버에서 메세지 전송
        printf("서버에서 보낼 말을 입력하세요 :: ");    
        char msg[BUFFER_LEN];
        fgets(msg,BUFFER_LEN,stdin);
        char buffer[BUFFER_LEN] = {0};
        sprintf(buffer,"%s",msg);
        writeHeader(client_fd, buffer, strlen(buffer));
        //클라이언트 접속 종료
        printf("Server: %s client closed.\n", temp);
        close(client_fd);
    }
 
    //서버 listen socket 종료
    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
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//client.cpp
# include "header.h"
 
ssize_t readLine(int socketFD, char *buffer, size_t max)
{
    ssize_t readBytes, totalRead;
    char *writingBuffer = buffer;
    char c;
 
    for(totalRead = 1; totalRead < max; totalRead++)
    {
        if( (readBytes = read(socketFD, &c, 1)) == 1)
        {
            *writingBuffer = c;
            writingBuffer++;
    
            if(c == '\n')
                break// Get the new line character
        }
        else if(readBytes == 0
        {
            if(totalRead == 1)
                return 0// EOF, and no data in the line
            else
                break// EOF, and some data in the line
        }
        else
        {
            if(errno == EINTR)
            {
                totalRead--;
                continue// Read again
            }
            else
                return -1// Real error
        }
    }
 
    *writingBuffer = 0// EOF for the buffer
    return totalRead;
}
 
 
ssize_t readn(int socketFD, char *buffer, size_t fixedLength)
{
    size_t leftBytes = fixedLength;
    ssize_t readBytes = 0;
    char *writingPointer = buffer;
 
    while(leftBytes > 0)
    {
        if( (readBytes = read(socketFD, writingPointer, leftBytes)) < 0)
        {
            if(errno = EINTR)
                readBytes = 0// Read Again
            else
                return -1// Real error
        }
    
        else if(readBytes == 0)
            break// EOF
    
        else
        {
            leftBytes -= readBytes;    
            writingPointer += readBytes;
            // readBytes bytes are read
        }
    }
 
    return fixedLength - leftBytes;
}
 
 
ssize_t readheader(int socketFD, char* buffer, size_t max)
{
    size_t messageLength;
    ssize_t readBytes;
 
    // 길이를 읽어온다.
    readBytes = readn(socketFD, (char*)&messageLength, sizeof(size_t));
 
    // 데이터를 에러 없이 수신하였는지 확인한다.
    if(readBytes != sizeof(size_t))
    {
        return readBytes < -0;
    }
 
    // ntohl을 통해 messageLength를 이용 가능 하도록 만든다.
    messageLength = ntohl(messageLength);
        
    // 버퍼 최대 크기보다 수신한 message길이가 더 길면
    if(messageLength > max)
    {
        while(messageLength > 0)
        {    
            readBytes = readn(socketFD, buffer, max);        
            if(readBytes != max)
                return readBytes < -0;
 
            messageLength -= max;
            if(messageLength < max)
                max = messageLength;
            // Cleaning the socket buffer
        }
        
        errno = EMSGSIZE;
        return -1;
    }
 
    readBytes = readn(socketFD, buffer, messageLength); // read the message body
    
    if(readBytes != messageLength)
        return readBytes < -0;
 
    else
        return readBytes;
}
 
 
int main(int argc, char** argv) 
{
    if (argc != 2
    {
        printf("Usage: %s IPv4-address\n", argv[0]);
        return -1;
    }
 
    //서버에 접속할 소켓 데이터 구조 생성과정
 
       int   client_socket;
     struct sockaddr_in   server_addr;
       char   buff[BUFF_SIZE+5];
 
    //클라이언트용 소켓(connectSocket)을 set
      client_socket = socket(PF_INET, SOCK_STREAM, 0);
      if(client_socket == -1)
      {
          printf("socket 생성 실패\n");
           exit(1);
       }
 
    //connect file descriptor 선언
 
    // memset은 모든 값을 0으로 초기화 해주기위해 클라이언트 실행 시 이용한다.
     memset( &server_addr, 0sizeof( server_addr));
 
     server_addr.sin_family     = AF_INET;
      server_addr.sin_port       = htons(4000); // 포트번호를 4000으로 임의 지정해두었다.
     server_addr.sin_addr.s_addr= inet_addr("127.0.0.1"); // 서버 ip는 로컬 주소인 127.0.0.1로 지정해두었다.
 
    //서버에 접속하시오
 
      if(connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1)
       {
            printf("접속 실패\n");
            exit(1);
      }
 
    char buffer[BUFFER_LEN];
    int n = readheader(client_socket, buffer, BUFFER_LEN);
    printf("%d bytes read\n", n);
    buffer[n] = '\0';
    fputs(buffer, stdout);
    fflush(stdout);
 
    //클라이언트 접속 종료
     close( client_socket);
 
    return 0;
}
 
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus





< 결과 화면 >










반응형