대학교 이후로 거의 접해지 못했던 C
를 다시 시작하고 있습니다.
다시 시작하는 이유는 회사에서 C
개발자가 필요하기 때문입니다. 처음이라 진입이 쉽지는 않겠지만 포기하지 않고 계속 하다 보면 언젠가는 되리라 생각합니다.
예제는 http://www.joinc.co.kr/w/Site/win_network_prog/doc/winsock_basic 의 코드를 참조하며 모르는 부분에 주석을 추가하였습니다.
지속적으로 이전에 java
로 만들었던 채팅 서버, 메신저 서버 등을 만들어볼 생각입니다.
개발환경
- IDE: Visual Studio 2012
- OS: Windows 10
[echo_server.c]
/* File: echo_server.c Desc: 에코 서버. 서버는 클라이언트의 접속 요청을 수락한 후 송신된 메시지를 출력 및 동일 메시지 송신 후 클라이언트의 연결을 종료하는 프로그램 Date: 2016. 04. 27 Author: coozplz@gmail.com */ #include <winsock.h> #include <stdio.h> // Winsock 라이브러리 로딩을 위한 코드 #pragma comment(lib, "Ws2_32.lib"); #define MAX_PACKET_LEN 512 #define PORT 5552 #define BACK_LOG_SIZE 5 int main() { WSADATA wsaData; int status; int socketLength; int readSize, writeSize; SOCKET endPointSocket, clientSocket; struct sockaddr_in SocketInfo, ClientSocketInfo; char buffer[MAX_PACKET_LEN]; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("WSAStartup error \n"); return 0; } endPointSocket = socket(AF_INET, SOCK_STREAM, 0); if (endPointSocket == INVALID_SOCKET) { printf("endPointSocket is invalid \n"); return 0; } printf("Server is running on port %d\n", PORT); // 구조체를 초기화한다. ZeroMemory(&SocketInfo, sizeof(struct sockaddr_in)); SocketInfo.sin_family = AF_INET; // htons(): Host 시스템에서 Network 로 short 형 데이터를 보낼 때 바이트 오더를 바꿔주는 함수 SocketInfo.sin_port = htons(PORT); // htonl(): long형 데이터의 바이트 오더를 바꿔주는 함수. // INADDR_ANY 는 OS에 설정되어 있는 아이피 어느것이나 바인딩 한다는 의미 SocketInfo.sin_addr.S_un.S_addr = htonl(INADDR_ANY); status = bind(endPointSocket, (struct sockaddr*)&SocketInfo, sizeof(struct sockaddr_in)); if (status == SOCKET_ERROR) { printf("failed to bind socket \n"); return 0; } // // backlog 인자는 아직 미결인 연결들에 대한 큐의 늘어날 수 있는 최대 길이를 정의한다. // 큐에 도착한 연결 요청들이 꽉 찬다면 클라이언트는 ECONNREFUSED를 가리키는 에러를 받거나 만일 하위 프로토콜이 재전송을 지원한다면, 요청은 재시도가 성공되도록 하기 위해 무시된다. // status = listen(endPointSocket, BACK_LOG_SIZE); if (status == SOCKET_ERROR) { printf("failed to listen socket \n"); return 0; } while (TRUE) { // 클라이언트 구조체 초기화 ZeroMemory(&ClientSocketInfo, sizeof(struct sockaddr_in)); socketLength = sizeof(struct sockaddr_in); clientSocket = accept(endPointSocket, (struct sockaddr *) &ClientSocketInfo, &socketLength); if (clientSocket == INVALID_SOCKET) { printf("client socket is invalid\n"); closesocket(clientSocket); continue; } // inet_ntoa() : 네트워크 바이트 순서의 32비트 값을 Dotted-Decimal notation 의 주소값으로 변환 // 예 0x6601a8c0: 102.1.168.192 printf("[Accepted] %s:%d\n", inet_ntoa(ClientSocketInfo.sin_addr), (int)ntohs(ClientSocketInfo.sin_port)); ZeroMemory(&buffer, sizeof(buffer)); readSize = recv(clientSocket, (void *)buffer, MAX_PACKET_LEN, 0); if (readSize > 0) { printf("Message from client > %s\n", buffer); writeSize = send(clientSocket, buffer, readSize, 0); } else { printf("Error on reading\n"); } closesocket(clientSocket); printf("[Closed] %s:%d\n", inet_ntoa(ClientSocketInfo.sin_addr), (int)ntohs(ClientSocketInfo.sin_port)); } closesocket(endPointSocket); WSACleanup(); return 0; }
[echo_client.c]
/* File: echo_client.c Desc: 에코 클라이언트. 서버에 접속 요청을 한 후 사용자가 입력한 메시지를 서버에 송신 후 응답 메시지를 출력한 후 소켓 연결이 종료되었는지 판단하고 소켓 연결을 종료한다. Date: 2016. 04. 27 Author: coozplz@gmail.com */ #include <stdio.h> #include <WinSock2.h> // Winsock 라이브러리 로딩을 위한 코드 #pragma comment(lib, "Ws2_32.lib"); #define PORT_NUM 5552 #define MAX_LEN 1024 int main(int argc, char **argv) { SOCKET socketFd; WSADATA wsaData; struct sockaddr_in addr; char buffer[MAX_LEN]; char receiveBuffer[MAX_LEN]; int status = 0; int readSize = 0; status = WSAStartup(MAKEWORD(2, 2), &wsaData); if (status != NO_ERROR) { printf("WSAStart has error\n"); return 1; } socketFd = socket(AF_INET, SOCK_STREAM, 0); if (socketFd == INVALID_SOCKET) { printf("socketFd is invalid\n"); return 1; } ZeroMemory(&addr, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; // // inet_addr() : Dotted-decimal 타입을 네트워크 32바이트 순서로 변경 // addr.sin_addr.S_un.S_addr = inet_addr("192.168.10.52"); addr.sin_port = htons(PORT_NUM); status = connect(socketFd, (struct sockaddr_in *)&addr, sizeof(addr)); if (status == SOCKET_ERROR) { printf("connection failed...\n"); return 1; } while (TRUE) { printf("Connection is established...\n"); printf("> "); fgets(buffer, MAX_LEN - 1, stdin); if (strncmp(buffer, "quit\n", 5) == 0) { break; } printf("SEND: %s", buffer); send(socketFd, (void *)buffer, strlen(buffer), 0); ZeroMemory(&receiveBuffer, sizeof(receiveBuffer)); readSize = recv(socketFd, (void *)receiveBuffer, MAX_LEN, 0); printf("READ = %s\n", receiveBuffer); // // 소켓연결이 종료되었는지 판단하기 위해 다시 recv() 함수를 호출한다. // ZeroMemory(&receiveBuffer, sizeof(receiveBuffer)); readSize = recv(socketFd, (void *)receiveBuffer, MAX_LEN, 0); if (readSize <= 0) { printf("socket is disconnected by server!!\n"); break; } } closesocket(socketFd); WSACleanup(); printf("Press any key to exit program."); getchar(); return 0; }