일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- c# winform
- c#
- Data Structure
- 유니티
- 다트 언어
- git
- github
- C# delegate
- C++
- jupyter
- 포인터
- Houdini
- C언어 포인터
- 깃
- Flutter
- 도커
- vim
- c언어
- c# 추상 클래스
- Unity
- gitlab
- Algorithm
- dart 언어
- 구조체
- HTML
- 플러터
- jupyter lab
- c# 윈폼
- docker
- Python
Archives
- Today
- Total
nomad-programmer
[Programming/Network] UDP를 이용한 Time Server & Client 본문
클라이언트에서 서버로 현재 시간을 알려달라는 명령을 요청하면 서버에서 이를 처리하여 서버의 현재 시간을 클라이언트에게 보내주는 간단한 타임서버이다.
타임서버의 존재는 모든 클라이언트의 로컬 시간이 각기 다를 수 있는 문제점이 잠재적으로 있을 수 있기에 타임 서버를 둠으로써 모든 클라이언트의 시간을 동일하게 맞출 때 존재 의미가 있다.
Server 구현
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
#define PORT 3333
#define BUF_SIZE 100
int main(int argc, char* argv[]) {
WSADATA wsaData;
SOCKET servSock;
SOCKADDR_IN servAdr, clntAdr;
char message[BUF_SIZE];
char curTime[BUF_SIZE];
int strLen;
int clntAdrSz;
time_t t;
struct tm tm;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
fputs("wsastartup() error\n", stderr);
exit(1);
}
// 소켓 설정, SOCK_DGRAM으로 안하여 UDP로 설정됨.
servSock = socket(PF_INET, SOCK_DGRAM, 0);
if (servSock == INVALID_SOCKET) {
WSACleanup();
fputs("upd 소켓 생성 실패\n", stderr);
exit(1);
}
memset(&servAdr, 0, sizeof(servAdr));
servAdr.sin_family = AF_INET;
servAdr.sin_addr.s_addr = htonl(INADDR_ANY);
servAdr.sin_port = htons(PORT);
if (bind(servSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR) {
WSACleanup();
fputs("bind error\n", stderr);
exit(1);
}
while (1) {
clntAdrSz = sizeof(clntAdr);
// recvfrom : 데이터 수신 함수
// servSock : 데이터를 수신할 때 사용할 소켓의 파일 디스크립터
// message : 수신할 데이터를 저장할 버퍼
// BUF_SIZE : 수신할 수 있는 최대 바이트 수
// 0 : flags이며 옵션 설정하는데 필요한 인자. 일반적으로 0
// (SOCKADDR*)&clntAdr : 주소 정보 구조체 변수의 포인터. 함수 호출이 끝나면 데이터를 전송한 호스트의 주소 정보로 채워짐
// &clntAdrSz : (SOCKADD*)&clntAdr 포인터가 가리키고 있는 구조체 변수의 크기
strLen = recvfrom(servSock, message, BUF_SIZE, 0, (SOCKADDR*)&clntAdr, &clntAdrSz);
// 클라이언트의 fgets함수로 인하여 마지막 문자가 '\n' 문자이므로 이것을 null 문자로 변경
message[strLen - 1] = '\0';
// 만약 클라이언트에서 요청한 명령이 "show time" 이라면 서버의 현재 시각 정보를 클라이언트로 전달
if (!strcmp(message, "show time")) {
t = time(NULL);
tm = *localtime(&t);
// 서버 현재 시각을 curTime 문자열 배열에 저장
sprintf(curTime, "now: %04d-%02d-%02d %02d:%02d:%02d\n",
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
// sendto : 데이터 송신 함수
// 서버의 현재 시간 정보를 전송하는 부분
// servSock : 데이터 전송 시 사용할 소켓의 파일 디스크립터 설정
// curTime : 현재 시간 정보가 담긴 문자열 배열 전달
// strlen(curTime) : 몇 바이트를 전송할 것인지 설정
// 0 : flags이며 옵션 설정한느데 필요한 인자. 일반적으로 0을 설정
// (SOCKADDR*)&clntAdr : clntAdr을 SOCKADDR포인터형으로 형 변환 후 전달 (전송하고자 하는 곳의 주소 정보)
// sizeof(clntAdr) : (SOCKADDR*)&clntAdr포인터가 가리키고 있는 구조체의 변수 크기
sendto(servSock, curTime, strlen(curTime), 0, (SOCKADDR*)&clntAdr, sizeof(clntAdr));
}
// 클라이언트에서 들어온 명령이 "show time"이 아닐 시 에러 메시지 전송
else {
strcpy(message, "명령어를 잘못 입력하였습니다. (cmd ex: show time)\n");
sendto(servSock, message, strlen(message), 0, (SOCKADDR*)&clntAdr, sizeof(clntAdr));
}
}
closesocket(servSock);
WSACleanup();
return 0;
}
Client 구현
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
#define PORT 3333
#define BUF_SIZE 100
#define IP_ADDR "127.0.0.1"
int main() {
WSADATA wsaData;
SOCKET sock;
SOCKADDR_IN servAdr;
char message[BUF_SIZE];
int strLen;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
fputs("wsastartup() error\n", stderr);
exit(1);
}
// 소켓 설정, SOCK_DGRAM으로 안하여 UDP로 설정됨.
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
WSACleanup();
fputs("upd 소켓 생성 실패\n", stderr);
exit(1);
}
memset(&servAdr, 0, sizeof(servAdr));
servAdr.sin_family = AF_INET;
servAdr.sin_addr.s_addr = inet_addr(IP_ADDR);
servAdr.sin_port = htons(PORT);
// 서버와 연결
connect(sock, (SOCKADDR*)&servAdr, sizeof(servAdr));
while (1) {
fputs("command(q to quit): ", stdout);
fgets(message, sizeof(message), stdin);
if (!strcmp(message, "q\n")) {
break;
}
// send : 데이터 송신 함수
// sock : 데이터 전송 시 사용할 소켓의 파일 디스크립터 설정
// message : 전송할 문자열 데이터
// strlen(message) : 전송할 문자열의 크기
// flags : 전송할 데이터 또는 읽는 방법에 대한 option.
send(sock, message, strlen(message), 0);
// recv : 데이터 수신 함수
// sock : 데이터를 수신할 때 사용할 소켓의 파일 디스크립터
// message : 수신한 데이터를 저장할 버퍼
// sizeof(message) - 1 : 읽을 수 있는 버퍼의 최대 크기
// 0 : 읽을 데이터 유형 또는 읽는 방법에 대한 option
strLen = recv(sock, message, sizeof(message) - 1, 0);
// 서버에서 보내온 문자열 데이터에 null 문자 삽입
message[strLen] = '\0';
printf("server에서 보내온 시간: %s\n", message);
}
closesocket(sock);
WSACleanup();
return 0;
}
/* 실행 결과
command(q to quit): asdflk
server에서 보내온 시간: 명령어를 잘못 입력하였습니다. (cmd ex: show time)
command(q to quit): 12oiweuio
server에서 보내온 시간: 명령어를 잘못 입력하였습니다. (cmd ex: show time)
command(q to quit): show time
server에서 보내온 시간: now: 2021-02-17 21:14:18
command(q to quit): show time
server에서 보내온 시간: now: 2021-02-17 21:14:29
command(q to quit): q
*/
'Programming > Network' 카테고리의 다른 글
[Programming/Network] TCP/IP 스택 (0) | 2020.10.04 |
---|
Comments