티스토리 뷰
- 실행 함수(비동기 + 논-블로킹)
- WSASend
- 비동기 입출력 소켓
- WSABUF 배열의 시작 주소 + 개수
- 보내고/받은 바이트 수
- 상세 옵션인데 0
- WSAOVERLAPPED 구조체 주소값
- 입출력이 완료되면 OS가 호출할 콜백 함수
- WSARecv
- Scatter-Gater
AcceptExConnectEx- WSAWaitForMultipleEvents
- WSAGetOverlappedResult
- 비동기 소켓
- 넘겨준 overlapped 구조체
- 전송된 바이트 수
- 비동기 입출력 작업이 끝날때까지 대기할지? (false)
- 비동기 입출력 작업 관련 부가 정보. 거의 사용 안함.
- 실행 과정 (이벤트 방식)
- Overlapped 함수를 건다 (WSARecv, WSASend)
- Overlapped 함수가 성공했는지 확인 후
- → 성공했으면 결과를 얻어서 처리
- → 실패했으면 사유를 확인
- Overlapped 이벤트 기반 코드 작동 방식
- 비동기 입출력 지원하는 소켓 생성 + 통지 받기 위한 이벤트 객체 생성.
- 비동기 입출력 함수 호출 (1에서 만든 이벤트 객체를 같이 넘겨줌).
- 비동기 작업이 바로 완료되지 않으면, WSA_IO_PENDING 오류 코드.
- 운영체제는 이벤트 객체를 signaled 상태로 만들어서 완료 상태 알려줌.
- WSAWaitForMultipleEvents 함수 호출해서 이벤트 객체의 signal 판별
- WSAGetOverlappedResult 호출해서 비동기 입출력 결과 확인 및 데이터 처리
#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
//소켓 정보 저장을 위한 구조체와 변수
struct SOCKETINFO {
WSAOVERLAPPED overlapped;
SOCKET sock;
char buf[BUFSIZE + 1];
int recvBytes;
int sendBytes;
WSABUF wsabuf;
};
int nTotalSockets = 0;
SOCKETINFO* SocketInfoArray[WSA_MAXIMUM_WAIT_EVENTS];
WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
CRITICAL_SECTION cs;
//비동기 입출력 처리 함수
DWORD WINAPI WorkerThread(LPVOID arg);
//소켓 관리 함수
BOOL AddSocketInfo(SOCKET sock);
void RemoveSocketInfo(int nIndex);
//오류 출력 함수
void err_quit(const char* msg);
int main()
{
WSADATA wsaData;
SOCKADDR_IN sock_addr;
int retVal;
//윈속 초기화
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
err_quit("WSAStartup");
InitializeCriticalSection(&cs);
//소켓 생성
SOCKET listen_s = socket(AF_INET, SOCK_STREAM, 0);
if (listen_s == INVALID_SOCKET)
err_quit("socket");
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");
if (listen(listen_s, SOMAXCONN) == SOCKET_ERROR) err_quit("listen");
//더미(dummy) 이벤트 객체 생성
WSAEVENT hEvent = WSACreateEvent();
if (hEvent == WSA_INVALID_EVENT) err_quit("WSACreateEvent()");
EventArray[nTotalSockets++] = hEvent;
//스레드 생성
HANDLE hThread = CreateThread(NULL, 0, WorkerThread, NULL, 0, NULL);
if (hThread == NULL) return 1;
CloseHandle(hThread);
//데이터 통신에 사용할 변수
SOCKET clientsock;
SOCKADDR_IN clientaddr;
int addrlen;
DWORD recvbytes, flags;
while (1) {
//소켓 초기화
addrlen = sizeof(clientaddr);
clientsock = accept(listen_s, (SOCKADDR*)&clientaddr, &addrlen);
if (clientsock == INVALID_SOCKET) {
err_quit("accept()");
break;
}
if (::WSAGetLastError() == WSAEWOULDBLOCK)
continue;
printf("[TCP서버] 클라이언트 접속 IP : %s %d\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
//소켓 정보 추가
if (AddSocketInfo(clientsock) == FALSE) {
closesocket(clientsock);
printf("[TCP서버] 클라이언트 종료 IP : %s %d\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
continue;
}
//비동기 입출력 시작
SOCKETINFO* ptr = SocketInfoArray[nTotalSockets - 1];
flags = 0;
retVal = WSARecv(ptr->sock, &ptr->wsabuf, 1, &recvbytes, &flags, &ptr->overlapped, NULL);
if (retVal == SOCKET_ERROR) {
if (WSAGetLastError() != WSA_IO_PENDING) {
err_quit("WSARecv()");
RemoveSocketInfo(nTotalSockets - 1);
continue;
}
}
//소켓의 개수(nTotalSockets) 변화를 알림
WSASetEvent(EventArray[0]);
}
//윈속 종료
DeleteCriticalSection(&cs);
WSACleanup();
return 0;
}
DWORD WINAPI WorkerThread(LPVOID arg) {
int retVal;
while (1) {
//이벤트 객체 관찰
DWORD index = WSAWaitForMultipleEvents(nTotalSockets, EventArray, FALSE, WSA_INFINITE, FALSE);
if (index == WSA_WAIT_FAILED) continue;
//여기서 리턴 하는 값은 인덱스 + WSA_WAIT_EVENT_0 의 값이므로 WAS값을 빼줘야 한다.
index -= WSA_WAIT_EVENT_0;
WSAResetEvent(EventArray[index]);
if (index == 0) continue;
//클라 정보 얻기
SOCKETINFO* ptr = SocketInfoArray[index];
SOCKADDR_IN clientaddr;
int addrlen = sizeof(clientaddr);
getpeername(ptr->sock, (SOCKADDR*)&clientaddr, &addrlen);
//비동기 입출력 결과 확인
DWORD cbTransferred, flags;
retVal = WSAGetOverlappedResult(ptr->sock, &ptr->overlapped, &cbTransferred, FALSE, &flags);
if (retVal == FALSE || cbTransferred == 0) {
RemoveSocketInfo(index);
printf("[TCP 서버] 클라이언트 종료 : IP 주소 = %s, 포트번호 = %d\r\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
continue;
}
//데이터 전송량 갱신
if (ptr->recvBytes == 0) {
ptr->recvBytes = cbTransferred;
ptr->sendBytes = 0;
//받은 데이터 출력
ptr->buf[ptr->recvBytes] = '\0';
printf("[TCP /%s : %d] %s\r\n", inet_ntoa(clientaddr.sin_addr),
ntohs(clientaddr.sin_port), ptr->buf);
}
else {
ptr->sendBytes += cbTransferred;
}
if (ptr->recvBytes > ptr->sendBytes) {
//데이터 보내기
ZeroMemory(&ptr->overlapped, sizeof(ptr->overlapped));
ptr->overlapped.hEvent = EventArray[index];
ptr->wsabuf.buf = ptr->buf + ptr->sendBytes;
ptr->wsabuf.len = ptr->recvBytes - ptr->sendBytes;
DWORD sendbytes;
retVal = WSASend(ptr->sock, &ptr->wsabuf, 1, &sendbytes, 0, &ptr->overlapped, NULL);
if (retVal == SOCKET_ERROR) {
if (WSAGetLastError() != WSA_IO_PENDING) {
err_quit("WSASend()");
}
continue;
}
}
else {
ptr->recvBytes = 0;
//데이터 받기
ZeroMemory(&ptr->overlapped, sizeof(ptr->overlapped));
ptr->overlapped.hEvent = EventArray[index];
ptr->wsabuf.buf = ptr->buf;
ptr->wsabuf.len = BUFSIZE;
DWORD recvbytes;
flags = 0;
retVal = WSARecv(ptr->sock, &ptr->wsabuf, 1, &recvbytes, &flags, &ptr->overlapped, NULL);
if (retVal == SOCKET_ERROR) {
if (WSAGetLastError() != WSA_IO_PENDING) {
err_quit("WSASend()");
}
continue;
}
}
}
}
//소켓 정보 추가
BOOL AddSocketInfo(SOCKET sock) {
EnterCriticalSection(&cs);
if (nTotalSockets >= WSA_MAXIMUM_WAIT_EVENTS) {
LeaveCriticalSection(&cs);
return FALSE;
}
SOCKETINFO* ptr = new SOCKETINFO;
if (ptr == NULL) {
LeaveCriticalSection(&cs);
return FALSE;
}
WSAEVENT hEvent = WSACreateEvent();
if (hEvent == WSA_INVALID_EVENT) {
LeaveCriticalSection(&cs);
return FALSE;
}
ZeroMemory(&ptr->overlapped, sizeof(ptr->overlapped));
ptr->overlapped.hEvent = hEvent;
ptr->sock = sock;
ptr->recvBytes = ptr->sendBytes = 0;
ptr->wsabuf.buf = ptr->buf;
ptr->wsabuf.len = BUFSIZE;
SocketInfoArray[nTotalSockets] = ptr;
EventArray[nTotalSockets] = hEvent;
++nTotalSockets;
LeaveCriticalSection(&cs);
return TRUE;
}
//소켓 정보 삭제
void RemoveSocketInfo(int nIndex) {
EnterCriticalSection(&cs);
SOCKETINFO* ptr = SocketInfoArray[nIndex];
closesocket(ptr->sock);
delete ptr;
WSACloseEvent(EventArray[nIndex]);
if (nIndex != (nTotalSockets- 1))
SocketInfoArray[nIndex] = SocketInfoArray[nTotalSockets - 1];
--nTotalSockets;
LeaveCriticalSection(&cs);
}
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);
}
'서버 공부 > 네트워크' 카테고리의 다른 글
TCP/IP) Completion Port 모델 (0) | 2022.07.12 |
---|---|
TCP/IP) Overlapped I/O(2) 모델(CallBack) (0) | 2022.07.12 |
TCP/IP 윈도우 소켓 프로그래밍 10장 연습문제(with 스레드풀) (0) | 2022.07.10 |
TCP/IP) WSAEventSelect 모델 통신 (0) | 2022.07.09 |
TCP/IP) Select 모델 서버 (0) | 2022.07.08 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- Dreamhack
- 학교
- 워셜알고리즘
- 레지스터
- 개발
- 링크드 리스트
- Select모델
- 야경
- queue
- 백준
- 컨퍼런스
- 지뢰찾기
- BFS
- 고양이
- 더블버퍼링
- 인제대학교
- 알고리즘
- 보안
- STL
- 스레드풀
- 멀티쓰레드
- 자료구조
- c++
- 드림핵
- 시스템보안
- 정보보안
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
글 보관함