250831

lililllilillll·2025년 8월 31일

개발 일지

목록 보기
280/350

✅ 한 것들


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


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


Neovim 서버 복붙 문제

서버에서 yy p로 복붙할 때 바로 입력되지 않음

Waiting for OSC 52 response from the terminal

Wezterm에 있던 걸 복사하기 위해 :set clipboard=unnamedplus 같은 설정을 해놨다면,
neovim이 복붙할 때 nvim 내부가 아니라 클립보드랑 내용을 소통함
로컬로 복사할 때는 nvim이 wezterm에 OSC52로 내용을 보내주는게 가능한데,
역으로 붙여넣을 때 wezterm이 nvim이 있는 서버로 내용을 보내주는게 불가능함

vim.schedule(function()
  -- 기본 레지스터(")는 시스템 클립보드에 매핑하지 않음
  vim.o.clipboard = ''

  -- Neovim 0.10 내장 OSC52 copy 전용 사용
  local osc52 = require 'vim.ui.clipboard.osc52'
  vim.g.clipboard = {
    name = 'osc52',
    copy = {
      ['+'] = osc52.copy '+',
      ['*'] = osc52.copy '*',
    },
    -- paste는 굳이 클립보드에 묻지 않고 내부 레지스터(") 내용만 돌려줌
    paste = {
      ['+'] = function()
        return { vim.fn.split(vim.fn.getreg('"'), '\n'), vim.fn.getregtype('"') }
      end,
      ['*'] = function()
        return { vim.fn.split(vim.fn.getreg('"'), '\n'), vim.fn.getregtype('"') }
      end,
    },
  }
end)

init.lua의 clipboard 설정 코드를 이걸로 바꾸기
로컬에서 서버로 붙여넣을 땐 Ctrl+Shift+V 사용
서버 내부 nvim에서 yy → p 같이 내부 동작할 때도 정상 작동

01-2 리눅스 기반 파일 조작하기

리눅스는 소켓 조작을 파일 조작과 동일하게 간주.
파일 입출력 함수를 소켓 입출력에 사용 가능.
윈도우는 파일과 소켓을 구분.

파일 디스크립터 : 파일 또는 소켓에 부여된 정수

파일 조작 기본 함수

int open(const char *path, int flag); // 데이터를 읽거나 쓰기 위해 파일 열 때 사용
int close(int fd); // 파일 닫기
ssize_t write(int fd, const void * buf, size_t nbytes); // 파일에 데이터 출력
ssize_t read(int fd, void *buf, size_t nbytes); // 성공 시 수신한 바이트 수
  • size_t : unsigned int
  • ssize_t : singed int
  • _t 붙는 이유 : 시스템 바뀌어서 비트 바뀌면 typedef 지정된 자료형을 바꿔서 컴파일하여 맞추기 위해 별도 이름 붙인 것

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

윈도우 소켓 개발에 필요한 것

헤더 파일 winsock2.h 포함

#include <winsock2.h>
#include <ws2tcpip.h>  // IPv6, getaddrinfo 같은 함수 쓰려면 필요
// #include <windows.h> 보다 반드시 먼저 포함

ws2_32.lib 라이브러리 링크

#pragma comment(lib, "ws2_32.lib")

또는 프로젝트 속성 → Linker → Input → Additional Dependencies에 ws2_32.lib 추가.

이건 visual studio가 아니면 의미가 없음.

직접 컴파일할 땐 gcc server.c util.c -o server.exe -lws2_32 처럼 뒤에 라이브러리 링크를 붙여야 한다.

winsock의 초기화

#include<winsock2.h>
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

윈도우 소켓 초기화 함수

  • 첫 번째 매개변수 : 소켓 버전정보 (MAKEWORD()로 버전 정보 쉽게 구성 가능)
  • 두 번째 매개변수 : WSADATA의 주소 값
int main(int argc, char* argv[])
{
	WSADATA wsaData;
	....
	if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
		ErrorHandling("WSAStartup() error!:);
	....
	return 0;
}

사용 예시

int WSACleanup(void);

초기화된 라이브러리 해제

01-4 윈도우 기반의 소켓관련 함수와 예제

윈도우 기반 소켓관련 함수들

SOCKET socket(int af, int type, int protocol); // 소켓 생성
int bind(SOCKET s, const struct sockaddr * name, int namelen); // IP주소와 PORT 번호 할당
int listen(SOCKET s, int backlog); // 소켓이 클라 연결 요청 받을 준비
SOCKET accept(SOCKET s, struct sockaddr * addr, int * addrlen); // 연결 수락
int connect(SOCKET s, const struct sockaddr * name, int namelen); // 연결 요청
int closesocket(SOCKET s); // 소켓 닫기

윈도우에서의 파일 핸들과 소켓 핸들

리눅스는 소켓도 파일 취급. 파일 디스크립터가 할당됨.
윈도우는 파일 생성할 때 핸들 반환. 근데 파일 핸들과 소켓 핸들 구분함.

SOCKET : 정수로 표현되는 소켓 핸들 값 저장 위해 typdef로 재정의된 자료형

int send(SOCKET s, const char * buf, int len, int flags); // 소켓에 데이터 전송 (이건 리눅스에도 존재)
int recv(SOCKET s, const char * buf, int len, int flgas); // 소켓에서 데이터 수신

윈도우 기반 서버, 클라이언트 예제

#include<stdio.h>
#include<stdlib.h>
#include<winsock2.h>
void ErrorHandling(char* message);

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET hServSock, hClntSock;
	SOCKADDR_IN servAddr, clntAddr;

	int szClntAddr;
	char message[] = "Hello World!";
	if (argc != 2)
	{
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}

	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
		ErrorHandling("WSAStartup() error!");

	hServSock = socket(PF_INET, SOCK_STREAM, 0);
	if (hServSock == INVALID_SOCKET)
		ErrorHandling("socket() error");

	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servAddr.sin_port = htons(atoi(argv[1]));

	if (bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)
		ErrorHandling("bind() error");

	if (listen(hServSock, 5) == SOCKET_ERROR)
		ErrorHandling("listen() error");

	szClntAddr = sizeof(clntAddr);
	hClntSock = accept(hServSock, (SOCKADDR*)&clntAddr, &szClntAddr);
	if (hClntSock == INVALID_SOCKET)
		ErrorHandling("aceept() error");

	send(hClntSock, message, sizeof(message), 0);
	closesocket(hClntSock);
	closesocket(hServSock);
	WSACleanup();
	return 0;
}

void ErrorHandling(char* message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

서버 예제

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
void ErrorHandling(char* message);

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET hSocket;
	SOCKADDR_IN servAddr;

	char message[30];
	int strLen;
	if(argc != 3)
	{
		printf("Usage : %s <IP> <port>\n", argv[0]);
		exit(1);
	}

	if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
		ErrorHandling("WSAStartup() error!");

	hSocket=socket(PF_INET, SOCK_STREAM, 0);
	if(hSocket==INVALID_SOCKET)
		ErrorHandling("WSAStartup() error!");

	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_family=AF_INET;
	servAddr.sin_addr.s_addr=inet_addr(argv[1]);
	servAddr.sin_port=htons(atoi(argv[2]));

	if(connect(hSocket, (SOCKADDR*)&servAddr, sizeof(servAddr))==SOCKET_ERROR)
		ErrorHandling("connect() error");

	strLen=recv(hSocket, message, sizeof(message)-1, 0);
	if(strLen==-1)
		ErrorHandling("read() error!");
	printf("Message from server: %s \n", message);

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

void ErrorHandling(char* message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}



🎮 Project GNC


Craftring UI

  • 전체 UI를 관장하는 UI Manager는 싱글톤으로 만들기로 했으므로 거기서 각 UI에 접근
  • 각 UI를 담당하는 manager들은 ConUI
  • Open()과 Close()를 ConMenu의 virtual method로 만듬. virtual이니까 변경도 자유롭다.

Esc 동작 관련

  • Esc를 눌렀을 때 현재 열려있는 메뉴가 있으면 끄고, 아무것도 없으면 게임 종료 메뉴를 표시
  • 이를 위해선 현재 열려있는 메뉴가 있는지 없는지를 알아야 함
  • 어떻게?


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

0개의 댓글