본문 바로가기
네트워크

3. UDP 소켓 함수

by Rudy 2021. 9. 28.

네트워크 프로그램 모델

1. 클라이언트-서버 모델

- 하나의 서비스는 서버와 클라이언트로 구성된다.

- 서버 프로그램이 먼저 실행되고, 클라이언트 프로그램은 필요할 때 실행되어 서버에 접속한다.

- 프로그램 작성이 쉽다.

- ex) www

 

2. Peer-to-Peer(P2P) 모델

- 서버와 클라이언트의 구분이 없다.

- 프로그램 작성이 어렵다.

- Torrent

 

UDP 클라이언트-서버

UDP 소켓

- 데이터를 항상 전달해 주지는 않는다.

- 간단하고 전송속도가 비교적 균일하다.

 

UDP 멀티플렉싱

: 클라이언트가 많아져도, 서버 소켓이 많아지지 않는다.

 

UDP 클라이언트-서버의 동작

UDP 소켓 함수

- 생성과 소멸: socket(), closesocket()

- 이름 붙이기: bind()

 

데이터 송신: 보낸 바이트수를 의미함. 음수는 에러코드

int sendto(
	SOCKET s,
	const char *buf, //소켓 디스크립터
    int len, //보낼 데이터의 크기
    int flag, //0으로 설정
    const SOCKADDR *to, //상대편 소켓의 이름
    int tolen //to의 길이
    )

- 전송할 데이터를 OS 내부의 소켓 버퍼로 복사한다.

-> 복사하는 동안 아주 잠깐 블로킹 상태이다. 하지만 소켓 버퍼에 빈 공간이 부족하면 자리가 나기를 기다리며 계속 블로킹 상태가 된다.

-> 리턴되었다고 해서 데이터가 상대편으로 전송된 것은 아니다.

-> Sendto()가 호출될 때 소켓에 이름이 없으면, OS가 임의로 이름을 붙인다.

 

데이터 수신: 받은 바이트 수를 의미함. 음수는 에러코드

int recvfrom(
	SOCKET s ,//소켓 디스크립터
    char *buf, //받을 데이터가 담길 버퍼 주소
    int len, //버퍼의 크기
    int flag, //0으로 설정
    const SOCKADDR *from, //상대편 소켓의 이름(OS가 채워 준다.)
    int *fromlen //from의 길이(프로그래머/OS가 채워 준다.)
    )

- OS 내부의 소켓 버퍼에서 buf로 데이터 복사

-> 복사하는 동안은 아주 잠깐 블로킹 상태이다.

-> 소켓 버퍼에 데이터가 없으면, 데이터를 기다리면서 계속 블로킹 상태이다.

-> UDP 패킷이 버퍼 크기 len보다 크면, 읽지 못한 부분은 버리고 에러코드를 리턴한다.

-> 데이터 바이트가 없는 UDP 패킷이 수신되면 리턴값은 0이다.

 

서버: 첫 번째 클라이언트로부터 데이터를 받은 후 종료된다.

//...
SOCKET s=socket(AF_INET, SOCK_DGRAM,0);

SOCKADDR_IN myAddress;
ZeroMemory(&myAddress, sizeof(myAddress));
myAddress.sin_family=AF_INET;
myAddress.sin_port=htons(50000);
myAddress.sin_addr.s_addr=INADDR_ANY;
bind(s,(SOCKADDR*)&myAddress,sizeof(myAddress));

SOCKADDR_IN clientAddress;
int iAddressLength=sizeof(clientAddress);
char chRxBuf[MAX_BUF]={0};
int iLength=recvfrom(s,chRxBuf,MAX_BUF,0,(SOCKADDR*)&clientAddress,&iAddressLength);

closesocket(s);
//...

 

클라이언트: 서버에게 데이터를 보낸 후 종료

//...
SOCKET s=socket(AF_INET,SOCK_DGRAM,0);

SOCKADDR_IN serverAddress;
ZeroMemory(&serverAddress, sizeof(serverAddress));
serverAddress.sin_family=AF_INET;
serverAddress.sin_port=htons(50000);
inet_pton(AF_INET,"127.0.0.1", &(serverAddress.sin_addr.s_addr));

const char chTxBuf[MAX_BUF]="Hello, UDP Server! I am your Client!";
int iLength=sendto(s,chTxBuf,strlen(chTxBuf),0,(SOCKADDR*)&serverAddress,sizeof(serverAddress));

closesocket(s);
//...

NULL로 끝나는 문자열이면 직접 세지 않고 strlen()

C 언어는 배열의 초기값으로 문자열을 저장할 때, 자동으로 NULL을 끝에 붙여준다.

 

 

UDP 소켓 프로그램 생성

 

클라이언트

#pragma comment(lib, "ws2_32.lib");
#include "Winsock2.h"
#include <stdio.h>
#include <WS2tcpip.h>
#include <time.h>
#define MAX_BUF 5000

//21812167
//1. UDP 소켓 하나 생성
//2. 서버에게 메시지 송신
//3. 서버로부터 현재 시간 수신한 후 종료
//4. 화면에 모든 송수신 문자열을 출력할 것




