251021

lililllilillll·2025년 10월 21일

개발 일지

목록 보기
331/350

✅ 한 것들


  • 윤성우의 열혈 TCP/IP 소켓 프로그래밍
  • 백준
  • 이력서 리뉴얼


📖 윤성우의 열혈 TCP/IP 소켓 프로그래밍


Chapter 13 다양한 입출력 함수들

13-1 send & recv 입출력 함수

send, recv 함수 마지막 인자에 옵션을 전달할 수 있다

MSG_OOB : 긴급 메시지 전송

  • SIGURG에 sighandler를 등록하면 긴급 메시지 수신 시 발생.
    • 핸들러 안에 긴급 메시지 처리 로직 구현하면 됨
  • fcntl()로 소켓 소유자를 getpid()이 반환하는 ID의 프로세스 변경해야 함
  • 어느 프로세스의 핸들러 함수 호출할지 지정해줘야 하기 때문
  • Urgent mode는 더 빨리 가지도 못하고, 1바이트만 지정한다
    • 긴급 메시지가 존재한다는 것과 긴급 메시지의 마지막 포인터만 전달한다
    • 메시지 처리를 재촉하는데 의미가 있는 것이기 때문

MSG_PEEK : 입력 버퍼에 수신된 데이터 존재하는지 확인

  • 이 옵션으로 recv() 호출하면 입력버퍼에 존재하는 데이터 읽혀도 데이터 안 지워짐
  • MSG_DONTWAIT 옵션과 묶여, 블로킹 안 하고 데이터 존재유무만 판단하는 함수에 사용됨

13-2 readv & writev 입출력 함수

데이터를 모아서 전송, 모아서 수신

  • writev : 여러 버퍼 데이터를 한 번에 전송 가능
  • readv : 데이터를 여러 버퍼에 나눠서 수신 가능
  • iovec 구조체 배열로 버퍼 주소 및 크기 정보 제공
  • iovec 구조체 정보 바탕으로 버퍼들 그러모아서 writev() 또는 readv()

13-3 윈도우 기반으로 구현하기

윈도우에선 리눅스에서 썼던 방식의 시그널 핸들링이 불가능
Out-of-band 데이터 수신도 예외상황이므로, select() 써서 대체 가능

writev, readv에 대응하는 함수는 윈도우에 없음.
대신 윈도우에선 중첩 입출력(Overlapped IO)를 이용할 수 있음.

Chapter 14 멀티캐스트 & 브로드캐스트

14-1 멀티캐스트

멀티캐스트는 UDP 기반.

  • UDP 데이터 전송은 목적지가 하나지만,
  • 멀티캐스트는 특정 그룹의 다수 호스트에게 전송.
  • 라우터에 패킷 도착하면 패킷 복사해서 호스트들에게 전송

TTL (Time to Live)

  • 패킷을 얼마나 멀리 전달할 것인지 결정하는 주 요소
  • 라우터를 하나 거칠 때마다 1씩 감소하고, 0 되면 패킷 소멸
  • 너무 크게 설정하면 트래픽에 악영향, 작게 설정하면 목적지 도달 안 함
  • setsockopt()로 IP_MULTICAST_TTL 옵션에서 TTL 설정 가능

멀티캐스트 그룹 가입

  • setsockopt()로 IP_ADD_MEMBERSHIP 옵션에서 가입 가능
  • ip_mreq에 가입할 그룹의 IP 주소 정보 및 소켓이 속한 호스트의 IP 주소 명시

Sender와 Receiver 구현

멀티캐스트에서 서버는 Sender, 클라는 Receiver라 한다

	send_sock=socket(PF_INET, SOCK_DGRAM, 0);
	memset(&mul_adr, 0, sizeof(mul_adr));
	mul_adr.sin_family=AF_INET;
	mul_adr.sin_addr.s_addr=inet_addr(argv[1]); // Multicast IP
	mul_adr.sin_port=htons(atoi(argv[2])); // Multicast Port
	
	setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL,
	    (void*)&time_live, sizeof(time_live));
	if((fp=fopen("news.txt", "r"))==NULL)
		error_handling("fopen() error");

	while(!feof(fp))
	{
		fgets(buf, BUF_SIZE, fp);
		sendto(send_sock, buf, strlen(buf), 0,
	 	(struct sockaddr*)&mul_adr, sizeof(mul_adr));
		sleep(2);
	}
	fclose(fp);
	close(send_sock);
	return 0;

