보통 서버 프로그래밍 할때
서버와 클라이언트의 데이터를 주고 받는 것이다.
소켓은 간단하게 표현하면 IP + Port구조로 되어있다. 예시로
클라이언트 : 111.1.1.1:1111
서버 : 222.2.2.2:2222
서버는 언제나 클라이언트를 받을 준비를 해야한다.
지금 사용하고 있는 컴퓨터의 소켓을 확인 하고 싶은면 cmd 실행후 netstat -an 실행해보자
사용하고 있는 tcp udp 소켓 확인을 할수가 있다
먼저 윈도우에서 제공하는 소켓을 이용해보자
WSADATA 에대한 지식이 조금 필요한데 먼저 코드로 활용한후에 복습 하자
#include <iostream>
#include <winsock2.h>
#include <thread>
using namespace std;
#define PACKET_SIZE 1024 // 패킷 사이즈
SOCKET skt,client_sock;
void proc_recvs(){
char buffer[PACKET_SIZE] = {0};
while(!WSAGetLastError()){ // while문 돌면서 체크 에러가 없으면
ZeroMemory(&buffer,PACKET_SIZE); // 버퍼 초기화후
recv(client_sock, buffer, PACKET_SIZE,0); // 데이터 받기
cout << "받은 메세지: " << buffer << endl; // 콘솔에 출력
}
}
int main(){
WSADATA wsa; // 먼저 소켓을 생성 해준뒤
WSAStartup(MAKEWORD(2,2), &wsa); // 초기화까지 해준다.
skt = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); // 그후 소켓도 같이 만들어 넣어준다.
SOCKADDR_IN addr = {}; // 통신을 교환할 주소값 poat 번호등 설정 함
addr.sin_family = AF_INET; // 주소 체계를 설정
addr.sin_port = htons(7777); // 포트 번호 설절
addr.sin_addr.s_addr = htonl(INADDR_ANY); // ip 주소 설정 INADDR_ANY == 내 컴퓨터에 랜카드의 ip 사용하라는뜻
bind(skt, (SOCKADDR*)&addr,sizeof(addr)); // 로컬에 있는 주소와 소켓을 연결 해주는 함수 입니다.
// 세팅 작업은 여기까지
listen(skt,SOMAXCONN); // 수신 대기 수신하는 주소에 소켓을 등록하고 대기 한다.
SOCKADDR_IN client = {}; // 수신 받을 클라이언트 구조체 생성 한다.
int client_size = sizeof(client);
ZeroMemory(&client,client_size); // 구조체를 초기화 해준다.
client_sock = accept(skt,(SOCKADDR*)&client,&client_size); // 들어오는 소켓이 있다면 연결을 허용한다.
char buffer[PACKET_SIZE] = {0}; // 데이터를 받을 버퍼를 생성
thread proc2(proc_recvs); // 데이터를 받을 함수를 쓰레드에 따로 등록해준다 별개로 동작한다.
while(!WSAGetLastError()){ // 에러가 없으면
cin >> buffer; // 데이터 입력 받기
send(client_sock,buffer,strlen(buffer),0); 데이터 전송
}
// 종료
proc2.join(); // 스레드 호출 차단
closesocket(client_sock); // 소켓 닫기
closesocket(skt); //소켓 닫기
WSACleanup(); // 클리어
}
코드 자체가 서버랑 비슷 한것이 많이 있다. 같이 셋팅해줘야하는부분도 있기 때문이다.
#include <iostream>
#include <winsock2.h>
#include <thread>
using namespace std;
#define PACKET_SIZE 1024
SOCKET skt;
void proc_recv(){
char buffer[PACKET_SIZE] = {};
string cmd;
while(!WSAGetLastError()){
ZeroMemory(&buffer,PACKET_SIZE);
recv(skt, buffer, PACKET_SIZE,0);
cmd=buffer;
if(cmd=="hi") break;
cout << "받은 메세지: " << buffer << endl;
}
}
int main(){
WSADATA wsa; // 먼저 소켓을 생성 해준뒤
WSAStartup(MAKEWORD(2,2),&wsa); // 초기화까지 해준다.
skt = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); // 그후 소켓도 같이 만들어 넣어준다.
SOCKADDR_IN addr = {}; // 통신을 교환할 주소값 poat 번호등 설정 함
addr.sin_family = AF_INET; // 주소 체계를 설정
addr.sin_port = htons(7777); // 포트 번호를 설정
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 여기에서는 조금 다르다. 일단 한 컴퓨터에서 테스트 진행할꺼라 127.0.0.1 로 설정 한다.
while(1){
if(!connect(skt, (SOCKADDR*)&addr,sizeof(addr))) break; // 커넥트 시도를 하고 성공하면 break로 while문 나온다.
}
thread proc1(proc_recv); // 받는 부분도 스레드에 등록해서 동작한다.
char msg[PACKET_SIZE] = {0}; // 보낼 메세지를 만들어준다.
while(!WSAGetLastError()){
cin >> msg; // 입력을 받아서
send(skt, msg,strlen(msg),0);// 전송한다.
}
// 종료
proc1.join(); // 쓰레드 차단
closesocket(skt); // 소켓 닫기
WSACleanup(); // 종료
}
두 코드에서 비슷한 부분이 많다
WSADATA 생성해서 Startup 들어가고
소켓을 만들어준뒤
SOCKADDR_IN 으로 통신을 할 주소값을 설정해준뒤
connect한후 메세지를 보내거나 send, recv 등등 해준다.
그후 종료 할때 join closesocket WSACLeanup 등등 해주면서 사용한 메모리 정리도 진행한다.
지금은 채팅정도 가능하지만 조금만 변형을 해서
데이터를 구조체나 자료형을 보내고 받는다면 게임서버 처럼도 활용을 할수있을꺼같다.
빌드후 실행을 해보자.
