251007

lililllilillll·2025년 10월 7일

개발 일지

목록 보기
317/350

✅ 한 것들


  • 윤성우의 열혈 TCP/IP 소켓 프로그래밍
  • 프로그래머스
  • 백준
  • Project FDS


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


06-3 UDP의 데이터 송수신 특성과 connect 함수 호출

TCP 기반 데이터에는 경계가 존재하지 않는다.

  • 데이터 송수신 과정에서 호출하는 입출력함수의 호출 횟수는 큰 의미를 갖지 않는다.

UDP 기반 데이터는 경계가 존재한다.

  • 입력함수의 호출횟수와 출력함수의 호출횟수가 완벽히 일치해야 한다.
  • sleep 후에 데이터가 여러 개 쌓여있다면 TCP는 한 번에 받을 수 있지만, UDP는 데이터 개수만큼 recvfrom()을 호출해야 한다.

UDP 소켓이 전송하는 패킷을 데이터그램이라고 한다.

  • 데이터의 경계가 존재하기 때문에 하나의 패킷이 하나의 데이터로 간주되기 때문이다.

UDP 소켓에는 데이터를 전송할 목적지와 PORT 번호를 등록하지 않는다.
때문에 sendto 함수호출을 통한 데이터의 전송 과정은 이렇다

  • 1단계: UDP 소켓에 목적지의 IP와 PORT 번호 등록
  • 2단계: 데이터 전송
  • 3단계: UDP 소켓에 등록된 목적지 정보 삭제
  • 같은 목적지에 여러 번 데이터를 보내면 1단계와 3단계를 반복하는 것은 비효율적이다.
    • 1단계, 3단계가 데이터 전송과정의 약 1/3에 해당

목적지가 등록되면 connected 소켓, 없으면 unconnected 소켓이라 부른다

  • UDP 소켓을 대상으로 connect()만 호출해주면 connected 소켓 생성
  • TCP 소켓처럼 목적지의 소켓와 연결하진 않는다. 목적지 정보가 등록될 뿐이다.
  • 이제 연결 대상 지정 생략하고 데이터 전송만 하면 된다.
  • sendto(), recvfrom()이 아닌 write(), read() 호출로도 데이터 송수신 가능해진다.

Chapter 07 소켓의 우아한 연결종료

07-1 TCP 기반의 Half-close

close()로 연결 종료하면 이후에 오는 데이터는 못 받는다.
shutdown()으로 일부만 종료(Half-close)하면 전송 혹은 수신 한 쪽만 닫을 수 있다.
전송 끝났다는 의미로 EOF 보내려면, 그리고 이후에 오는 메시지 받으려면 Half-close 필요하다.

  • SHUT_RD : 입력 스트림 종료
  • SHUT_WR : 출력 스트림 종료
  • SHUT_RDWR : 입출력 스트림 종료
	fp=fopen("file_server.c", "rb");

	(...)

	while(1)
	{
		read_cnt=fread((void*)buf, 1, BUF_SIZE, fp);
		if(read_cnt<BUF_SIZE)
		{
			write(clnt_sd, buf, read_cnt);
			break;
		}
		write(clnt_sd, buf, BUF_SIZE);
	}
	shutdown(clnt_sd, SHUT_WR);
	read(clnt_sd, buf, BUF_SIZE);
	printf("Message from client: %s \n", buf);

파일을 연 뒤 읽어서 클라에게 보내준 후 "Thank you"를 기다리는 서버 코드
shutdown()으로 발신은 끊지만 수신은 열어둔다

	fp=fopen("receive.dat", "wb");

	(...)

	connect(sd, (struct sockaddr*)&serv_adr, sizeof(serv_adr));

	while((read_cnt=read(sd, buf, BUF_SIZE))!=0)
		fwrite((void*)buf, 1, read_cnt, fp);

	puts("Received file data");
	write(sd, "Thank you", 10);

파일을 받아서 receive.dat에 저장한 뒤 "Thank you"를 보내는 클라 코드

07-2 윈도우 기반으로 구현하기

shutdown()은 동일하지만 전달할 상수 이름이 약간 다르다

  • SD_RECEIVE : 입력 스트림 종료
  • SD_SEND : 출력 스트림 종료
  • SD_BOTH : 입출력 스트림 종료


⚔️ 프로그래머스


이중우선순위큐

