대학교 이후로 거의 접해지 못했던 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;
}