티스토리 뷰
여태껏 만들던 서버는 동기 형식의 서버로써, accept() | connect() | send() | recv() | sendto() | recvfrom() 함수 모두 호출 시 조건이 만족될 때 까지 무한정 대기하는 형식을 취합니다. 따라서 조건이 맞지 않으면 다른 행동을 할 수 없습니다.
그렇다면, 비동기 형식은 무엇이냐? 비동기 형식은 소켓 함수 호출 시 조건이 만족되지 않더라도 함수가 리턴을 하므로 중단 없이 다음 코드를 수행할 수 있습니다. 다만 조건이 만족되지 않아서 리턴하게 되면 WSAEWOULDBLOCK코드를 리턴합니다. 따라서 WSAGetLastError()를 통해서 확인을 해야 합니다.
호출 방식은 아래와 같습니다. 전체적인 예제도 참고해서 봐주시면 감사하겠습니다. Client는 기본 세팅으로도 가능하기에 따로 올리지는 않겠습니다.
//소켓 생성
listen_s = socket(AF_INET, SOCK_STREAM, 0);
if (listen_s == INVALID_SOCKET)
err_quit("socket");
//비동기 소켓으로 전환 하는 과정
u_long on = 1;
retVal = ioctlsocket(listen_s, FIONBIO, &on);
if (retVal == SOCKET_ERROR) err_quit("ioctlsocket()");
//DummyServer
#pragma warning(disable:4996)
#pragma comment(lib, "ws2_32.lib")
#include<stdio.h>
#include<Winsock2.h>
#include<stdlib.h>
#define PORT 9000
#define BUFSIZE 512
void err_quit(const char* msg) {
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL);
printf("[%s] %s\n", msg, (char*)lpMsgBuf);
LocalFree(lpMsgBuf);
exit(1);
}
int main()
{
WSADATA wsaData;
SOCKET listen_s, clientsock;
SOCKADDR_IN sock_addr, clientaddr;
char buf[BUFSIZE + 1];
int addrlen, retVal;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
err_quit("WSAStartup");
//소켓 생성
listen_s = socket(AF_INET, SOCK_STREAM, 0);
if (listen_s == INVALID_SOCKET)
err_quit("socket");
//비동기 소켓으로 전환 하는 과정
u_long on = 1;
retVal = ioctlsocket(listen_s, FIONBIO, &on);
if (retVal == SOCKET_ERROR) err_quit("ioctlsocket()");
ZeroMemory(&sock_addr, sizeof(struct sockaddr_in));
sock_addr.sin_family = PF_INET;
sock_addr.sin_port = htons(PORT);
sock_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
if (bind(listen_s, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in)) == SOCKET_ERROR)
{
err_quit("Bind");
return 1;
}
if (listen(listen_s, 10) == SOCKET_ERROR)
{
err_quit("listen");
return 1;
}
while (1) {
//
ACCEPT_AGAIN:
addrlen = sizeof(clientaddr);
clientsock = ::accept(listen_s, (struct sockaddr*)&clientaddr, &addrlen);
if (clientsock == INVALID_SOCKET) {
if (WSAGetLastError() == WSAEWOULDBLOCK)
goto ACCEPT_AGAIN;
err_quit("accept()");
break;
}
//접속한 클라리언트 정보 출력
printf("[TCP서버] 클라이언트 접속 IP : %s %d\r\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
//클라이언트와 데이터 통신
while (1) {
RECEVICE_AGAIN:
retVal = recv(clientsock, buf, BUFSIZE, 0);
if (retVal == SOCKET_ERROR) {
if (WSAGetLastError() == WSAEWOULDBLOCK)
goto RECEVICE_AGAIN;
err_quit("recv()");
break;
}
else if (retVal == 1)
break;
//받은 데이터 출력
buf[retVal] = '\0';
printf("[TCP/%s : %d] %s\r\n", inet_ntoa(clientaddr.sin_addr),
ntohs(clientaddr.sin_port), buf);
//데이터 보내기
SEND_AGAIN:
retVal = send(clientsock, buf, retVal, 0);
if (retVal == SOCKET_ERROR) {
if (WSAGetLastError() == WSAEWOULDBLOCK)
goto SEND_AGAIN;
err_quit("send()");
break;
}
}
closesocket(clientsock);
printf("[TCP 서버] 클라이언트 종료 : IP 주소 = %s, 포트번호 = %d\r\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
}
closesocket(listen_s);
WSACleanup();
return 0;
}
'서버 공부 > 네트워크' 카테고리의 다른 글
TCP/IP) Select 모델 서버 (0) | 2022.07.08 |
---|---|
이상적인 소켓 입출력 모델의 특징 (0) | 2022.07.08 |
GUI 멀티쓰레드 소켓 프로그래밍(TCP) 2 - 호스트이름 구하기 추가 (0) | 2022.07.07 |
GUI 멀티쓰레드 소켓 프로그래밍(TCP) (0) | 2022.07.07 |
TCP/IP 윈도우 소켓 프로그래밍 7장 연습문제 (0) | 2022.07.05 |
댓글