TCP와 비교해 UDP는 간단함
클 라이언트에서 쏘면 서버 쪽에서 받아주는 것만 처리하면 됨
UDP는 목적지까지 가는 데 아니면 말고
잘 전달됐는지 확인할 수 없으면 ACK 같은 게 없어서
APP
|<= TCP OR UDP
------------------------------------------
O/S
H/W
IP IP IP
LAN LAN LAN
-------------------------------------------
| | |
MAC MAC MAC
sendto 함수는 누구에게 보낼지 일일히 설정 가능
서로 sendto함수를 통해 통로가 만들어지는 것임
TCP와 다르게 UDP 서버는 하나의 소켓만 사용
나만 알면 됨
UDP 서버, 클라이언트 검증 가능
로컬 어드레스 0 0 0 0 의미는 내컴퓨터의 IP가 여러개일때 일단 모두 모아서 이쪽으로 점유하겠다는 뜻
FOREIGN 0 0 0 0누구던 나한테 접속가능
컨트롤 c 종료
통신 가능한 것을 알 수 있음
UdpClient.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
int main(int argc, char *argv[])
{
int nSockFd; // 소켓 파일 디스크립터
char p_Buffer[BUFSIZ]; // 데이터 버퍼
int nBufferLen; // 버퍼 길이
struct sockaddr_in stSAddr; // 서버 주소 구조체
int nSAddr_size; // 주소 구조체 크기
if (argc != 3)
{
printf("Usage: %s <IP Address> <port>\n", argv[0]);
return -1;
}
// UDP 소켓 생성
nSockFd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// 서버 주소 설정
bzero(&stSAddr, sizeof(stSAddr));
stSAddr.sin_family = AF_INET;
stSAddr.sin_addr.s_addr = inet_addr(argv[1]); // 명령줄 인수로 전달된 IP 주소 사용
stSAddr.sin_port = htons(atoi(argv[2])); // 명령줄 인수로 전달된 포트 번호 사용
nSAddr_size = sizeof(stSAddr);
// 데이터 입력 및 송신
bzero(p_Buffer, BUFSIZ);
nBufferLen = read(0, p_Buffer, BUFSIZ); // 표준 입력으로부터 데이터를 읽음
nBufferLen = sendto(nSockFd, p_Buffer, nBufferLen, 0, (struct sockaddr *)&stSAddr, nSAddr_size); // 소켓을 통해 데이터 전송
if (nBufferLen > 0)
{
printf("TX: %s\n", p_Buffer); // 송신한 데이터 출력
}
// 데이터 수신
bzero(p_Buffer, BUFSIZ);
nBufferLen = recvfrom(nSockFd, p_Buffer, BUFSIZ, 0, (struct sockaddr *)&stSAddr, &nSAddr_size); // 소켓으로부터 데이터 수신
if (nBufferLen > 0)
{
printf("Server Information: \n");
printf("Addr: %s\n", inet_ntoa(stSAddr.sin_addr)); // 서버의 IP 주소 출력
printf("Port: %d\n", ntohs(stSAddr.sin_port)); // 서버의 포트 번호 출력
printf("RX: %s\n", p_Buffer); // 수신한 데이터 출력
}
close(nSockFd); // 소켓 닫기
return 0;
}
위의 코드는 UDP 프로토콜을 사용하여 클라이언트에서 서버로 데이터를 전송하고, 서버로부터 데이터를 수신하는 클라이언트 프로그램입니다.
프로그램 실행 시 명령줄 인수로 서버의 IP 주소와 포트 번호를 입력해야 합니다.
1. UDP 소켓 생성
- `nSockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);`
- AF_INET은 IPv4 주소 체계를 사용함을 나타내며, SOCK_DGRAM은 데이터그램 소켓을 생성하라는 의미입니다.
2. 서버 주소 설정
- `bzero(&stSAddr, sizeof(stSAddr));`
- 서버 주소 구조체(stSAddr)를 초기화합니다.
- `stSAddr.sin_family = AF_INET;` : 주소 체계를 IPv4로 설정합니다.
- `stSAddr.sin_addr.s_addr = inet_addr(argv[1]);` : 명령줄 인수로 전달된 IP 주소를 설정합니다.
- `stSAddr.sin_port = htons(atoi(argv[2]));` : 명령줄 인수로 전달된 포트 번호를 설정합니다.
- `nSAddr_size = sizeof(stSAddr);` : 주소 구조체의 크기를 저장합니다.
3. 데이터 입력 및 송신
- `nBufferLen = read(0, p_Buffer, BUFSIZ);` : 표준 입력으로부터 데이터를 읽어옵니다.
- `nBufferLen = sendto(nSockFd, p_Buffer, nBufferLen, 0, (struct sockaddr *)&stSAddr, nSAddr_size);` : 소켓을 통해 서버로 데이터를 전송합니다.
- 전송한 데이터를 출력합니다.
4. 데이터 수신
- `nBufferLen = recvfrom(nSockFd, p_Buffer, BUFSIZ, 0, (struct sockaddr *)&stSAddr, &nSAddr_size);` : 소켓으로부터 서버로부터 데이터를 수신합니다.
- 수신한 데이터와 서버의 IP 주소, 포트 번호를 출력합니다.
5. 소켓 닫기
- `close(nSockFd);` : 소켓을 닫습니다.
즉, 이 프로그램은 클라이언트가 서버에 데이터를 전송하고, 서버로부터 응답을 받는 단순한 UDP 통신을 수행하는 것입니다.
nc명령으로 서버를 열고 테스트 해보기
친구와 테스트 해보기
udp는 사진처럼 연결구조가 되어야 통신가능
strncasecmp : 앞에서 부터 4개 같으면
strcasecmp : 이전시간과 조금 다른데 확인 해보기
서버 프로그램 작성
UdpServer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int nSocketFd; // 소켓 파일 디스크립터
char pBuffer[BUFSIZ]; // 데이터 버퍼
int nBufferLen = 0; // 버퍼 길이
struct sockaddr_in stSAddr; // 서버 소켓 주소 구조체
struct sockaddr_in stCAddr; // 클라이언트 소켓 주소 구조체
int nCAddr_size; // 클라이언트 소켓 주소 구조체 크기
if (argc != 2)
{
printf("Usage: %s <port>\n", argv[0]);
return -1;
}
// UDP 소켓 생성
nSocketFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// 서버 소켓 주소 설정
memset(&stSAddr, 0, sizeof(stSAddr));
stSAddr.sin_family = PF_INET;
stSAddr.sin_addr.s_addr = htonl(INADDR_ANY);
stSAddr.sin_port = htons(atoi(argv[1]));
// 소켓에 주소 바인딩
if (bind(nSocketFd, (struct sockaddr *)&stSAddr, sizeof(stSAddr)) < 0)
{
printf("Binding Failed.\n");
return -1;
}
do
{
// 클라이언트 소켓 주소 구조체 초기화
nCAddr_size = sizeof(stCAddr);
memset(pBuffer, 0, BUFSIZ);
// 클라이언트로부터 데이터 수신
nBufferLen = recvfrom(nSocketFd, pBuffer, BUFSIZ, 0, (struct sockaddr *)&stCAddr, &nCAddr_size);
if (nBufferLen > 0)
{
printf("Client Information: \n");
printf("Addr: %s\n", inet_ntoa(stCAddr.sin_addr)); // 클라이언트의 IP 주소 출력
printf("Port: %d\n", ntohs(stCAddr.sin_port)); // 클라이언트의 포트 번호 출력
printf("RX: %s\n", pBuffer); // 수신한 데이터 출력
if (strncasecmp(pBuffer, "exit", 4) == 0)
break;
}
else if (nBufferLen <= 0)
break;
// 클라이언트로 데이터 전송
nBufferLen = sendto(nSocketFd, pBuffer, nBufferLen, 0, (struct sockaddr *)&stCAddr, nCAddr_size);
} while (1);
close(nSocketFd); // 소켓 닫기
return 0;
}
위의 코드는 UDP 프로토콜을 사용하여 클라이언트로부터 데이터를 수신하고, 수신한 데이터를 다시 클라이언트로 전송하는 간단한 서버 프로그램입니다.
클라쪽에서 잘 오는 것을 확인 할 수 이음
클라이언트 쪽에서 exit가 꼭 필요함 컨트롤 + c 해서 종료하면 포트를 물고있어서 자원을 놔주지 못하는 경우가 생길 수 있음 다음번에 실행했을때 정상적인 코드 실행이 안될 수 있음
UdpClient2.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
int main(int argc, char *argv[])
{
int nSockFd; // 소켓 파일 디스크립터
struct pollfd rfds[2]; // 파일 디스크립터 감시를 위한 pollfd 구조체 배열
int nRetval; // poll() 함수의 반환값
char p_Buffer[BUFSIZ]; // 데이터 버퍼
int nBufferLen; // 버퍼 길이
struct sockaddr_in stSAddr; // 서버 주소 구조체
int nSAddr_size; // 주소 구조체 크기
if (argc != 3)
{
printf("Usage: %s <IP Address> <port>\n", argv[0]);
return -1;
}
// UDP 소켓 생성
nSockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// 키보드 입력을 감시하는 파일 디스크립터 설정
rfds[0].fd = 0; // 표준 입력(키보드)의 파일 디스크립터
rfds[0].events = POLLIN; // 입력 가능한 데이터의 상태 변화를 감시
// 소켓 입력을 감시하는 파일 디스크립터 설정
rfds[1].fd = nSockFd; // 소켓의 파일 디스크립터
rfds[1].events = POLLIN; // 입력 가능한 데이터의 상태 변화를 감시
// 서버 주소 설정
bzero(&stSAddr, sizeof(stSAddr));
stSAddr.sin_family = AF_INET;
stSAddr.sin_addr.s_addr = inet_addr(argv[1]); // 명령줄 인수로 전달된 IP 주소 사용
stSAddr.sin_port = htons(atoi(argv[2])); // 명령줄 인수로 전달된 포트 번호 사용
nSAddr_size = sizeof(stSAddr);
do
{
nRetval = poll(rfds, 2, 1000); // 파일 디스크립터 감시
if (nRetval < 0)
break;
if (nRetval == 0)
continue;
// 키보드 입력 처리
if (rfds[0].revents & POLLIN)
{
bzero(p_Buffer, BUFSIZ);
nBufferLen = read(0, p_Buffer, BUFSIZ); // 키보드로부터 데이터를 읽음
nBufferLen = sendto(nSockFd, p_Buffer, nBufferLen, 0, (struct sockaddr *)&stSAddr, nSAddr_size); // 소켓을 통해 데이터 전송
if (nBufferLen > 0)
{
printf("TX: %s", p_Buffer); // 송신한 데이터 출력
if (strncasecmp(p_Buffer, "exit", 4) == 0)
break;
}
// 소켓 수신 처리
if (rfds[1].revents & POLLIN)
{
bzero(p_Buffer, BUFSIZ);
nBufferLen = recvfrom(nSockFd, p_Buffer, BUFSIZ, 0, (struct sockaddr *)&stSAddr, &nSAddr_size); // 소켓으로부터 데이터 수신
if (nBufferLen > 0)
{
printf("RX: %s", p_Buffer); // 수신한 데이터 출력
if (strncasecmp(p_Buffer, "exit", 4) == 0)
break;
}
}
} while (1);
close(nSockFd); // 소켓 닫기
return 0;
}
위의 코드는 UDP 프로토콜을 사용하여 클라이언트로부터 키보드 입력을 받아 서버로 전송하고, 동시에 서버로부터 데이터를 수신하여 출력하는 간단한 클라이언트 프로그램입니다.
프로그램 실행 시 명령줄 인수로 서버의 IP 주소와 포트 번호를 전달해야 합니다. 프로그램은 키보드 입력과 소켓 입력을 모두 감시하고, 데이터를 송수신하며 "exit"라는 입력이 들어오면 종료됩니다.
클라이언트와 서버가 채팅앱처럼 동작 하는 것을 확인 할 수 있음
UdpServer2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/poll.h>
int main(int argc, char *argv[])
{
int nSocketFd; // 소켓 파일 디스크립터
char pBuffer[BUFSIZ]; // 버퍼
int nBufferLen = 0; // 버퍼 길이
struct sockaddr_in stSAddr; // 서버 소켓 주소 구조체
struct sockaddr_in stCAddr; // 클라이언트 소켓 주소 구조체
int nCAddr_size; // 클라이언트 소켓 주소 구조체의 길이
int nRetval;
struct pollfd rfds[2]; // 표준 입력(키보드 입력), 소켓 입력을 감시하는 pollfd 구조체 배열
if (argc != 2)
{
printf("Usage: %s <port>\n", argv[0]);
return -1;
}
// UDP 소켓 생성
nSocketFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// 키보드 입력 설정
rfds[0].fd = 0; // 표준 입력(키보드 입력)의 파일 디스크립터 0
rfds[0].events = POLLIN; // 입력 가능 이벤트를 감시
rfds[0].revents = 0; // 이벤트 상태 초기화
// 소켓 입력 설정
rfds[1].fd = nSocketFd; // 소켓 파일 디스크립터
rfds[1].events = POLLIN; // 입력 가능 이벤트를 감시
rfds[1].revents = 0; // 이벤트 상태 초기화
// 서버 소켓 주소 초기화
memset(&stSAddr, 0, sizeof(stSAddr));
stSAddr.sin_family = PF_INET; // IPv4 주소 패밀리
stSAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 모든 IP 주소로부터의 입력을 허용
stSAddr.sin_port = htons(atoi(argv[1])); // 명령줄 인수로 전달된 포트 번호 설정
// 소켓과 주소를 바인딩
if (bind(nSocketFd, (struct sockaddr *)&stSAddr, sizeof(stSAddr)) < 0)
{
printf("Binding Failed.\n");
return -1;
}
do
{
// poll을 이용하여 입력을 감시
nRetval = poll(rfds, 2, 1000); // 1000ms(1초) 동안 입력을 감시
if (nRetval < 0)
break;
if (nRetval == 0)
continue;
// 키보드 입력 처리
if (rfds[0].revents & POLLIN) // 표준 입력(키보드 입력)에서 입력 가능 이벤트가 발생한 경우
{
memset(pBuffer, 0, BUFSIZ); // 버퍼 초기화
nBufferLen = read(0, pBuffer, BUFSIZ); // 표준 입력(키보드 입력)에서 데이터를 읽어옴
nBufferLen = sendto(nSocketFd, pBuffer, nBufferLen, 0,
(struct sockaddr *)&stCAddr, nCAddr_size); // 소켓을 통해 읽어온 데이터를 전송
if (nBufferLen > 0)
{
printf("TX: %s", pBuffer); // 전송한 데이터 출력
if (strncasecmp(pBuffer, "exit", 4) == 0) // 전송한 데이터가 "exit"일 경우 종료
break;
}
}
// 소켓 수신 처리
if (rfds[1].revents & POLLIN) // 소켓에서 입력 가능 이벤트가 발생한 경우
{
memset(pBuffer, 0, BUFSIZ); // 버퍼 초기화
nBufferLen = recvfrom(nSocketFd, pBuffer, BUFSIZ, 0,
(struct sockaddr *)&stCAddr, &nCAddr_size); // 소켓을 통해 데이터를 수신
if (nBufferLen > 0)
{
printf("RX: %s", pBuffer); // 수신한 데이터 출력
if (strncasecmp(pBuffer, "exit", 4) == 0) // 수신한 데이터가 "exit"일 경우 종료
break;
}
}
} while(1);
close(nSocketFd); // 소켓 종료
return 0;
}
위 코드는 UDP 소켓을 사용하여 클라이언트와 서버 간에 텍스트 데이터를 주고받는 프로그램입니다. 프로그램은 다음과 같은 동작을 수행합니다:
1. 소켓 생성 및 초기화:
- `socket()` 함수를 사용하여 UDP 소켓을 생성합니다.
2. 주소 설정 및 바인딩:
- `stSAddr` 구조체를 초기화하고, 서버의 IP 주소와 포트 번호를 설정합니다.
- `bind()` 함수를 사용하여 소켓을 해당 주소에 바인딩합니다.
3. `poll()` 함수를 사용한 입력 감시:
- `poll()` 함수를 사용하여 표준 입력(키보드 입력) 및 소켓 입력을 감시합니다.
- `poll()` 함수의 타임아웃 값으로 1000ms(1초)을 설정합니다.
4. 키보드 입력 처리:
- 표준 입력(키보드 입력)에서 입력 가능 이벤트가 발생한 경우, 데이터를 읽어옵니다.
- 읽어온 데이터를 소켓을 통해 클라이언트에게 전송합니다.
- 전송한 데이터를 출력하고, 만약 데이터가 "exit"일 경우 프로그램을 종료합니다.
5. 소켓 수신 처리:
- 소켓에서 입력 가능 이벤트가 발생한 경우, 데이터를 수신합니다.
- 수신한 데이터를 출력하고, 만약 데이터가 "exit"일 경우 프로그램을 종료합니다.
6. 반복 실행:
- 프로그램은 계속해서 입력 이벤트를 감시하고 데이터를 주고받습니다.
- 프로그램 종료 조건이 충족될 때까지 루프를 반복합니다.
7. 소켓 종료:
- `close()` 함수를 사용하여 소켓을 닫고 자원을 해제합니다.
따라서, 이 코드는 UDP 소켓을 사용하여 클라이언트와 서버 간에 상호작용하는 간단한 채팅 프로그램입니다. 키보드로부터 입력을 받아 서버로 전송하고, 서버로부터 수신한 데이터를 출력합니다. 데이터가 "exit"일 경우 프로그램이 종료됩니다.
'자료구조, 운영체제, 네트워크, 시스템설계 > 정보보안' 카테고리의 다른 글
MultPlex 서버 Poll & Select 기법 활용하여 채팅 프로그램 만들기! (0) | 2023.05.23 |
---|---|
TCP 서버, 클라이언트 구축 (0) | 2023.05.14 |
네트워크용기본 API바이트 순서 및 주소체계 변환 호스트정보 취득 및 송수신 함수들 (0) | 2023.05.02 |
파일기술자, 바이트 배열 처리 함수 (0) | 2023.04.18 |