vector<int> solution(vector<string> operations) {
    vector<int> answer = vector<int>(2,0);
    map<int, int> scale;
    priority_queue<int> maxH;
    priority_queue<int, vector<int>, greater<int>> minH;
    int count = 0;
    int del;
    for(string op : operations)
    {
        // 최소힙을 왼쪽, 최대힙을 오른쪽으로 생각하고, 딕셔너리로 숫자를 
        // 값을 삭제했을 때, 삭제하지 않은 쪽 힙으로 숫자가 기울어진다
        // 값을 삭제하기 전에, 딕셔너리에서 해당 값을 확인하고, 숫자만큼 균형을 맞춘다
        
        if(op[0] == 'I') // 주어진 숫자 삽입
        {
            int val = atoi(op.substr(2).c_str());
            minH.push(val);
            maxH.push(val);
            count++;
        }
        else if(op[0] == 'D' && count > 0)
        {
            if(op[2] == '1') // 최댓값 삭제
            {
                while(scale[maxH.top()]>0) // 최소힙에서 해당 숫자를 이미 지워버렸을 때
                {
                    del = maxH.top(); maxH.pop();
                    scale[del]--;
                }
                del = maxH.top(); maxH.pop();
                scale[del]--;
            }
            else // 최솟값 삭제
            {
                while(scale[minH.top()]<0) // 최대힙에서 해당 숫자를 이미 지워버렸을 때
                {
                    del = minH.top(); minH.pop();
                    scale[del]++;
                }
                del = minH.top(); minH.pop();
                scale[del]++;
            }
            count--;
        }        
    }
    if(count>0)
    {
        while(scale[maxH.top()]>0) // 최소힙에서 해당 숫자를 이미 지워버렸을 때
        {
            del = maxH.top(); maxH.pop();
            scale[del]--;
        }
        while(scale[minH.top()]<0) // 최대힙에서 해당 숫자를 이미 지워버렸을 때
        {
            del = minH.top(); minH.pop();
            scale[del]++;
        }
        answer[1] = minH.top();
        answer[0] = maxH.top();
    }
    return answer;
}

leetcode에서 못 풀고 답지 봤었던 것 같은 기억이 있어서 그거 살려서 풀었다

  • typedef 대신 using 쓰는게 현대적이고 명확하고 템플릿도 커버 가능하다고 함
  • atoi() 대신 stoi() 쓰면 c_str() 안 써도 된다. 애초에 atoi()<string>에 안들어있음.


⚔️ 백준


4991 로봇 청소기

#include"4991.h"
using namespace std;
typedef pair<int, int> pii;
typedef vector<pii> vp;
typedef vector<char> vc;
typedef vector<vc> vvc;
typedef vector<bool> vb;
typedef vector<vb> vvb;
typedef map<pii, int> mp;
typedef vector<int> vi;
typedef vector<vi> vvi;

namespace
{
	vi dirx = { 0,0,1,-1 };
	vi diry = { 1,-1,0,0 };

	void CheckCost(pii sp, vvc& room, mp& idxMap, vvi& d2d, int w, int h)
	{
		queue<pii> q;
		vvb visited = vvb(h, vb(w, false));
		q.push(sp);
		visited[sp.first][sp.second] = true;
		int spIdx = idxMap[{sp.first, sp.second}];
		int dist = 1;
		while (!q.empty())
		{
			int qlen = size(q);
			while (qlen--)
			{
				pii qf = q.front(); q.pop();
				
				for (int i = 0; i < 4; i++)
				{
					int newi = qf.first + dirx[i];
					int newj = qf.second + diry[i];
					if (newi < 0 || h <= newi || newj < 0 || w <= newj) continue;
					if (visited[newi][newj]) continue;
					visited[newi][newj] = true;
					q.push({ newi,newj });
					if (room[newi][newj] == '*' || room[newi][newj] == 'o')
					{
						int newpIdx = idxMap[{newi, newj}];
						d2d[spIdx][newpIdx] = dist;
						d2d[newpIdx][spIdx] = dist;
					}
				}
			}
			dist++;
		}
	}

	int MinCostSum(vp& dirties, vvi& d2d, mp& idxMap, int w, int h)
	{
		// dirties의 순열을 만든다
		// 시작 > 순열 각 원소의 다음 원소에 대해 방문 비용 더한다
		vector<int> perm;
		while(size(perm)<size(dirties))
		{
			for(int i=0; i<)
			if()
			{
				break;
			}
		}
	}
}

void B4991::Solution()
{
	int w, h;
	pii startp; // 시작 위치
	mp idxMap; // 각 더러운 칸을 인덱스로 변환
	vp dirties; // 각 더러운 칸 저장
	vvi d2d; // 각 더러운 칸에서 다른 더러운 칸으로 가는 비용
	vvc room; // 전체 맵
	while (1)
	{
		cin >> w >> h;
		if (w == 0) break;
		room = vvc(h, vc(w));
		dirties = vp();
		idxMap = mp();
		int dirtyIdx = 0;
		for (int i = 0; i < h; i++)
		{
			for (int j = 0; j < w; j++)
			{
				cin >> room[i][j];
				if (room[i][j] == '*')
				{
					pii newDirty = { i,j };
					dirties.push_back(newDirty);
					idxMap[newDirty] = dirtyIdx;
					dirtyIdx++;
				}
				else if (room[i][j] == 'o')
				{
					startp = { i,j };
				}
			}
		}
		idxMap[startp] = dirtyIdx;
		d2d = vvi(dirtyIdx+1, vi(dirtyIdx+1));

		// 각 더러운 칸에 대해 bfs를 한다
		// 더러운 칸 방문 순서로 순열을 만든다
		// bfs 결과를 순열 합계값으로 만들고 최솟값을 갱신한다

		CheckCost(startp, room, idxMap, d2d, w, h);
		for (pii dirty : dirties) CheckCost(dirty,room, idxMap, d2d,w,h);

		int minCost = MinCostSum(dirties,d2d,idxMap,w,h);

		cout << "tes";
	}
}

일단 여기까지. 내일 마저.

순열 만들려다가 설마 이딴 풀이가 정답일리 없을 것 같아서 정답을 찾아봤는데 이게 맞는 접근이었다.



🎮 Project FDS


  • 캐릭터 이동 로직


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

0개의 댓글