소켓 프로그래밍 보고서

본 보고서는 내 동아리인 Layer7의 숙제는 아니고, Layer7에서의 팀 프로젝트를 하기에 앞서 소켓프로그래밍을 전혀 모르는 팀원들과 함께 프로젝트 진행에 앞서 공부를 하기로 약속했기 때문이다.

그래서 오늘 그 보고서를 쓴다.

일단 구글에서 "C언어 소켓프로그래밍" 을 검색했다. 그런데, POSIX 규격에서 작동하는 <sys/socket.h>을 사용한 소켓프로그래밍 강좌가 매우 많았다.
Windows에서는 sys/socket.h가 아닌 winsock2.h를 사용해야한다.
나는 평소에 POSIX 규격을 준수하는 macOS를 사용하기때문에 내가 코딩하기에는 문제가 없었지만 동아리 프로젝트 시연은 Windows에서 진행해야 하기 때문에 좀 애를 먹었다.

나중에 시간이 된다면 이와 관련한 내용도 다루어 보도록 하겠다.

아무튼, 일단 visual studio를 켜고 속성에 다음과 같은 종속성을 추가해주어야한다.



만약 안된다면 ws2_32.lib 을 추가하면 된다.
그리고, 다음과 같은 과정을 통해 서버를 만들 수 있다.

0. 소켓 변수 및 소켓주소를 담는 구조체 생성
1. WSAStartup 함수를 실행함으로써 소켓을 지금부터 사용하겠다고 선언
2. 소켓 생성
3. 서버의 주소를 담고있는 SOCKADDR_IN 구조체를 초기화
4. 포트, 주소, 프로토콜과 같은 내용을 SOCKADDR_IN 구조체에 지정
5. 서버 바인딩
6. 리스닝 설정
7. 클라이언트 허용(accept)
8. 메시지 수신 / 보내기
9. 소켓 닫기

클라이언트는 다음과 같다.
0. 소켓 변수 및 소켓주소를 담는 구조체 생성
1. WSAStartup 함수를 실행함으로써 소켓을 지금부터 사용하겠다고 선언
2. 소켓 생성
3. 서버의 주소를 담고있는 SOCKADDR_IN 구조체를 초기화
4. 포트, 주소, 프로토콜과 같은 내용을 SOCKADDR_IN 구조체에 지정
5. 서버 커넥트
6. 리시브
7. 메시지 수신 / 보내기
8. 소켓 닫기


그래서 해당 내용들을 코드로 옮겨보면 다음과 같다.
주석만 읽어도 이해하는데 큰 이해가 없을것이니 별도의 설명은 추가하지 않겠다. 서버가 클라이언트의 메시지를 받기 전까지 대기하다가 메시지를 받으면 연결을 종료하는 프로그램이다.

서버쪽 코드는 다음과 같다.


#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <WinSock2.h>

int main(void) {
 SOCKET serverSocket, acceptSocket; // 서버 소켓과 해당 내용을 받아주는 소켓 생성
 WSADATA wsaData; // 소켓사용을 알리는 함수인 WSAStartup 함수를 위해서 필요함
 SOCKADDR_IN serverAddress, clientAddress; // 서버 주소와 클라이언트의 주소를 담는 구조체 선언
 char cache[100];
 int clientSize;

 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) { // word 형식으로 버전을 입력 받는데, makeword 매크로 함수를 통하여 상위 2바이트와 하위 2바이트에 주 버전 번호와 부 버전 번호를 적는다. 윈도우 소켓을 사용하겠다고 알린다.
  printf("Error occured while starting WSA, error %u", WSAGetLastError());
  return -1;
 }

 serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // IPv4, SOCK_STREAM for TCP, IPPROTO_TCP for TCP, but dunno why I have to set two arguments to use TCP

 if (serverSocket == INVALID_SOCKET) { // if failed to create socket
  printf("Creating socket failed, error %u", WSAGetLastError());
  WSACleanup();
  return -1;
 }

 memset(&serverAddress, 0, sizeof(serverAddress)); // initialize the structure serverAddress

 serverAddress.sin_family = PF_INET; // set the protocol family to IPv4
 serverAddress.sin_port = htons(4500); // set the port
 // the function 'htons' is a function that changes the short memory value from host byte ordering to network byte ordering.
 // host byte ordering is little endian, and network byte ordering is big endian.
 serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); // set bind area to ANY

 if (bind(serverSocket, (SOCKADDR*)& serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) {
  printf("Binding failed, error %u", WSAGetLastError());
  closesocket(serverSocket);
  WSACleanup();
  return -1;
 }

 if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) { // somaxconn means the max number of connections
  printf("Listening failed, error %u", WSAGetLastError());
  closesocket(serverSocket);
  WSACleanup();
  return -1;
 }

 clientSize = sizeof(clientAddress);
 memset(&serverAddress, 0, clientSize); // initialize the structure clientAddress
 puts("Listening...");
 
 acceptSocket = accept(serverSocket, (SOCKADDR*)& serverAddress, &clientSize);
 if (acceptSocket == INVALID_SOCKET) {
  printf("Accept failed, error %u", WSAGetLastError());
  closesocket(serverSocket);
  WSACleanup();
  return -1;
 }
 printf("Connected\nWaiting for client\'s message.\n");

 recv(acceptSocket, cache, sizeof(cache), 0);
 printf("What you've got from the client: %s", cache);

 closesocket(serverSocket);
 closesocket(acceptSocket);
 WSACleanup();

 return 0;
}
그리고 다음은 클라이언트 코드이다.


#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <WinSock2.h>

int main(int argc, char* argv) {
 SOCKET clientSocket;
 SOCKADDR_IN serverAddress;
 WSADATA wsaData;
 char input[100];

 printf("Connecting...\n\n");
 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) { // word 형식으로 버전을 입력 받는데, makeword 매크로 함수를 통하여 상위 2바이트와 하위 2바이트에 주 버전 번호와 부 버전 번호를 적는다. 윈도우 소켓을 사용하겠다고 알린다.
  printf("Error occured while starting WSA, error %u", WSAGetLastError());
  return -1;
 }

 memset(&serverAddress, 0, sizeof(serverAddress));
 clientSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

 if (clientSocket == INVALID_SOCKET) { // if failed to create socket
  printf("Creating socket failed, error %u", WSAGetLastError());
  return -1;
 }

 serverAddress.sin_family = PF_INET;
 serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1");
 serverAddress.sin_port = htons(4500);

 if (connect(clientSocket, (SOCKADDR*)& serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) {
  printf("Connection failed, error %u", WSAGetLastError());
  return -1;
 }

 printf("Connection Success !\nType here to send: ");
 gets(input);
 send(clientSocket, input, sizeof(input), 0);

 closesocket(clientSocket);
 WSACleanup();

 return 0;
}

Comments

Popular Posts