- 본 내용은 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 ? -1 : 0; // 그다음 문자열을 보낸다. writeBytes = writen(socketFD, buffer, length); if(writeBytes != length) return writeBytes < 0 ? -1 : 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, 0x00, sizeof(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, 0x00, sizeof(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 ? -1 : 0; } // ntohl을 통해 messageLength를 이용 가능 하도록 만든다. messageLength = ntohl(messageLength); // 버퍼 최대 크기보다 수신한 message길이가 더 길면 if(messageLength > max) { while(messageLength > 0) { readBytes = readn(socketFD, buffer, max); if(readBytes != max) return readBytes < 0 ? -1 : 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 ? -1 : 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, 0, sizeof( 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 |
< 결과 화면 >
'Applied > Network' 카테고리의 다른 글
소켓 프로그래밍 - (6) UDP 소켓 통신 응용(닉네임, 최대 인원, 메시지 응용) (2) | 2016.10.06 |
---|---|
소켓 프로그래밍 - (5) UDP 소켓 통신 (3) | 2016.10.06 |
소켓 프로그래밍 - (3) 서버에서 클라이언트로 메세지 전송 (0) | 2016.09.13 |
소켓 프로그래밍 - (2) 서버 및 클라이언트 생성 (0) | 2016.09.13 |
소켓 프로그래밍 - (1) 리눅스에서 코딩 및 Compile 방법 (0) | 2016.09.12 |