Sender.c

  • UDP 소켓 생성
  • setsockopt()로 TTL 지정
  • IP 주소는 멀티 캐스트 주소를 줘야 함
    • (224.0.0.0/24 대역은 라우터가 포워딩 안 해서 로컬 서브넷 안 벗어난다)
  • udp니까 sendto()를 사용한다
	recv_sock=socket(PF_INET, SOCK_DGRAM, 0);
	memset(&adr, 0, sizeof(adr));
	adr.sin_family=AF_INET;
	adr.sin_addr.s_addr=htonl(INADDR_ANY);
	adr.sin_port=htons(atoi(argv[2]));

	if(bind(recv_sock, (struct sockaddr*) &adr, sizeof(adr))==-1)
		error_handling("bind() error");
	
	join_adr.imr_multiaddr.s_addr=inet_addr(argv[1]);
	join_adr.imr_interface.s_addr=htonl(INADDR_ANY);

	setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&join_adr, sizeof(join_adr));

	while(1)
	{
		str_len=recvfrom(recv_sock, buf, BUF_SIZE-1, 0, NULL, 0);
		if(str_len<0)
			break;
		buf[str_len]=0;
		fputs(buf, stdout);
	}

Receiver.c

14-2 브로드캐스트

멀티캐스트 : 서로 다른 네트워크라도 그룹에 가입만 됐으면 데이터 수신 가능
브로드캐스트 : 동일한 네트워크 호스트로만 전송 대상 제한

  • 동일한 네트워크 모든 호스트에게 동시에 데이터 전송
  • UDP 기반
  • Directed 브로드캐스트, Local 브로드캐스트로 구분됨
    • Directed 브로드캐스트 주소 : 192.12.34 네트워크 모든 호스트 → 192.12.34.255
    • Local 브로드캐스트 주소 : 255.255.255.255로 전송하면 자신이 연결된 네트워크 모든 호스트에게 전달
  • UDP 구현과 거의 차이 없다.
    • 실행 시 설정하는 IP만 달라짐
    • 코드에서 소켓 옵션 변경만 해주면 됨. (Receiver에선 옵션 변경 필요 없음)


⚔️ 백준


4991

#include<string.h>
#include<vector>
#include<iostream>
#include<queue>
#include<map>
#include <tuple>
using namespace std;
typedef pair<int, int> pii;
typedef vector<pii> vp;
typedef pair<pii, int> ppii;

namespace
{
	int w, h;
	char room[20][20];
	int checked[20][20][1 << 10];
	pii start;
	vp dust;
	map<pii, int> bitMap;
	vp dirs = { {0,1},{0,-1},{1,0},{-1,0} };
	

	int FindMinCourse()
	{
		// bfs 때리는데, 같은 비트마스크 이미 있으면 넘어가기
		// qsize만큼만 돌린 뒤 count 하나 올리기
		memset(checked, 0, sizeof(checked));
		int cnt = 0;
		queue<ppii> q;
		int bitMask = 0;
		q.push({ start, bitMask });
		checked[start.first][start.second][bitMask] = true;

		while (!q.empty())
		{
			int qsize = q.size();
			while(qsize--)
			{
				int i, j;
				tie(i, j) = q.front().first;
				bitMask = q.front().second;
				q.pop();
				// 1 << dust.size()를 괄호로 감싸야 했다
				if (bitMask == ((1 << dust.size()) - 1)) return cnt;
				for (pii dir : dirs)
				{
					int newi = i + dir.first;
					int newj = j + dir.second;
					int newBitMask = bitMask;
					if (newi < 0 || h <= newi || newj < 0 || w <= newj) continue;
					if (room[newi][newj] == 'x') continue;
					if (room[newi][newj] == '*') newBitMask |= 1 << bitMap[{newi, newj}];
					if (checked[newi][newj][newBitMask]) continue;
					checked[newi][newj][newBitMask] = true;
					q.push({ {newi,newj},newBitMask });
				}
			}
			cnt++;
		}
		return -1;
	}
}

int main()
{
    cin.tie(0);
    cout.tie(0);
	while (1)
	{
		cin >> w >> h;
		if (w == 0) break;
		dust = vp();
		for (int i = 0; i < h; i++)
		{
			for (int j = 0; j < w; j++)
			{
				cin >> room[i][j];
				if (room[i][j] == '*')
				{
					dust.push_back({ i,j });
					bitMap[{i, j}] = dust.size() - 1;
				}
				else if (room[i][j] == 'o')
				{
					start = { i,j };
				}
			}
		}
		cout << FindMinCourse() << '\n';
	}
}

답지 보고 이해한 후에 실수한 거 gpt로 찾기까지 하면서 품

  • 백준에서 memset()은 <string.h>가 들어가야 한다
  • 비트 연산자보다 일반 연산자가 우선순위 높아서, 한 번 감싸야 함
  • room[newi][newj] 들어갈 자리에 room[i][j] 넣는 실수
  • '*'이면 dust에 넣어야 하는데, 'x'면 넣어버리는 실수
  • bitMap에 dust.size() -1 해야 하는데 개수대로 넣으면 되겠지~ 하고 -1 누락한 실수


profile
너 정말 **핵심**을 찔렀어

0개의 댓글