int main(void) {
	WSADATA wsa;


	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
		//소켓 라이브러리가 로딩이 됐는지 안 됐는지 확인
		//0아닌 값이 넘어오면 로딩이 안 된 것
		printf("Error in starting up Winsock\n");
		return -1;
	}

	SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);

	if (s == INVALID_SOCKET) {
		printf("Error in socket(), Error code: %d\n", WSAGetLastError());
		WSACleanup();
		return -1;
	}

	SOCKADDR_IN serverAddress;
	ZeroMemory(&serverAddress, sizeof(serverAddress));
	serverAddress.sin_family = AF_INET;
	serverAddress.sin_port = htons(50000);
	inet_pton(AF_INET, "192.168.219.103", &(serverAddress.sin_addr.s_addr));
	
	const char chTxBuf[MAX_BUF] = "Hello, UDP Time Server!";
	char chRxBuf[MAX_BUF] = { 0 };

	int iLength = sendto(s, chTxBuf, strlen(chTxBuf), 0, (SOCKADDR*)&serverAddress, sizeof(serverAddress));

	if (iLength == SOCKET_ERROR) {
		printf("Error in sendto(), Error code: %d\n", WSAGetLastError());
		closesocket(s);
		WSACleanup();
		return -1;
	}//sendto 함수에서 에러가 난 경우
	printf("Message send to the server:%s\n", chTxBuf);

	int rlen=sizeof(serverAddress);
	int rLength = recvfrom(s, chRxBuf, MAX_BUF, 0, (SOCKADDR*)&serverAddress,&rlen);
	
	if (rLength == SOCKET_ERROR) {
		printf("Error in recvfrom(), Error code: %d\n", WSAGetLastError());
		closesocket(s);
		WSACleanup();
		return -1;
	}//recvfrom() 함수에서 에러가 난 경우
	
	printf("Message receive from the server: %s  \n", chRxBuf);

	closesocket(s);
	WSACleanup();
	return 0;


}

서버

#pragma comment(lib, "ws2_32.lib");
#include "Winsock2.h"
#include <stdio.h>
#include <WS2tcpip.h>
#include <time.h>

#define MAX_BUF 5000
//21812167 
//1. UDP소켓을 하나 생성
//2. 클라이언트의 접속을 기다림
//3. 클라이언트가 접속하여 메시지를 보내면 
//4. 현재 시각으로 응답
//5. 화면에 모든 송수신 문자열을 출력하고 프로그램이 영원히 종료되지 않고 다음 클라이언트를 기다려야 함

int main() {
	WSADATA wsa;
	time_t t = time(NULL);
	struct tm tm;
	localtime_s(&tm, &t);

	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
		//소켓라이브러리가 로딩이 됐는지 안 됐는지 확인한다.
		//0아닌 값이 넘어오면 로딩이 안 된 것이다.
		printf("Error in starting up Winsock\n");
		return -1;
	}

	SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s == INVALID_SOCKET) {
		printf("Error in socket(), Error code: %d\n", WSAGetLastError());
		WSACleanup();
		return -1;
	}
	SOCKADDR_IN myAddress;
	ZeroMemory(&myAddress, sizeof(myAddress));
	myAddress.sin_family = AF_INET;
	myAddress.sin_port = htons(50000);
	myAddress.sin_addr.s_addr = INADDR_ANY;

	if (bind(s, (SOCKADDR*)&myAddress, sizeof(myAddress)) == SOCKET_ERROR) {
		printf("Error in bind(), Error code: %d\n", WSAGetLastError());
		closesocket(s);
		WSACleanup();
		return -1;
	} //bind()에서 오류가 난 경우

	SOCKADDR_IN clientAddress;
	int iAddressLength = sizeof(clientAddress);
	char chRxBuf[MAX_BUF] = { 0 };
	char chTxBuf[MAX_BUF] = "Hi, Client. Current time is ...";
	//int chTxBuf1 = tm.tm_hour;
	//int chTxBuf2 = tm.tm_min;

	while (1) {
		printf("Waiting...\n");
		int iLength = recvfrom(s, chRxBuf, MAX_BUF, 0, (SOCKADDR*)&clientAddress, &iAddressLength);
		if (iLength == SOCKET_ERROR) {
			printf("Error in recvfrom(), Error code: %d\n", WSAGetLastError());
			closesocket(s);
			WSACleanup();
			return -1;
		}
		printf("Message receive from the Client: %s\n", chRxBuf);

		int rLength = sendto(s, chTxBuf, strlen(chTxBuf), 0, (SOCKADDR*)&clientAddress, sizeof(clientAddress));
		

		if (rLength == SOCKET_ERROR) {
			printf("Error in sendto(), Error code: %d\n", WSAGetLastError());
			closesocket(s);
			WSACleanup();
			return -1;
		}
		printf("Message send to the Client: %s\n", chTxBuf);

	}


	closesocket(s);
	WSACleanup();
	return 0;
}

결과

'네트워크' 카테고리의 다른 글

네트워크-소켓  (0) 2021.09.09

댓글