이번 포스팅에서는 소켓프로그래밍의 코드부터 직접 실행하는 것까지 다룰 예정이다.
먼저 앞선 포스팅에서 소켓프로그래밍을 위한 환경설정을 다루었다.
https://what-am-i.tistory.com/222
[c++]소켓프로그래밍_visualstudio(1)[환경설정]
소켓이란 애플리케이션 계층과 전송계층을 연결해주는 인터페이스라고 생각하면 된다. 이를 코드를 이용해 직접 구현해보고 실행하여 결과를 확인해보자 2개로 글을 나누었다. 코드 부분의 글
what-am-i.tistory.com
여기에서 이어서 진행할것이다.
server의 코드
#include <WinSock2.h>
#include <stdlib.h>
#include<stdio.h>
#define SERVERPORT 9000
#define BUFSIZE 512
int main(int argc, char* argv[]) {
int retval;
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
return 1;
}
SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN serveraddr;
ZeroMemory(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); // 내 IP주소를 network서버 order로 변경하여 저장
serveraddr.sin_port = htons(SERVERPORT);
bind(listen_sock, (SOCKADDR*)&serveraddr, sizeof(serveraddr));
listen(listen_sock, SOMAXCONN); //하부 프로토콜의 최대의 큐의 크기로 동시에 받아들일 수 있도록한다.
SOCKET client_sock;
SOCKADDR_IN clientaddr;
int addrlen;
char buf[BUFSIZE + 1];
while (1) {
addrlen = sizeof(clientaddr);
client_sock = accept(listen_sock, (SOCKADDR*)&clientaddr, &addrlen);
printf("\n[TCP 서버] 클라리언트 접속 : IP 주소 = %s, 포트 번호 = %d\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
while (1) {
retval = recv(client_sock, buf, BUFSIZE, 0);
buf[retval] = '\0';
printf("\n[TCP %s : %d ] %s\n", inet_ntoa(clientaddr.sin_addr),
ntohs(clientaddr.sin_port), buf);
retval = send(client_sock, buf, retval, 0);
}
closesocket(client_sock);
printf("[TCP 서버] 클라이언트 종료 : IP 주소 =%s, 포트번호 = %d\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
}
closesocket(listen_sock);
WSACleanup();
return 0;
}
client의 코드
#include <WinSock2.h>
#include <stdlib.h>
#include <stdio.h>
#define SERVERIP "127.0.0.1"
#define SERVERPORT 9000
#define BUFSIZE 512
int main(int argc, char *argv[]) {
int retval;
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return 1;
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN serveraddr;
ZeroMemory(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(SERVERIP);
serveraddr.sin_port = htons(SERVERPORT);
connect(sock, (SOCKADDR*)&serveraddr, sizeof(serveraddr));
char buf[BUFSIZE + 1];
int len;
while (1) {
printf("\n[보낼 데이터] ");
if (fgets(buf, BUFSIZE + 1, stdin) == NULL) break;
len = strlen(buf);
if (buf[len - 1] == '\n') buf[len - 1] = '\0';
if (strlen(buf) == 0) break;
retval = send(sock, buf, strlen(buf), 0);
printf("[TCP 클라이언트] %d바이트를 보냈습니다.\n", retval);
retval = recv(sock, buf, retval, 0);
if (retval == 0) break;
buf[retval] = '\0';
printf("[TCP 클라이언트] %d바이트를 받았습니다.\n", retval);
printf("[받은 데이터] %s\n", buf);
}
closesocket(sock);
WSACleanup();
return 0;
}
이렇게 코드를 만들고
각각 우측상단의 빌드 -> 솔루션 빌드를 눌러준다.
그리고 서버 먼저 ctrl + f5를 이용해 실행시키고 client를 실행시켜 주면 둘은 connect되어 echo로 대화프로그램을 실행시킬 수 있다.
다음과 같이 client에서 보내는 말을 서버에서 보여주고 다시 보내는 것을 확인할 수 있다.
코드에 대해서 설명을 한다면
WSAStartup() 은 실행에 필요한 라이브러리를 초기화한다.
socket은 사용할 소켓을 생성한다. SOCK_STREAM은 TCP를 의미하고 SOCK_DGRAM은 UDP를 의미한다.
bind에서는 생성한 소켓과 주소, 포트를 연결한다.
listen은 client가 연결을 시도하도록 기다린다. somaxconn은 주석에도 써놓았듯이 TCP가 감당할 수 있는 동시에 접속 요청을 받을 수 있는 최대 큐의 개수로 설정한다.
accept은 접속요청을 한 client와의 연결을 허가하고 연결을 위해 새로운 소켓핸들을 만들어 반환한다.
여기서 새로운 소켓핸들은 client_sock이다. 왜냐하면 listen_sock는 다른 client들의 연결도 허가하는 일을 해야하기 때문이다.
그리고 recv, send를 이용해 전달할 말을 보내고 받는다.
위 작성된 코드는 echo 코드로 client가 입력한 말을 그래도 server가 echo 한다.
이를 제대로된 채팅 프로그램으로 만드는 방법은 코드를 recv, send하는 부분을 약간만 바꿔주면 된다.
server의 while(1)의 코드를
while (1) {
retval = recv(client_sock, buf, BUFSIZE, 0);
buf[retval] = '\0';
printf("\n[TCP %s : %d ] %s\n", inet_ntoa(clientaddr.sin_addr),
ntohs(clientaddr.sin_port), buf);
printf("\n[보낼 데이터] ");
if (fgets(buf, BUFSIZE + 1, stdin) == NULL) break;
len = strlen(buf);
if (buf[len - 1] == '\n') buf[len - 1] = '\0';
if (strlen(buf) == 0) break;
retval = send(client_sock, buf, strlen(buf), 0);
}
이처럼 fgets를 이용해 입력받아 보내는 코드를 추가하고
client의 while(1)의 코드를
while (1) {
printf("\n[보낼 데이터] ");
// 입력받아 server에게 전송
if (fgets(buf, BUFSIZE + 1, stdin) == NULL) break;
len = strlen(buf);
if (buf[len - 1] == '\n') buf[len - 1] = '\0';
if (strlen(buf) == 0) break;
retval = send(sock, buf, strlen(buf), 0);
printf("[TCP 클라이언트] %d바이트를 보냈습니다.\n", retval);
retval = recv(sock, buf, BUFSIZE, 0);
if (retval == 0) break;
buf[retval] = '\0';
printf("[TCP 클라이언트] %d바이트를 받았습니다.\n", retval);
printf("[받은 데이터] %s\n", buf);
}
이렇게 수정하면 된다.
결과는 다음과 같다.
hi와 hello를 잘 전달하고 받았음을 확인할 수 있다.
'4-1 > 인터넷프로토콜' 카테고리의 다른 글
[c++]소켓프로그래밍_visualstudio(1)[환경설정] (0) | 2022.05.01 |
---|---|
[2주차정리]IP와 IP패킷헤더 (0) | 2022.04.13 |
[1주차정리]PDU,ICI,IDU,SDU,PCI,네트워크 장비(스위치, 라우터, 브리지) (0) | 2022.04.13 |
ARP와 ICMP 그리고 멀티캐스트 (0) | 2022.04.12 |
[1_2]계층간의 통신과 네트워크 장비 (0) | 2022.03.09 |