주의 사항 원래 코드 파일인 C로는 컴파일되는 코드지만 CPP 로 하면 안되는 부분이 있습니다. GetProcAddress 와 FreeLibrary 인 곳인데, 첫번째 인수가 struct HISTANCE 형이여야 합니다. C 코드는 그냥 void 포인터로 넣어도 문제가 없는데 CPP 에서는 형검사(타입 체킹)을 하기 때문에 문제가 발생합니다.
cannot convert parameter 1 from 'void *' to 'struct HINSTANCE__ *'
(HISTANCE)를 붙여서 강제 형변환(type-casting)을 하면 해결할수 있습니다. GetProcAddress(hndlIcmp, "IcmpCreateFile"); -> GetProcAddress((HINSTANCE)hndlIcmp, "IcmpCreateFile"); FreeLibrary(hndlIcmp); -> FreeLibrary((HINSTANCE)hndlIcmp);
C 에서는 문제가 없습니다.
실행 화면
코드 내용
/*
저작권
http://www.sockaddr.com/ExampleSourceCode.html
http://cakel.tistory.com
교육용을 전제로 자유롭게 사용할수 있습니다.
PingI.cpp -- 마이크로소프트 ICMP API 를 사용한 간단한 ping 프로그램
사용법 : pingi.exe [호스트 주소]
*/
#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <string.h>
#pragma comment(lib,"wsock32.lib")
typedef struct tagIPINFO
{ // IP 정보 태그 관련 구조체 입니다.
u_char Ttl; // Time To Live
u_char Tos; // Type Of Service
u_char IPFlags; // IP 플래그
u_char OptSize; // 옵션 데이터의 크기
u_char FAR *Options; // 옵션 데이터 버퍼
}IPINFO, *PIPINFO;
typedef struct tagICMPECHO
{ // 에코 요청 자료의 구조체
u_long Source; // 원본 주소
u_long Status; // IP 상태
u_long RTTime; // 순회 시간(Round Trip Time : RTT) ms
u_short DataSize; // 응답 데이터 크기
u_short Reserved; // 알수 없음
void FAR* pData; // 응답 데이터 버퍼
IPINFO ipInfo; // 응답 옵션
}ICMPECHO, *PICMPECHO;
// ICMP.DLL 출력 함수 포인터
HANDLE (WINAPI* pIcmpCreateFile)(VOID);
BOOL (WINAPI* pIcmpCloseHandle)(HANDLE);
DWORD (WINAPI* pIcmpSendEcho)
(HANDLE, DWORD, LPVOID, WORD, PIPINFO, LPVOID, DWORD, DWORD);
//
// main 함수 부분입니다.
int main(int argc, char* *argv)
{
WSADATA wsaData; // WinSock Applicatoin DATA
ICMPECHO icmpEcho; // Internet Control Message Protocol 에코 응답 버퍼
HANDLE hndlIcmp; // ICMP.DLL 를 다룰 LoadLibrary() 함수의 핸들
HANDLE hndlFile; // IcmpCreateFile() 함수의 핸들
LPHOSTENT pHost; // 호스트 객체 구조를 가리키는 포인터
struct in_addr iaDest; // 인터넷 주소 구조체
DWORD *dwAddress; // IP 주소
IPINFO ipInfo; // IP 옵션 구조
int nRet; // 일반적으로 응답 코드로 사용
DWORD dwRet; // DWORD(더블워드형) 응답 코드
int x;
// 인수 확인
if(argc != 2)
{
fprintf(stderr,"\n사용법: pingi [호스트 또는 IP주소]\n");
return -1;
}
// 동적으로 ICMP.DLL 을 코드 수행시 바로 불러옵니다.
hndlIcmp = LoadLibrary("ICMP.DLL");
if(hndlIcmp == NULL)
{
fprintf(stderr,"\nICMP.DLL 파일을 불러오지 못했습니다.\n");
return -1;
}
// ICMP 함수 포인터를 반환 받습니다.
pIcmpCreateFile = ( HANDLE (WINAPI*)(void) )GetProcAddress((HINSTANCE)hndlIcmp, "IcmpCreateFile");
pIcmpCloseHandle = ( BOOL (WINAPI* )(HANDLE) )GetProcAddress((HINSTANCE)hndlIcmp, "IcmpCloseHandle");
pIcmpSendEcho = (DWORD (WINAPI* )(HANDLE, DWORD, LPVOID, WORD, PIPINFO, LPVOID, DWORD, DWORD))
GetProcAddress((HINSTANCE)hndlIcmp,"IcmpSendEcho");
//모든 함수 포인터를 확인합니다.
if(pIcmpCreateFile == NULL || pIcmpCloseHandle == NULL || pIcmpSendEcho == NULL)
{
fprintf(stderr,"\nICMP 프로시져 주소를 찾는데 문제가 생겼습니다.\n");
FreeLibrary((HINSTANCE)hndlIcmp);
return -1;
}
// 윈속을 시작 합니다.
nRet = WSAStartup(0x0101, &wsaData); // 귀찮아서 MAKEWORD(1,1)을 쓰지 않고 바로 0x0101 로 WORD 형 자료를 완성합니다.
if(nRet)
{
fprintf(stderr,"\nWSAStartup() 오류 발생했습니다. 코드 번호 : %d\n", nRet);
WSACleanup();
FreeLibrary((HINSTANCE)hndlIcmp);
return -1;
}
// 윈속 버전을 확인 합니다.
if (0x0101 != wsaData.wVersion)
{
fprintf(stderr,"\n이 컴퓨터의 윈속이 버전 1.1을 지원하지 않습니다.\n");
WSACleanup();
FreeLibrary((HINSTANCE)hndlIcmp);
return -1;
}
// 목적지를 검색합니다.
// inet_addr() 함수로 호스트 명인지 호스트 주소인지 검사 합니다.
iaDest.s_addr = inet_addr(argv[1]);
if(iaDest.s_addr == INADDR_NONE) // IP주소가 아닌 경우
pHost = gethostbyname(argv[1]);
else // IP 주소인 경우
pHost = gethostbyaddr( (const char*)&iaDest, sizeof(struct in_addr), AF_INET);
if (pHost == NULL) // 목적지 호스트에 도달하지 못한 경우
{
fprintf(stderr,"\n%s 호스트를 찾을수 없었습니다.\n", argv[1]);
WSACleanup();
FreeLibrary((HINSTANCE)hndlIcmp);
return -1;
}
// 사용자에게 우리가 무얼 할지 가르쳐 줍니다.
printf("%s [%s] 호스트 로 핑(Ping)합니다.", pHost->h_name, inet_ntoa( (*(LPIN_ADDR)pHost->h_addr_list[0])) );
// IP 주소를 복사 합니다.
dwAddress = (DWORD*) (*pHost->h_addr_list);
// ICMP 에코 요청 핸들을 가집니다.
hndlFile = pIcmpCreateFile();
for(x = 0; x < 4; x++) // 4번 재시도 합니다.
{
// 그럴싸한 기본 값을 설정합니다.
ipInfo.Ttl = 255;
ipInfo.Tos = 0;
ipInfo.OptSize = 0;
ipInfo.Options = NULL;
// icmpEcho.ipInfo.Ttl = 256;
// ICMP 에코 요청을 합니다.
dwRet = pIcmpSendEcho(
hndlFile, // IcmpCreateFile() 함수에서 가저온 핸들
*dwAddress, // 목적지 IP 주소
NULL, // 보낼 버퍼를 가리키는 포인터
0, // 보낼 버퍼의 바이트 길이
&ipInfo, // 요구 옵션
&icmpEcho, // 응답 버퍼
sizeof(struct tagICMPECHO), // 에코 보낼 자료의 주소 길이
5000); // 기타릴 시간 밀리초(천분의 1초 => 5초)
// 결과를 출력합니다.
iaDest.s_addr = icmpEcho.Source;
printf("\n %s 에서 응답이 왔습니다. 시간 = %ldms TTL=%d",
inet_ntoa(iaDest),
icmpEcho.RTTime,
icmpEcho.ipInfo.Ttl);
if(icmpEcho.Status)
{
printf("\n오류 : icmpEcho.Status 상태코드 = %ld", icmpEcho.Status);
break;
}
}
printf("\n");
// 요구 했던 파일 핸들을 닫습니다.
pIcmpCloseHandle(hndlFile);
FreeLibrary((HINSTANCE)hndlIcmp);
WSACleanup();
return 0;
}
// 저작권
// http://www.sockaddr.com/ExampleSourceCode.html
// http://cakel.tistory.com
// 교육용을 전제로 자유롭게 쓰실수 있습니다.
//
// ping.h - RFC 규격에 맞게 ICMP 와 IP 헤더를 지정하는 파일입니다.
//
// packing 용량 단위 설정하는 부분입니다. alignment 를 1 로 두어서 최소 자료 전송을 노립니다.
// 대신에 접근하는 속도가 떨어지므로 좋은 방법만은 아닙니다.
#pragma pack(1)
#define ICMP_ECHOREPLAY 0
#define ICMP_ECHOREQ 8
// IP 헤더 부분 -- RFC 791
// http://www.faqs.org/rfcs/rfc791.html
// http://blog.naver.com/after1007/50014330586 참고
typedef struct tagIPHDR
{
u_char VIHL; // 버전과 IHL(Internet Header Length) 값
u_char TOS; // Type of Service (서비스의 형태)
short TotLen; // Total Length (전체 길이)
short ID; // Identification (식별자)
short FlagOff; // Fragment Offset (단편 좌표)
u_char TTL; // Time to Live (생존 시간)
u_char Protocol; // Protocol (프토토콜, 전송 규약)
u_short Checksum; // 체크섬(값 비교)
struct in_addr iaSrc; // Internet Address - Source (인터넷 주소 - 원본)
struct in_addr iaDsc; // Internet Address - Destination (인터넷 주소 - 대상)
} IPHDR, *PIPHDR;
// ICMP 헤더 -- RFC 792
// http://www.faqs.org/rfcs/rfc792.html
// http://www.microsoft.com/korea/technet/deploy/tcpintro5.asp
// http://technet2.microsoft.com/WindowsServer/ko/Library/732438fe-70c5-4e68-9663-ecbd955d29ea1042.mspx?mfr=true
// http://blog.naver.com/isnta/20024953151 참고
typedef struct tagICMPHDR
{
u_char Type; // 형식
u_char Code; // 코드
u_short Checksum; // 체크섬
u_short ID; // 식별자
u_short Seq; // 순서
char Data; // 자료(데이터)
} ICMPHDR, *PICMPHDR;
#define REQ_DATASIZE 32 // 에코(Echo) 요구 자료 길이
// ICMP Echo Request - 에코 자료 요구
typedef struct tagECHOREQUEST
{
ICMPHDR icmpHdr;
DWORD dwTime;
char cData[REQ_DATASIZE];
} ECHOREQUEST, *PECHOREQUEST;
// ICMP Echo Reply // 에코 응답
typedef struct tagECHOREPLY
{
IPHDR ipHdr;
ECHOREQUEST echoRequest;
char cFiller[256];
} ECHOREPLY, *PECHOREPLY;
#pragma pack() // packing 용량 단위 기본값으로 변경합니다.
//
// 저작권
// http://www.sockaddr.com/ExampleSourceCode.html
// http://cakel.tistory.com
// 교육용을 전제로 자유롭게 쓰실수 있습니다.
//
// ping.cpp -- ICMP 와 RAW 소켓을 사용하는 Ping 프로그램입니다.
//
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#include "ping.h"
#pragma comment(lib,"wsock32.lib")
// 내부 함수
void Ping(LPCSTR pstrHost);
void ReportError(LPCSTR pstrFrom);
int WaitForEchoReply(SOCKET s);
u_short in_cksum(u_short *addr, int Len);
// ICMP 에코 요구/응답 함수
int SendEchoRequest(SOCKET, LPSOCKADDR_IN);
DWORD RecvEchoReply(SOCKET, LPSOCKADDR_IN, u_char*);
// 시작
int main(int argc, char* *argv)
{
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(1,1);
int nRet;
// 인수 비교
if(argc != 2)
{
fprintf(stderr,"\n사용법 : ping [호스트명]\n");
return 1;
}
// 윈속 시작
nRet = WSAStartup(wVersionRequested, &wsaData);
if(nRet)
{
fprintf(stderr,"\n윈속을 시작하는데 오류 발생했습니다.\n");
return 1;
}
// ping 작업을 합니다.
Ping(argv[1]);
// 윈속을 비웁니다.
WSACleanup();
return 0;
}
// Ping() 함수
// SendEchoRequest() 함수와 RecvEchoReply() 함수를 수행하고
// 결과를 출력합니다.
void Ping(LPCSTR pstrHost)
{
SOCKET rawSocket;
LPHOSTENT lpHost;
struct sockaddr_in saDest;
struct sockaddr_in saSrc;
DWORD dwTimeSent;
DWORD dwElapsed;
u_char cTTL;
int nLoop;
int nRet;
// Raw 소켓을 생성합니다.
rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if(rawSocket == SOCKET_ERROR)
{
ReportError("socket() 함수 문제 발생");
return;
}
// 호스트를 찾아 봅니다(Lookup host)
lpHost = gethostbyname(pstrHost);
if(lpHost == NULL)
{
fprintf(stderr, "\n호스트를 찾을수가 없습니다 : %s\n", pstrHost);
return;
}
// 목적지 소켓 주소로 연결을 시도 합니다.
saDest.sin_addr.s_addr = *((u_long FAR *)(lpHost->h_addr));
saDest.sin_family = AF_INET;
saDest.sin_port = 0;
// 사용자에게 프로그램이 할려고 하는 작업을 표시 합니다.
printf("\n%s [%s] 호스트에게 %d 바이트의 자료를 핑합니다.\n",
pstrHost,
inet_ntoa(saDest.sin_addr),
REQ_DATASIZE);
// 몇번(4) 핑 작업을 합니다.
for(nLoop = 0; nLoop < 4; nLoop++)
{
// ICMP 에코 요구(Echo request)를 보냅니다.
SendEchoRequest(rawSocket, &saDest);
// select() 함수로 자료가 받을때 까지 기다립니다.
nRet = WaitForEchoReply(rawSocket);
if(nRet == SOCKET_ERROR)
{
ReportError("select() 함수에서 문제 발생");
break;
}
if(!nRet)
{
printf("\n시간 초과");
break;
}
// 응답을 받습니다.
dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);
// 소요된 시간을 계산합니다.
dwElapsed = GetTickCount() - dwTimeSent;
printf("\n%s 에서 응답을 받았습니다. 바이트=%d 시간 %ldms TTL=%d",
inet_ntoa(saSrc.sin_addr), REQ_DATASIZE,
dwElapsed, cTTL);
}
printf("\n");
nRet = closesocket(rawSocket);
if(nRet == SOCKET_ERROR)
ReportError("closesocket() 함수 수행중 오류 발생.");
}
// SendEchoRequest()
// 에고 요구 헤더 정보를 채우고 목적지로 보냅니다.
int SendEchoRequest(SOCKET s, LPSOCKADDR_IN lpstToAddr)
{
static ECHOREQUEST echoReq;
static nId = 1;
static nSeq = 1;
int nRet;
// 에코 요구 정보를 채웁니다.
echoReq.icmpHdr.Type = ICMP_ECHOREQ;
echoReq.icmpHdr.Code = 0;
echoReq.icmpHdr.Checksum = 0;
echoReq.icmpHdr.ID = nId++;
echoReq.icmpHdr.Seq = nSeq++;
// 보낼 정보를 채웁니다.
for(nRet = 0; nRet < REQ_DATASIZE; nRet++)
echoReq.cData[nRet] = ' '+nRet;
// 보낼 때 틱 횟수(tick count, 시간) 저장합니다.
echoReq.dwTime = GetTickCount();
// 보낼 자료를 패킷에다 넣고 체크섬(비교값)을 계산합니다.
echoReq.icmpHdr.Checksum = in_cksum( (u_short*)&echoReq, sizeof(ECHOREQUEST) );
// 에코 요구 신호를 보냅니다.
// sendto(소켓, 전송할 데이터, 데이터의 길이, 역할 나타내는 플래그, 원격 호스트 주소, 원격 호스트 주소의 크기)
// 소켓이 접속의 유무와 관계 없이 일방적으로 전송하는 함수 입니다.
// http://white.chungbuk.ac.kr/~jchern/sendto.html 참고
nRet = sendto(s, // 소켓
(LPSTR)&echoReq, // 버퍼
sizeof(ECHOREQUEST), // 버퍼 크기
0, // 플래그
(LPSOCKADDR)lpstToAddr, // 목적지
sizeof(SOCKADDR_IN)); // 주소 길이
if(nRet == SOCKET_ERROR)
ReportError("sendto() 함수 수행중 문제가 발생했습니다.");
return (nRet);
}
// RecvEchoReply() 함수
// 들어오는 자료를 받고 받은 항목들을 분석합니다.
DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char* pTTL)
{
ECHOREPLY echoReply;
int nRet;
int nAddrLen = sizeof(struct sockaddr_in);
// 에코 응답을 받습니다.
// recvform(소켓 식별자, 수신된 데이터를 받을 버퍼, 버퍼의 길이, 역할을 나타내는 플래그, 준 곳의 주소를
// 리턴 받기 위한 버퍼 포인터, 버퍼 사이즈를 지정한 포인터)
// 소켓으로 부터 데이터그램을 받고, 송신자의 주소와 포트를 반환하는 함수 입니다.
// http://white.chungbuk.ac.kr/~jchern/recvfrom.html 참고
nRet = recvfrom(s, // 소켓
(LPSTR)&echoReply, // 버퍼
sizeof(ECHOREPLY), // 버퍼의 크기
0, // 플래그
(LPSOCKADDR)lpsaFrom, // 받는 주소
&nAddrLen); // 주소 길의를 담는 자료를 가리키는 포인터
// 되돌아 오는 값을 검사합니다.
if(nRet == SOCKET_ERROR)
ReportError("recvfrom() 함수 수행중 문제가 발생했습니다.");
// 보낸 시간 값과 IP TTL 값을 받습니다.
*pTTL = echoReply.ipHdr.TTL;
return(echoReply.echoRequest.dwTime);
}
// ReportError() 함수
// 무슨 일 있습니까?
void ReportError(LPCSTR pWhere)
{
fprintf(stderr,"\n%s WSAGetLastError 오류 코드 번호 : %d\n", WSAGetLastError());
}
// WaitforEchoReply() 함수
// select() 함수를 사용하여 언제 자료가 읽혀 질지 결정합니다.
int WaitForEchoReply(SOCKET s)
{
struct timeval Timeout;
fd_set readfds;
readfds.fd_count = 1;
readfds.fd_array[0] = s;
Timeout.tv_sec = 5;
Timeout.tv_usec = 0;
// select(I/O 변화를 감지하는 소켓의 갯수, 읽기 상태 변화 감지할 소켓, 쓰기 상태 변화 감지할 소켓
// 예외 상태 변화 감지할 소켓, 기다리기 위한 시간)
// http://white.chungbuk.ac.kr/~jchern/select.html 참고
return( select(1, &readfds, NULL, NULL, &Timeout) );
}
// 마이크 무유스(Mike Muuss) 의 in_cksum() 함수
// 원래 핑(ping) 코드와 그의 설명입니다.
// 참고 http://myhome.shinbiro.com/~eaglelee/rawsocket.txt
//
//* 저자 -
//* 마이크 무유스(Mike Muuss)
//* 미 육군 탄도 연구소(U. S. Army Ballistic Research Laboratory)
//* 1983년 12월
/*
* I N _ C K S U M
*
*
* 인터넷 프로토콜 계열 헤더를 위한 체크섬 계산 루틴(C 버전)
*
*/
u_short in_cksum(u_short *addr, int len)
{
register int nleft = len;
register u_short *w = addr;
register u_short answer;
register int sum = 0;
/*
* 우리의 원래 알고리듬은 간단합니다. 32 비트 누산기(덧셈)를 써서,
* 우리는 16비트 단어를 순차적으로 더한 후 마지막에,
* 상위 16비트를 뒤로 접어 하위 16비트로 내립니다.
*
*/
while(nleft > 1){
sum += *w++;
nleft -= 2;
}
/* 필요하다면, 홀수 바이트 부분을 정리 합니다. */
if(nleft == 1) {
u_short u = 0;
*(u_char*)(&u) = *(u_char*)w;
sum += u;
}
/*
* 상위 16비트를 하위 16비트로 더합니다.
*/
sum = (sum >> 16) + (sum &0xffff); /* 상위 16비트를 하위 16비트로 더하기 */
sum += (sum >> 16); /* 자리 올림수를 더합니다. */
answer = ~sum; /* 16 비트로 자릅니다. */
return (answer);
}
소개 코드를 머리 속에서 최대한 외울려고 했는데, 서버에서 bind 부분이랑 accept 이 막혔습니다. 기존에 준 ref 코드와 다른 것은 오류를 최대한 막을려고 했기 때문에 ref 기반에서 출발했지만 약간 다릅니다. 하지만 거의 같습니다. 제가 했던 리포트랑 엄청 짧고 간편해졌습니다;
/*
tcp_server_ref.cpp
저작권
http://ccl.korea.ac.kr
http://cakel.tistory.com
교육용을 전제로 자유롭게 사용하실수 있습니다.
5000 번 포트를 쓰는 로컬호스트용 애코 서버 입니다.
되도록이면 머리 속에서 나오는 것을 쳐 낼려고 했습니다.
*/
#include <winsock2.h>
#include <stdio.h>
#include <string.h>
#pragma comment (lib,"ws2_32.lib")
#define PORT 5000
int main()
{
// 윈속 초기화
WSADATA wsd;
if(WSAStartup(MAKEWORD(2,2), &wsd) != 0)
{
printf("winsock 오류\n");
return 1;
}
// 서버 주소, 받을 클라이언트 주소
int s, ns, addrsize;
sockaddr_in client, server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY; // 로컬 서버(localhost)를 나타냄. 혼자 놀이용.
server.sin_port = htons(PORT);
// bind() - 새로 오는 클라이언트를 받을 welcome 소켓
s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(bind(s, (sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
{
printf("bind() 오류\n");
WSACleanup();
return 1;
}
// listen 포트를 열어 두어 떡밥을 기다리는 중
listen(s,2);
printf("클라이언트를 기다리는 중\n");
addrsize = sizeof(client);
char buff[1024] = {0};
// accept 물었다. 소켓을 할당 한다.
ns = accept(s, (sockaddr*)&client, &addrsize);
if(ns == INVALID_SOCKET)
{
printf("accept() 오류\n");
WSACleanup();
return 1;
}
// inet_ntoa 랑 ntohs 를 주의
printf("%s:%d로 접속\n", inet_ntoa(client.sin_addr),ntohs(client.sin_port));
int ret;
// 값을 주고 받는데 메시지 값이 SOCKET_ERROR 이면 가차없이 종료
while(ret != SOCKET_ERROR)
{
ret = recv(ns, buff, 1024, 0);
printf("받은 메시지의 크기 : %d\n", ret);
if(ret == SOCKET_ERROR) break;
ret = send(ns, buff, ret, 0);
printf("보낸 메시지의 내용 : %s\n", buff);
memset(buff, 0, 1024);
}
printf("접속을 종료\n");
closesocket(ns);
closesocket(s);
WSACleanup();
return 0;
}
/*
tcp_client_ref.cpp
저작권
http://ccl.korea.ac.kr
http://cakel.tistory.com
교육용을 전제로 자유롭게 사용하실수 있습니다.
5000 번 포트를 쓰는 로컬호스트용 애코 클라이언트 입니다.
되도록이면 머리 속에서 나오는 것을 쳐 낼려고 했습니다.
*/
#include <winsock2.h>
#include <stdio.h>
#include <string.h>
#pragma comment (lib,"ws2_32.lib")
#define PORT 5000
#define IP "127.0.0.1" // 로컬 접속용 즉 혼자놀이용
int main(void)
{
// 윈속을 설정
WSADATA wsd;
if(WSAStartup(MAKEWORD(2,2), &wsd) != 0)
{
fprintf(stderr,"틀린 윈속 버전\n");
return 1;
}
int s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if ( s == SOCKET_ERROR)
{
fprintf(stderr,"socket() 함수 오류\n");
WSACleanup();
return 1;
}
// 서버 주소 설정
sockaddr_in server;
server.sin_addr.s_addr = inet_addr(IP);
server.sin_port = htons(PORT);
server.sin_family = AF_INET;
// connect() 떡밥을 물으러 간다.
if(connect(s, (sockaddr* )&server, sizeof(server)) == SOCKET_ERROR)
{
printf("connect() 오류\n");
closesocket(s);
WSACleanup();
return 1;
}
// 물었다.
char buff[1024] = {0};
int ret;
// SOCKET_ERROR 되기 전까진 무조건 자료 전송
while(ret != SOCKET_ERROR)
{
printf("출력할 글자 입력 : ");
fgets(buff,1024,stdin);
ret = send(s, buff, strlen(buff),0);
memset(buff,0,1024);
if(ret == 0) break;
ret = recv(s, buff, 1024,0);
printf("받은 문자열 : %s", buff);
}
printf("연결을 종료\n");
closesocket(s);
WSACleanup();
return 0;
}
변경 내용 - 약간의 한글화 / CheckMail.h 헤더 부분 내장 (내용이 작아서 넣었음)
수행 내용 - TCP/IP - POP3 서버에 접속 및 통신하는 내용입니다. - 수행화면
작업 로그
----시작(STARTUP)---- WSAStartup() 함수 성공
---- FINDHOST(호스트 찾기) ---- WSAAsyncGetHostByName() 함수를 써서 서버를 찾습니다. SM_GETHOST 메시지를 받았습니다. WSAAsyncGetHostByName() 함수 수행중 31 밀리초(ms)가 완료 하는데 소요되었습니다. 서버를 찾았습니다.
---- 호스트에 접속합니다.(Connect to host) ----
socket(AF_INET, SOCK_STREAM, 0); 을 호출 합니다. WSAASyncSelete() 함수를 호출 합니다 getservbyname() 함수 수행으로 0밀리초(ms)가 소요 되었습니다. connect() 함수를 호출합니다.
---- 처리 메시지(Process Messages) ---- FD_CONNECT 알림을 받았습니다. FD_WRITE 알림을 받았습니다. FD_READ 알림을 받았습니다. recv() 함수를 호출합니다. 서버로 부터의 응답입니다 : +OK NMQpopper NaverMail Team 2004 base Qpopper (version Naver1.0) at i4l043.nhncorp.com starting.
AppState = CONNECTING, senging USER FD_READ 알림을 받았습니다. recv() 함수를 호출합니다. 서버로 부터의 응답입니다 : +OK Password required for cakel.
AppState = SEND, 패스워드를 전송합니다. FD_READ 알림을 받았습니다. recv() 함수를 호출합니다. 서버로 부터의 응답입니다 : +OK cakel has 16 visible messages in 2312723 octets.
AppState = PASS, STAT 를 보냅니다. FD_READ 알림을 받았습니다. recv() 함수를 호출합니다. 서버로 부터의 응답입니다 : +OK 16 2312723
AppState = STAT, 응답을 읽습니다. ---- 결과(Result)---- 16 메시지 2312723 바이트 QUIT 명령를 전송합니다. FD_READ 알림을 받았습니다. recv() 함수를 호출합니다. 서버로 부터의 응답입니다 : +OK Pop server at i4l043.nhncorp.com signing off.
호스트 QUIT OK 소켓을 닫습니다. FD_CLOSE 통보를 받았습니다.
수행 방법 - 바로 수행되게 Link 부분을 수정했습니다. #pragma comment (lib,"wsock32.lib") // wsock32.lib 를 링크하기 #pragma comment( linker, "/subsystem:windows" ) // WinMain 을 불러 쓰기. int main 을 쓰지 않습니다.
- Resouce 부분은 직접 만들어서 추가했습니다. 처음이라 그런지 X 버튼으로 종료가 되지 않습니다. - 프로젝트에 Resource 을 추가하시고 같이 생성되는 recource.h 파일을 include 하시면 됩니다. - 워크스페이스(workspace)에 넣어 두시면 많이 편합니다(엉뚱하게 있어도 오류가 나는 경우가 있음) 추가하는 법은 아래 파일 트리에 Add Files in Project 를 누르시던지 Project 메뉴에 Add to Project -> Files 를 누르시면 됩니다.
추가된 리소스 트리와 파일입니다. MFC 를 쓰지 않고, WinAPI 를 썼습니다.
리소스 구조 입니다. 단순합니다;
- password 부분은 보이진 않지만 암호화 되어 전송되지 않기 때문에 외부 유출이 가능하므로 조심하시기 바랍니다. - POP3 서버를 사용할수 있는 계정으로 접근 합니다. - 만들어진 코드에서 문제가 시스템 명령 상으로 닫혀지지 않습니다. 아시는 분 댓글 부탁 드립니다. 해킹은 아닌데; 이상하게 X 버튼이 먹히지 않습니다. 그냥 닫기 컨트롤을 누르면 닫힙니다^^;
코드 보기(좀 깁니다.)
/*
저작권 공지
http://www.sockaddr.com/ExampleSourceCode.html
http://cakel.tistory.com
교육용을 전제로 자유롭게 배포 가능합니다.
winapi_checkmail
간단하게 POP3 를 이용한 통신을 통해 전자우편 서버와 통신을 하는 내용입니다.
Windows API 를 쓰기 때문에 윈도우 상에서 교신 내용이 나옵니다.
아래 5개 파일로 구성되어 있습니다.
resource.h - GUI 구성과 C++ 코드와 연동을 위한 헤더 파일
checkpop3.rc - GUI 실제 구성하는 틀(Frame)
winapi_checkmail.dsw - MSDEV : Visual Studio 6용 작업 공간
winapi_checkmail.dsp - MSDEV : 작업용 Project 설정 File
checkpop3.cpp - 구동을 맏는 핵심 C++
*/
/*
CheckMail.h Header 부분입니다.
내용이 작아서 checkpop3.cpp 에 내장(embedded) 시켰습니다.
전자우편을 확인하기 위해 POP3 서버를 기다립니다.
*/
// 16-bit 윈도우즈 에서 MAKEWORD 를 정의 합니다.
// MAKEWORD (a, b) - 상위 8비트(BYTE) a OR 하위 8비트(BYTE) b = 16비트(WORD 형)로 받음
#include <windows.h>
#include <string.h>
#include <stdio.h>
#include <winsock.h>
#include "resource.h"
#ifndef WIN32
#define MAKEWORD(a, b) ( (WORD) ( ((BYTE)(A)) | ( (WORD) ( (BYTE)(b)) ) << 8) )
#endif
// Winsock 을 Link 시키고 윈도우즈용 시스템이라는 것을 명시 합니다.
// 안 쓰면 컴파일러시 옵션을 주시 않으면 WinMain 링크 오류가 생깁니다.
// 원인 모르는 Link 오류해결시 참고 하시기 바랍니다.
#pragma comment (lib,"wsock32.lib")
#pragma comment( linker, "/subsystem:windows" )
// WINAPI 관련 함수 정의는 아직 모르겠습니다.
// 도우미 매크로
#define Display(s) SendDlgItemMessage(hwndDlg, IDC_EDIT1, EM_REPLACESEL, 0, (LPARAM) ((LPSTR)s) )
#define EnableOneButton(id, flag) EnableWindow( GetDlgItem(hwndDlg, id), flag)
#define EnableButtons(flag) EnableOneButton(IDC_CHECKMAIL, flag)
// 비동기 통지(Asynchronous notificatio)를 위한 메시지 정의
#define SM_GETHOST WM_USER+1
#define SM_ASYNC WM_USER+2
// 함수 프로토타입(Prototypes)
BOOL CALLBACK MainDialogProc(HWND, UINT, WPARAM, LPARAM);
void HandleGetHostMsg(HWND, WPARAM, LPARAM);
void HandleAsyncMsg(HWND, WPARAM, LPARAM);
void ProcessData(HWND, LPSTR, int);
void ProcessData(HWND, LPSTR, int);
void ProcessLine(HWND, LPSTR);
void CloseSocket(SOCKET);
// 전역(Global) 변수 부분 입니다.
/////////////////////////////////
// POP3 호스트 이름
char gszServerName[255];
// 사용자 ID
char gszUserID[80];
// 비밀번호
char gszPassword[80];
// WSAAsyncHostByName() 함수를 위한 핸들(Handle) - 자원을 접근(Access) 할수 있는 포인터(Pointer)
HANDLE hndGetHost;
// WSAAsyncGetHostByName() 함수를 위한 HostEnt 버퍼(Buffer)
char bufHostEnt[MAXGETHOSTSTRUCT];
// 소켓
SOCKET socketPOP;
// wsprintf() 함수를 위한 스크래치 버퍼(Scratch buffer - 임시 저장 공간)
char gszTemp[255];
// 타이밍을 위한 변수들
// 이 예제에세만(For example only) 쓰입니다 -- 실제로는 필요하지 않습니다.
DWORD gdwTicks;
DWORD gdwElapsed;
// recv() 데이터 버퍼 입니다.
char gbufRecv[256];
// 응용프로그램 작동 상태(Application State)를 정의 합니다.
int gnAppState;
#define STATE_CONNECTING 1 // 접속중
#define STATE_USER 2 // 사용자
#define STATE_PASS 3 // 비밀번호
#define STATE_STAT 4 // 상태
#define STATE_QUIT 5 // 나가기
// WinMain() - 접근 점(Entry Point)
int PASCAL WinMain
(
HINSTANCE hinstCurrent,
HINSTANCE hinstPrevious,
LPSTR lpszCmdLine,
int nCmdShow)
{
// 변수를 초기화 합니다.
int nReturnCode;
WSADATA wsaData;
// 필요한 Winsock 의 Version 은 1.1
#define MAJOR_VERSION_REQUIRED 1
#define MINOR_VERSION_REQUIRED 1
WORD wVersionRequired = MAKEWORD(MAJOR_VERSION_REQUIRED, MINOR_VERSION_REQUIRED);
// WinSock DLL 을 시작합니다.
nReturnCode = WSAStartup(wVersionRequired, &wsaData);
if (nReturnCode !=0)
{
MessageBox(NULL, "WSAStartUp()함수를 수행하는데 문제가 생겼습니다", "CheckMail - winapi_checkmail", MB_OK);
return 1;
}
// 원하는 Version 의 Winsock 이 없다고 표시합니다.
if (wsaData.wVersion != wVersionRequired)
{
// 필요한 Version 이 없습니다.
MessageBox(NULL, "틀린 WinSock Version 입니다", "Checkmail - winapi_checkmail", MB_OK);
WSACleanup(); // 생성된 WinSock Application 을 지웁니다.
return 1;
}
// 기본 윈도우로 쓰인 대화 상자를 염니다.
DialogBox(
hinstCurrent,
MAKEINTRESOURCE(IDD_DIALOG_MAIN),
NULL,
MainDialogProc);
WSACleanup();
return 0;
}
// MainDialogProc() - 기본 윈도우 프로시저(Main Window Procedure) 입니다.
BOOL CALLBACK MainDialogProc(
HWND hwndDlg,
UINT msg,
WPARAM wParam,
LPARAM lParam
)
{
BOOL fRet = FALSE;
switch(msg)
{
case WM_INITDIALOG:
// WSADATA 에서 오는 정보를 표시 합니다.
Display("----시작(STARTUP)----\r\n");
Display("WSAStartup() 함수 성공\r\n\r\n");
break;
case WM_COMMAND:
switch(wParam)
{
// 메일 확인 버튼을 눌렀을때
case IDC_CHECKMAIL:
// POP 정보를 받습니다.
// 서버 이름, 사용자 ID 와 비밀번호
if(!GetDlgItemText(
hwndDlg,
IDC_SERVERNAME,
gszServerName,
sizeof(gszServerName)
))
{
MessageBox(hwndDlg,
"서버 이름을 넣어 주세요",
"POP 알림", MB_OK);
break;
}
if (!GetDlgItemText(hwndDlg, IDC_USERID,
gszUserID, sizeof(gszUserID)) )
{
MessageBox(hwndDlg,
"사용자 ID를 넣어 주세요",
"POP 알림", MB_OK);
break;
}
if (!GetDlgItemText(
hwndDlg,
IDC_PASSWORD,
gszPassword,
sizeof(gszPassword)
))
{
MessageBox(hwndDlg,
"비밀번호를 넣어 주세요",
"POP 알림", MB_OK);
break;
}
// 비동기 Version 의 gethostbyname() 함수로
// 호스트 이름을 확인하는 요청을 합니다.
// 작업이 완료 되면 SM_GETHOST 메시지가 표시 됩니다.
Display("---- FINDHOST(호스트 찾기) ---- \r\n");
Display("WSAAsyncGetHostByName() 함수를 써서 " "서버를 찾습니다. \r\n");
// 이번 예제를 위해 WSAAsyncGetHostByName() 수행 시간을 잽니다.
gdwTicks = GetTickCount();
hndGetHost = WSAAsyncGetHostByName(
hwndDlg, SM_GETHOST,
gszServerName,
bufHostEnt,
MAXGETHOSTSTRUCT);
if(hndGetHost == 0)
{
MessageBox(hwndDlg,
"시작하는데 오류가 생겼습니다"
"WSAAsyncGetHostByName() 함수 수행",
"CheckMail - winapi_checkmail",
MB_OK);
}
else
{
EnableButtons(FALSE);
gnAppState = 0;
}
fRet = TRUE;
break;
// 취소 퍼든을 누르면 기본 윈도우(Main window)에서 나가게 합니다.
case IDC_CANCEL:
if(gnAppState)
CloseSocket(socketPOP);
PostQuitMessage(0);
fRet = TRUE;
break;
}
break;
// 비동기 gethostbyname() 함수의 되돌림(return) 값을 다룹니다.
case SM_GETHOST:
HandleGetHostMsg(hwndDlg, wParam, lParam);
fRet = TRUE;
break;
// 비동기 메시지를 처리 합니다.
case SM_ASYNC:
HandleAsyncMsg(hwndDlg, wParam, lParam);
fRet = TRUE;
break;
}
return fRet;
}
// HandleGetHost() 함수 정의
// WSAASyncGetHostByName() 함수가 완료 되었을때 불리어 집니다.
void HandleGetHostMsg(
HWND hwndDlg,
WPARAM wParam,
LPARAM lParam)
{
SOCKADDR_IN saServ; // 인터넷을 위한 소켓 주소입니다.
LPHOSTENT lpHostEnt; // 호스트 진입점(host entry) 을 가리키는 포인터 입니다.
LPSERVENT lpServEnt; // 서버 진입점(sever entry) 을 가리키는 포인터 입니다.
int nRet; // 되돌림 값(return code)입니다.
Display("SM_GETHOST 메시지를 받았습니다.\r\n");
if( (HANDLE)wParam != hndGetHost)
return;
// 이번 예제에서 WSAGetHostByName() 함수의
// 수행 중인 시간을 표시 합니다.
gdwElapsed = ( GetTickCount() - gdwTicks );
wsprintf( (LPSTR)gszTemp, (LPSTR) "WSAAsyncGetHostByName() 함수 수행중 %ld"
" 밀리초(ms)가 완료 하는데 소요되었습니다.\r\n",
gdwElapsed);
Display(gszTemp);
// 오류 코드를 확인합니다.
nRet = WSAGETASYNCERROR(lParam);
if (nRet)
{
wsprintf( (LPSTR)gszTemp,
(LPSTR) "WSAAsyncGetHostByName() 함수 수행중 오류가 생겼습니다. : %d\r\n",
nRet);
Display(gszTemp);
EnableButtons(FALSE);
return;
}
// 서버를 찾았습니다(Server found OK).
// bufHostEnt 에 서버의 정보를 담고 있습니다.
Display("서버를 찾았습니다.\r\n\r\n");
Display("---- 호스트에 접속합니다.(Connect to host) ----\r\n\r\n");
// 소켓(Socket)을 생성합니다.
Display("socket(AF_INET, SOCK_STREAM, 0); 을 호출 합니다.\r\n");
// socket(인터넷 주소, TCP 소켓 스트림, 플래그)
socketPOP = socket(AF_INET, SOCK_STREAM, 0);
if (socketPOP == INVALID_SOCKET)
{
Display("소켓을 생성하지 못했습니다.\r\n");
EnableButtons(TRUE);
return;
}
// 비동기 통지(asynchronous notificatio)를 위해 소켓을
// non-blocking 으로 만들고 등록 합니다.
Display("WSAASyncSelete() 함수를 호출 합니다\r\n");
if(WSAAsyncSelect(socketPOP,
hwndDlg,
SM_ASYNC,
FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE))
{
Display("WSAAsyncSelect() 함수 부르는 중에 문제가 생겼습니다.\r\n");
EnableButtons(TRUE);
return;
}
// getservbyname() 함수로 포트 번호를 찾습니다.
// 간단한 동기화 버전(synchronous version)에서 사용 되었기 때문에
// 거의 항상 빨리 이루어 집니다.
// 이 예제를 위해 getgervbyname() 함수의 수행 시간을 확인합니다.
gdwTicks = GetTickCount();
// getservbyname(서비스,프토토콜)
lpServEnt = getservbyname("pop3", "tcp");
gdwElapsed = (GetTickCount() - gdwTicks);
wsprintf( (LPSTR)gszTemp, (LPSTR) "getservbyname() 함수 수행으로 %ld"
"밀리초(ms)가 소요 되었습니다.\r\n", gdwElapsed);
Display(gszTemp);
// 서비스 정보로 제공해주는 포트 번호 정보를 찾지 못했다면
if (lpServEnt == NULL)
{
// 잘 알려진 포트(well-known)로 채웁니다.
saServ.sin_port = htons(110);
Display("getservbyent() 함수가 성공하지 못해서 110번 포트를 사용합니다.\r\n");
}
else
{
// 수행도우미(서번트 - servent)에서 받은 되돌림(return)값
saServ.sin_port = lpServEnt->s_port;
}
// 서버 주소 구조체를 완성합니다.
saServ.sin_family = AF_INET; // 인터넷 주소 계열
lpHostEnt = (LPHOSTENT)bufHostEnt;
saServ.sin_addr = *( (LPIN_ADDR)*lpHostEnt->h_addr_list);
// 소켓에 접속합니다.
Display("connect() 함수를 호출합니다.\r\n");
// connect(접속하여 연결될 소켓, 접속할 서버주소, 주소의 길이
nRet = connect(socketPOP, (LPSOCKADDR)&saServ, sizeof(SOCKADDR_IN) );
if(nRet == SOCKET_ERROR)
{
if(WSAGetLastError() != WSAEWOULDBLOCK)
{
Display("접속 중에 오류가 발생했습니다.\r\n");
EnableButtons(TRUE);
return;
}
}
gnAppState = STATE_CONNECTING;
Display("\r\n ---- 처리 메시지(Process Messages) ---- \r\n");
}
void HandleAsyncMsg(
HWND hwndDlg,
WPARAM wParam,
LPARAM lParam)
{
int nBytesRead;
switch(WSAGETSELECTEVENT(lParam))
{
case FD_CONNECT:
Display("FD_CONNECT 알림을 받았습니다.\r\n");
break;
case FD_WRITE:
Display("FD_WRITE 알림을 받았습니다.\r\n");
break;
case FD_READ:
Display("FD_READ 알림을 받았습니다.\r\n");
Display("recv() 함수를 호출합니다.\r\n");
// 대기 자료를 받습니다.
nBytesRead = recv(
socketPOP, // 소켓
gbufRecv, // 버퍼
sizeof(gbufRecv), // 최대로 받을 길이
0); // recv 함수의 플래그 값
// recv() 함수의 되돌림 값을 받습니다.
if(nBytesRead == 0)
{
// 연결이 끝어 졌습니다.
MessageBox(
hwndDlg,
"연결에 예상치 못하게 끊어 졌습니다.",
"recv() 함수 오류", MB_OK);
break;
}
if(nBytesRead == SOCKET_ERROR)
{
wsprintf( (LPSTR)gszTemp,
"recv() 오류 : %d", nBytesRead);
MessageBox(
hwndDlg,
gszTemp,
"recv() 함후 오류",
MB_OK);
break;
}
// 버퍼에 종단 문자(terminate)를 넣습니다.
gbufRecv[nBytesRead] ='\0';
// 해석 하기 위해서 넘겨 줍니다.
ProcessData(hwndDlg, gbufRecv, nBytesRead);
break;
case FD_CLOSE:
Display("FD_CLOSE 통보를 받았습니다.\r\n");
EnableButtons(TRUE);
break;
}
}
// ProcessData() 함수
// 들어오는 문서를 받아서 줄을 끊어서 저장합니다.
void ProcessData(HWND hwndDlg, LPSTR lpBuf, int nBytesRead)
{
static char szResponse[512];
static int nLen = 0;
char *cp;
// 새 자료를 저장할 공간이 있을까요?
if ( (nLen + nBytesRead) > sizeof(szResponse) )
{
// 아니요~ , 공간이 없습니다.
Display("!!!! 버퍼가 가득 찼습니다. 자료가 잘려(절삭)집니다.\r\n");
nLen = 0;
szResponse[0] = '\0';
return;
}
// 공간 있어요~
// 버퍼에 새 줄을 추가 합니다.
strcat(szResponse, lpBuf);
nLen = strlen(szResponse);
// 저체 줄을 작업을 합니다.
while(1)
{
// 버퍼에 새줄 값을 담고 있나요?
cp = strchr(szResponse, '\n');
if(cp == NULL)
// 못 찾았습니다.
break;
// CR/LF (Carrage Return / Line Feed - 행갈이/줄끝) 쌍을 가지고 있습니다.
// LF 을 NULL 값을 바꿉니다.
*cp = '\0';
// PrcessLine() 함수에 넣습니다.
ProcessLine(hwndDlg, szResponse);
// 버퍼에 남겨진 데이터를 앞으로 당겨 옵니다.
cp++;
if(strlen(cp))
memmove(szResponse, cp, strlen(cp)+1);
else
szResponse[0] = '\0';
}
}
// ProcessLine()
// 서버(Server)에게 명령줄에 대한 응답을 받고 다음에 뭘 할지 결정합니다.
void ProcessLine(HWND hwndDlg, LPSTR lpStr)
{
int nRet;
long lCount;
long lSize;
Display("서버로 부터의 응답입니다 : \r\n");
Display(lpStr);
Display("\r\n");
// 오류에 대한 응답인지 확인합니다.
if(lpStr[0] == '-')
{
Display("부정적인 응답입니다.(Negative response) :");
switch(gnAppState)
{
case STATE_CONNECTING:
Display("접속이 거부 당했습니다.\r\n");
break;
case STATE_USER:
Display("알수 없는 사용자ID입니다.\r\n");
break;
case STATE_PASS:
Display("틀린 비밀번호입니다.\r\n");
break;
case STATE_STAT:
Display("STAT 명령이 지원하지 않습니다.\r\n");
break;
case STATE_QUIT:
Display("QUIT 명령이 지원하지 않습니다.\r\n");
break;
}
Display("Sending QUIT\r\n");
wsprintf(gszTemp, "QUIT\r\n");
Display(gszTemp);
nRet = send(
socketPOP, // 소켓
gszTemp, // 데이터 버퍼
strlen(gszTemp), // 데이터의 길이
0); // 플래그
gnAppState = STATE_QUIT;
return;
}
// 긍정적인 응답을 받았습니다.
switch(gnAppState)
{
case STATE_CONNECTING:
// 로그인(LOGING) 부분에서 사용자(USER) 정보 부분을 전송하고
// 프로그램 상태를 수정합니다.
Display("AppState = CONNECTING, "
"senging USER\r\n");
wsprintf(gszTemp, "USER %s\r\n", gszUserID);
nRet = send(
socketPOP, // 소켓
gszTemp, // 데이터 버퍼
strlen(gszTemp), // 데이터의 길이
0);
gnAppState = STATE_USER;
break;
case STATE_USER:
// 로그인 부분에서 패스워드(PASSWORD) 부분을 전송하고
// 프로그램 상태를 수정합니다.
Display("AppState = SEND, 패스워드를 전송합니다.\r\n");
wsprintf(gszTemp, "PASS %s\r\n", gszPassword);
nRet = send(
socketPOP, // 소켓
gszTemp, // 데이터 버퍼
strlen(gszTemp), // 데이터의 길이
0);
gnAppState = STATE_PASS;
break;
case STATE_PASS:
// STAT 명령어를 보내고,
// 프로그램 상태를 수정합니다.
Display("AppState = PASS, STAT 를 보냅니다.\r\n");
wsprintf(gszTemp, "STAT\r\n");
nRet = send(
socketPOP, // 소켓
gszTemp, // 데이터 버퍼
strlen(gszTemp), // 데이터의 길이
0);
gnAppState = STATE_STAT;
break;
case STATE_STAT:
// STAT 응답을 읽습니다.
// 결과를 출력합니다.
Display("AppState = STAT, 응답을 읽습니다.\r\n");
sscanf(lpStr, "%s %ld %ld", gszTemp, &lCount, &lSize);
Display("---- 결과(Result)----\r\n");
wsprintf(gszTemp, "%ld 메시지 %ld 바이트\r\n", lCount, lSize);
Display(gszTemp);
// QUIT 명령을 전송합니다.
// 프로그램의 상태를 수정합니다.
Display("QUIT 명령를 전송합니다.\r\n");
wsprintf(gszTemp, "QUIT\r\n");
nRet = send(
socketPOP, // 소켓
gszTemp, // 데이터 버퍼
strlen(gszTemp), // 데이터의 길이
0);
gnAppState = STATE_QUIT;
break;
case STATE_QUIT:
Display("호스트 QUIT OK\r\n");
Display("소켓을 닫습니다.\r\n");
CloseSocket(socketPOP);
}
}
// CloseSocket()
// 프로토콜 스택에 있는 버퍼를 비우고 소켓을 닫습니다.
void CloseSocket(SOCKET sock)
{
int nRet;
char szBuf[255];
// 원격지에게 우리는 자료를 보내지 않을 거라는 정보를 전송합니다.
shutdown(sock, 1);
while(1)
{
// 자료 받기를 시도 합니다.
// 이는 아직 프로토콜에 남아 있을지 모르는 자료를 지우기 위해서 입니다.
nRet = recv(
sock, // 소켓
szBuf, // 데이터 버퍼
sizeof(szBuf), // 버퍼의 길이
0); // revc 플래그
// 연결이 끝났거나 어떤 다른 문제라도 생기면 받는걸 중단합니다.
if (nRet == 0 || nRet == SOCKET_ERROR)
break;
}
// 상대편에게 우리는 자료를 더 받지도 않을 거라고 알리니다.
shutdown(sock,2);
// 소켓을 닫습니다.
closesocket(sock);
}
if (theSocket == INVALID_SOCKET) { PRINTERROR("socket()"); return;
}
// 주소 구조체를 채웁니다. SOCKADDR_IN saServer;
saServer.sin_family = AF_INET; saServer.sin_addr = *((LPIN_ADDR)*lpHostEntry->h_addr_list); // 서버 주소 saServer.sin_port = htons(nPort); // 명령줄에서 포트 번호를 받습니다.
// 서버로 접속합니다
int nRet;
// connect(소켓, 서버 주소, 서버 주소의 길이 nRet = connect(theSocket, (LPSOCKADDR)&saServer, sizeof(struct sockaddr) ); if(nRet == SOCKET_ERROR) { PRINTERROR("socket()"); closesocket(theSocket); return;
}
// 서버로 자료를 보냅니다.
char szBuf[256]; strcpy(szBuf, "클라이언트에서 보내는 TCP 값입니다");
// Windows 에서는 Unix 계열과 다르게 보낼때 write 대신에 send 를 씁니다. // send(연결된 소켓, 보낼 자료 버퍼, 자료의 길이, 상태값 nRet = send(theSocket, szBuf, strlen(szBuf), 0);
if (nRet == SOCKET_ERROR) { PRINTERROR("send()"); closesocket(theSocket); return;
}
// 응답을 기다립니다 // Windows 에서는 Unix 계열과 다르레 받을때는 read 대신에 recv 를 씁니다. // recv(연결된 소켓, 받은 자료를 저장할 버퍼, 버퍼의 길이, 상태값) nRet = recv(theSocket, szBuf, sizeof(szBuf), 0); if (nRet == SOCKET_ERROR) { PRINTERROR("recv()"); closesocket(theSocket); return; }
// 받은 값을 표시합니다. printf("\n값을 받았습니다 : %s", szBuf);
closesocket(theSocket); return;
}
- 서버(winsock_3_2)
/* 저작권 표기(Copyrights) http://www.sockaddr.com/ExampleSourceCode.html http://cakel.tistory.com
교육용을 목적으로 자유롭게 쓰실수 있습니다.
아주 간단한 스트림 서버 예제 입니다. udp_client (Client.cpp) 와 작동 합니다.
이 프로그램은 TCP 프로토콜을 이용해서 서버를 만드는 일을 합니다. 클라이언트에서 자료를 기다리고 들어온 값을 표기하며 클라이언트에게 값을 되돌리고 종료를 합니다.
wsock32.lib 를 링크 시키기고 컴파일 하세요
bind() 함수를 사용하기 위해 명령줄에 포트 번호를 넣어 주세요. 이미 쓰고 있지 않는다면 어떤 포트 번호도 쓸수 있습니다.
예 : udp_server 2000
*/
#include <stdio.h> #include <winsock.h>
// 프로토타입 함수 void StreamServer(short nPort);
// 오류를 표기하는 매크로 도우미 #define PRINTERROR(s) fprintf( stderr, "%d\n", s, WSAGetLastError() )
//
int main(int argc, char* *argv) { WORD wVersionRequested = MAKEWORD(1, 1); WSADATA wsaData; int nRet; short nPort;
// 포트 번호를 확인 합니다. if (argc !=2) { fprintf(stderr, "\n사용법 : udp_server [포트 번호]\n"); return 1;
}
nPort = atoi(argv[1]);
// 윈속을 시작하고 버전을 확인합니다 nRet = WSAStartup(wVersionRequested, &wsaData); if(wsaData.wVersion != wVersionRequested) { fprintf(stderr, "\n 윈속 버전이 틀렸습니다.\n"); return 1;
// 프토타입 함수 선언 void DatagramClient(char* szServer, short nPort);
// 오류를 표시하기 위한 매크로 도우미 정의 #define PRINTERROR(s) fprintf(stderr, "\n%: &d\n", s, WSAGetLastError())
//
int main(int argc, char* *argv) { WORD wVersionRequested = MAKEWORD(1,1); // 윈속 버전 1.1을 요구 참고 : http://blog.naver.com/dreamset?Redirect=Log&logNo=60010620996 WSADATA wsaData; int nRet; short nPort; // 호스트와 포트 인수를 검사합니다.
saServer.sin_family = AF_INET; saServer.sin_addr = *( (LPIN_ADDR)*lpHostEntry->h_addr_list); // 서버 주소 saServer.sin_port = htons(nPort); // 명령줄에서 포트 번호를 받은 것입니다.
// 서버이름과 포트 번호를 표기 합니다. printf("\n서버 이름이 %s 이(가) 포트번호 %d\n 에서 요청을 기다립니다.", szBuf, nPort);
// 클라이언트에서 오는 자료를 기다립니다. SOCKADDR_IN saClient;
memset(szBuf, 0, sizeof(szBuf));
// recvfrom(응답할 소켓, 받은 버퍼, 버퍼의 바이트수, 플래그 - 상태값, // 클라이언트 주소에서 받는 버퍼, 클라이언트 주소 버퍼의 길이 nRet = recvfrom(theSocket, szBuf, sizeof(szBuf), 0, (LPSOCKADDR)&saClient, &nLen);
// 이제 어떤 자료를 받았습니다. printf("값을 받았습니다 : %s", szBuf);
// 클라이언트로 값을 돌려 줍니다. strcpy(szBuf, "서버에서"); // sendto(보낼 소켓, 보낼 버퍼, 버퍼의 바이트수, 플래그 - 상태값, // 자료를 보낼 주소, 주소의 길이 sendto(theSocket, szBuf, strlen(szBuf), 0, (LPSOCKADDR)&saClient, nLen);
/* 일반적으로 서버는 계속 작동을 해야지 다른 클라이언트들의 요청을 받습니다. 이 예제에서 하나의 요청(transaction, 트랜잭션, 거래)이 완료 되자 마자 종료 합니다.
printf("\nHighVersion - 상위버전.....: %d.%d", LOBYTE(pWSAData->wHighVersion), HIBYTE(pWSAData->wHighVersion) ); printf("\nDescription - 설명.........: %s", pWSAData->szDescription); printf("\nSystem Status - 시스템상태.: %s", pWSAData->szSystemStatus); printf("\nMax number of socket - 최대 소켓수 : %d", pWSAData->iMaxSockets); printf("\nMAX UDP datagram size - UDP 최대 데이터그램 크기: %d\n", pWSAData->iMaxUdpDg);
}
/*
// 저작권 // http://www.sockaddr.com/ExampleSourceCode.html // http://cakel.tistory.com // 교육용을 목적으로 자유롭게 배포 가능합니다. // WSEDesc.c -- 윈속 오류 설명
*/
#include <winsock.h>
typedef struct tagWSAEDESCRIPTION { int iErrorCode; char* szDescription;
}WSAEDESCRIPTION;
WSAEDESCRIPTION ErrorDescriptions[] = { WSAEINTR, "WSAEINTR : Interrupted function call, 함수 호출이 인터럽트(강제수행 되었습니다", WSAEBADF, "WSAEBADF : Bad file number, 잘못된 파일 번호 입니다.", WSAEACCES, "WSAEACCES : Permission denied, 허용되지 않았습니다.", WSAEFAULT, "WSAEFAULT : Address fault, 부르지 못했습니다.", WSAEINVAL, "WSAINVAL : Invaild argument, 값이 틀렸습니다.", WSAEMFILE, "WSAEMFILE : Too many open files, 파일이 너무 많이 열렀습니다.", WSAEWOULDBLOCK, "WSAEWOULDBLOCK : Resource temporarily unavailable (Would block), 자원이 없습니다.(아마 막혔을 겁니다.)", WSAEINPROGRESS, "WASEINPROGRESS : Operation now in progress, 작업중입니다.", WSAEALREADY, "WSAEREADY : Operation already in progress, 이미 수행중입니다.", WSAENOTSOCK, "WSANOTSOCK : Socket operation operation on non-socket, 소켓이 아닌 곳에서 소켓 작업중입니다", WSAEDESTADDRREQ, "WSAEDESTADDRREQ : Destination address required, 목적지 주소가 필요 합니다.", WSAEMSGSIZE, "WSAEMSGSIZE : Message too long, 메시지가 너무 깁니다.", WSAEPROTOTYPE, "WSAEPROTOTYPE : Protocol wrong type for socket, 소켓에 맞지 않은 프로토콜 입니다.", WSAENOPROTOOPT, "WSAENOPROTOOPT : Bad protocol option, 잘못된 프로토콜 옵션입니다.", WSAEPROTONOSUPPORT, "WSAEPROTONOSUPPORT : Protocol not supported, 지원하지 않는 프로토콜입니다.", WSAESOCKTNOSUPPORT, "WSAESOCKTNOSUPPORT : Socket type not supported, 지원하지 않는 소켓 형식입니다.", WSAEOPNOTSUPP, "WSAEOPNOSUPP : Operation not supported, 지원하지 않는 명령입니다.", WSAEPFNOSUPPORT, "WSAEPFNOSUPP : Protocol family not supported, 지원하지 않는 프로토콜 계열입니다.", WSAEAFNOSUPPORT, "WSAEAFNOSUPPORT : Address family not supported by protocol family, 프로토콜 계열에서 지원하지 않는 주소입니다.", WSAEADDRINUSE, "WAEADDRINUSE : Address already in use, 이미 사용 중인 주소 입니다.", WSAEADDRNOTAVAIL, "WSAEADDRNOTAVAIL : Cannot assign requested address, 요구한 주소를 할당할수 없습니다.", WSAENETDOWN, "WSAENETDOWN : Network is down, 네트워크가 작동하지 않습니다.", WSAENETUNREACH, "WSAENETUNREACH : Network is unreachable, 네트워크로 접근할수 없습니다.", WSAENETRESET, "WSAENETRESET : Network dropped connetion on reset, 네트워크가 초기화 되어 꾾어졌습니다.", WSAECONNABORTED, "WSAECONNABORTED : Software caused connection abort, 소프트웨어에 의해 연결을 중단되었습니다.", WSAECONNRESET, "WSAECONNRESET : Connection reset by peer, 상대방에 의해 연결이 초기화 되었습니다.", WSAENOBUFS, "WSAENOBUFS : No buffer space available, 버퍼 공간이 부족합니다", WSAEISCONN, "WSAEISCONN : Socket is already connected, 소켓이 이미 연결되었습니다.", WSAESHUTDOWN, "WSAESHUTDOWN : Cannot send after socket shutdown, 소켓이 닫힌 후에는 보낼 수가 없습니다.", WSAETOOMANYREFS, "WSAETOOMANYREFS : Too many references, 너무 많은 참조를 했습니다.", WSAETIMEDOUT, "WSAETIMEDOUT : Connection timed out, 연결 시간 초과 했습니다.", WSAELOOP, "WSAELOOP : Loop, 반복", WSAENAMETOOLONG, "WSAENAMETOOLONG : Name too long, 이름이 너무 깁니다.", WSAEHOSTDOWN, "WSAEHOSTDOWN : Host is down, 호스트가 꺼졌습니다.", WSAEHOSTUNREACH, "WSAEHOSTUNREACH : No route to host, 호스트로 접근할수가 없습니다.", WSAENOTEMPTY, "WSAENOTEMPTY : Directory not empty, 디렉토리가 안 비었습니다.", WSAEPROCLIM, "WSAEPROCLIM : Too many processes, 프로세스가 너무 많습니다.", WSAEUSERS, "WSAEUSERS : Too many users, 사용자가 너무 많습니다.", WSAEDQUOT, "WSAEDQUOT : Disk quota, 디스크 허용량 초과", WSAESTALE, "WSAESTALE : Stale, 오래됨", WSAEREMOTE, "WSAEREMOTE : Remote, 원격", WSASYSNOTREADY, "WSAESYSNOTREADY : Network subsystem is unavailable", WSAVERNOTSUPPORTED, "WSAEVERNOTSUPPROTED : WINSOCK.DLL version out of range, 윈속 버전이 맞지 않습니다.", WSANOTINITIALISED, "WSANOTINITIALISED : Successful WSAStartup() not yet performed, 성공적인 WSAStartup() 함수가 아직 수행되지 않았습니다.", WSAEDISCON, "WSAEDISCON : Graceful shutdown in progress, 우아한(?) 종료가 진행 중입니다.", WSAHOST_NOT_FOUND, "WSAHOST_NOT_FOUND : Host not found, 호스트를 발견하지 못했습니다.", WSATRY_AGAIN, "WSATRY_AGAIN : Non-authoritative host not found, 비인증 호스트를 발견하지 못했습니다.", WSANO_RECOVERY, "WSANO_RECOVERY : Non-recoverable error, 회복할수 없는 오류", WSANO_DATA, "WSANO_DATA : Vaild name, no data record of requested type, 유효한 이름이였으나 원하는 자료형을 받지 못했습니다.", #ifdef _WINSOCK2API_ // 윈속 2.0 이상에서 지원하는거 같습니다. WSAENOMORE, "WSAENOMORE : ", WSAECANCELLED, "WSAECANCELLED : Cancelled, 취소됨", WSAEINVAILDPROCTABLE, "WSAEINVAILDPROCTABLE : Invaild procedure table form service provider, 잘못된 프로지져 테이블 폼 서비스 제공자 입니다." WSAEPROVIDERFAILEDINIT, "WSAEPROVIDERFAILEDINIT : Unable to initialize a service provider, 서비스 제공자를 시작할수 없습니다.", WSASYSCALLFAILURE, "WSASYSCALLFAILURE, : System call Failure, 시스템 호출 실패", WSASERVICE_NOT_FOUND, "WSASERVICE_NOT_FOUND : Service not found, 서비스를 찾을수 없습니다.", WSATYPE_NOT_FOUND, "WSATYPE_NOT_FOUND : Type not found", 형식을 찾을수 없습니다.", WSA_E_NO_MORE, "WSA_E_NO_MORE : ", WSA_E_CANCELLED, "WSA_E_CANCELLED : ", WSAEREFUSED, "WSAREFUSED : Refused, 거부됨",
int main() { // 소켓 디스크립트, socket descript 정의 int ssock, csock, clen; int fflag = 0; struct sockaddr_in client_addr, server_addr; // 주소 구조체 정의 char buf[MAXBUF] = "I like you!"; // 클라이언트에 보내줄 문자열
// 서버 소켓 생성 if( ssock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) < 0 ) { perror("socket error : "); exit(1);
}
clen = sizeof(client_addr);
// 주소 구조체에 주소 지정 memset( &server_addr, 0, sizeof(server_addr) ); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(3317); // 사용할 포트로 3317번 포트 사용
// bind 함수를 사용하여 서버 소켓의 주소 설정 if( bind(ssock, (struct sockaddr* )&server_addr, sizeof(server_addr)) > 0 ) { perror("bind error : "); exit(1);
}
// 위에서 지정한 주소로 클라이언트 접속을 기다림 if (listen(ssock, 8) > 0) { perror("listen error : "); exit(1);
int main
(int argc, char **argv) { WORD wVersionRequested; /* socket dll
version info */ WSADATA wsaData; /* data for socket lib
initialisation */ SOCKET sock; /* socket details
*/ const int BUF_LEN=10000; /* Buffer size for transfers */
struct sockaddr_in address; /* socket address stuff */ struct hostent
* host; /* host stuff */ int err; /*
error trapping */ float socklib_ver; /* socket dll version
*/ char File_Buf[BUF_LEN]; /* file buffer */ char
DomainName[100]; /* domain name from user */ char HostName[100];
/* host name from user */ time_t now; /*
for date and time */
if (argc < 2) /* check we
have command line options */ { printf("\nUseage: whois domainname
[whois.server]\n"); exit(0); }
strcat(DomainName, argv[1]);
/* get domain name from command line */ strcat(DomainName, "\r\n"); /* add
crlf as whois servers expect it */
if (argc == 3)
strcat(HostName, argv[2]); /* get host name from command line */
else strcat(HostName, "whois.internic.net");
wVersionRequested
= MAKEWORD( 1, 1 );
/* * We need to call the WSAStartup
routine BEFORE we try to use any of * the Winsock dll calls.
*/
if ( WSAStartup( wVersionRequested, &wsaData ) != 0 )
handle_error();
if ( socklib_ver < 1.1 )
{ printf ("\nError: socket library must support 1.1 or
greater.\n"); WSACleanup(); /* clean up before exit */
exit(0); }
/* write current date and time to log file and screen
*/
time(&now); sprintf(File_Buf, "Whois Session Started
%.24s.\n\n", ctime(&now)); write_file(File_Buf);
/* *
Open a socket. The AF_INET parameter tells windows we want to use the *
internet. Other parameters for different networking can be chosen e.g. *
for netbios, IPX etc. The SOCK_STREAM parameter lets windows know we want
* to use TCP rather than UDP, and the final parameter will always be *
zero for what we want to do and tells windows to use whatever * default
communication protocol has been established (eg PPP and IP) */
/* We now need to initialise a couple of variables in
the address * structure. Once again, to tell windows we are using the
internet, * and also what port we want to use when connecting to a
remote * computer. In this case it is port 43 which is the standard port
for * whois. The htons routine is used to convert the way Intel
chips * store data in memory, which is different compared to many other
computers. * The standard is based on Motorola format.
*/
address.sin_family=AF_INET; /* internet */
address.sin_port = htons(43); /* port 43 for whois */
/* write to
the log file and screen */
sprintf(File_Buf,"Connecting to %s\n",
HostName); write_file(File_Buf);
/* * host is a pointer
to a structure of the predefined type hostent. We * need to call
gethostbyname with the DNS name we want to use to return * a pointer to a
hostent structure. This is so we can resolve an IP * address from our
ISP's nameserver. */
if ( (host=gethostbyname(HostName)) ==
NULL ) handle_error();
/* we then initialise the address
structure with the resolved IP address
*/
address.sin_addr.s_addr=*((unsigned long *)
host->h_addr);
/* Now we're ready to actually connect to the whois
server itself */
if ( (connect(sock,(struct sockaddr *) &address,
sizeof(address))) != 0) handle_error();
/* * We
should be connected to the whois server at this point * so we need to
send the domain name and wait for the response. The send * and recv
routines are always used with TCP. These enable handshaking * compared
to the sendto and recvfrom routines which are used for UDP * protocol,
ie without handshaking. */
strcpy(File_Buf,
DomainName); err=send(sock,File_Buf,strlen(File_Buf),0); /* send domain
name */ err=recv(sock,File_Buf,BUF_LEN,0); /* discard first
response */ err=recv(sock,File_Buf,BUF_LEN,0); /* get query
results back */ write_file(File_Buf);
/* Always call
WSACleanup before exiting */
WSACleanup(); /* clean up before exit
*/ exit(0); }
void
handle_error(void) { /* * Errors are handled by calling the
WSAGetLastError routine which * will return the last error as one of
the following. As we develop * this tutorial, we will go into much more
detail on what they mean * and what caused
them. */
switch ( WSAGetLastError() ) { case
WSANOTINITIALISED : printf("Unable to initialise
socket.\n"); break; case WSAEAFNOSUPPORT
: printf("The specified address family is not
supported.\n"); break; case WSAEADDRNOTAVAIL
: printf("Specified address is not available from the local
machine.\n"); break; case WSAECONNREFUSED
: printf("The attempt to connect was forcefully rejected.\n");
break; case WSAEDESTADDRREQ : printf("address
destination address is required.\n"); break; case WSAEFAULT
: printf("The namelen argument is
incorrect.\n"); break; case WSAEINVAL : printf("The
socket is not already bound to an address.\n"); break; case
WSAEISCONN : printf("The socket is already
connected.\n"); break; case WSAEADDRINUSE
: printf("The specified address is already in
use.\n"); break; case WSAEMFILE : printf("No more
file descriptors are available.\n"); break; case WSAENOBUFS
: printf("No buffer space available. The socket cannot be
created.\n"); break; case WSAEPROTONOSUPPORT
: printf("The specified protocol is not
supported.\n"); break; case WSAEPROTOTYPE
: printf("The specified protocol is the wrong type for this
socket.\n"); break; case WSAENETUNREACH :
printf("The network can't be reached from this host at this
time.\n"); break; case WSAENOTSOCK : printf("The
descriptor is not a socket.\n"); break; case WSAETIMEDOUT
: printf("Attempt timed out without establishing a
connection.\n"); break; case WSAESOCKTNOSUPPORT
: printf("Socket type is not supported in this address
family.\n"); break; case WSAENETDOWN
: printf("Network subsystem
failure.\n"); break; case WSAHOST_NOT_FOUND
: printf("Authoritative Answer Host not
found.\n"); break; case WSATRY_AGAIN
: printf("Non-Authoritative Host not found or
SERVERFAIL.\n"); break; case WSANO_RECOVERY
: printf("Non recoverable errors, FORMERR, REFUSED,
NOTIMP.\n"); break; case WSANO_DATA : printf("Valid
name, no data record of requested type.\n"); break; case
WSAEINPROGRESS : printf("address blocking Windows Sockets operation
is in progress.\n"); break; case WSAEINTR
: printf("The (blocking) call was canceled via
WSACancelBlockingCall().\n"); break; default
: printf("Unknown
error.\n"); break; }
WSACleanup(); exit(0); }
void
write_file(char *buf) { /* writes results to a log file and also to the
screen */
int main(int argc, char** argv) { WORD wVersionRequested; // 소켓 dll 버전 정보 WSADATA wsaData; // 소켓 lib 생성화를 위한 자료 SOCKET sock; // 소켓 정보 const int BUF_LEN = 10000; // 전송을 위한 크기 설정 struct sockaddr_in address; // 소켓 주소 struct hostent* host; // 호스트 int err; // 문제 코드 잡기 float socklib_ver; // 소켓 DLL 버전 char File_Buf[BUF_LEN]; // 파일 버퍼 char DomainName[100]; // 사용자 도메인 이름 char HostName[100]; // 사용자 호스트 이름 time_t now; // 날짜와 시간
if (argc < 2) // 실행 옵션을 가지고 있는지 확인 { printf("\n사용법 : whois 도메인명 [whois.server] \n"); exit(0);
}
strcpy(DomainName, argv[1]); // 실행줄에서 도메인 이름 얻기 strcat(DomainName, "\r\n"); // 서버가 예상하는 Carrage Return , Line Feed 를 넣어 준다.
// 소켓 DLL 이 1.1 또는 이상인지 확인 합니다. socklib_ver = (float)(HIBYTE( wsaData.wVersion ) / 10.0); socklib_ver += LOBYTE( wsaData.wVersion );
if ( socklib_ver < 1.1 ) { printf("\n오류 : 소켓 라이브러리 버전이 1.1 이상이여야 합니다.\n"); WSACleanup(); // 나가기 전에 정리 exit(0);
}
// 현재 시간과 날짜를 화면에 기록합니다
time(&now); sprintf(File_Buf, "Whois 세션 시작 %.24s.\n\n", ctime(&now)); write_file(File_Buf);
/* 소켓을 엽니다. AF_INET 파라메터는 윈도우즈에게 우리가 인터넷 요구한다는 것을 알려줍니다. 다른 파라메터는 서로 다른 네트워크가 선택되었을 때를 위함입니다. 예) NetBIOS, IPX 등. SOCK_STREAM 파라메터는 윈도우즈에게 TCP 를 UDP 보다더 원한다 는 것을 알려줍니다. 그리고 마지막 파라메터는 항상 0 으로 정해서 윈도우즈에게 기본적으로 전송할 프로토콜이 어떤건지 알려 줍니다 (예 PPP, IP)
/* 우리는 이제 주소 구조에 있는 몇몇 변수들을 생성해야 합니다. 다시 한번 윈도우즈에게 우리는 인터넷을 쓰고 우리가 원격지에 있는 컴퓨터와 접속시 쓸 포트 번호를 말해줍니다. 이번 경우 43 포트가 쓰이는데 이는 whois 를 위한 표준 포트 입니다. htons 루틴은 인텔 계열 칩의 메모리 저장 방식을 다른 컴퓨터들과 맞게 하기 위해서 변환하는데 쓰입니다. 이 표준은 모토로라 형식에 근간합니다.
*/
address.sin_family = AF_INET; // 인터넷 address.sin_port = htons(43); // whois 를 위한 포트 43
// 로그 파일과 화면에 기록
sprintf(File_Buf, "%s 에 접속합니다\n", HostName); write_file(File_Buf);
/* host 는 hostent 형으로 미리 정의된 구조체의 포인터입니다. 우리는 gethostbyname 에 DNS 주소를 써서 hostent 구조체의 주소를 받습니다. 이는 우리가 IP 주소를 ISP 네임서버 에서 받아올 수 있게 하기 위해서 입니다.
/* 우리는 이 시점에서 whois 서버에 접속을 해야 합니다. 그리고 domain 이름을 보내고 응답을 기다립니다. send 와 recv 루틴이 항상 TCP 와 함께 사용됩니다. 이들은 헨드셰이킹 을 하는데 sendto 와 recvfrom 루틴을 쓰는 UDP 프로토콜(헨드세이킹을 하지 않습니다) 과 같습니다. */
strcpy(File_Buf, DomainName); err = send(sock, File_Buf, strlen(File_Buf), 0); // 도메인 이름을 보냅니다. err = recv(sock, File_Buf, BUF_LEN, 0); // 첫번째 응답을 버립니다. err = recv(sock, File_Buf, BUF_LEN, 0); // 돌아오는 질의의 결과를 받습니다. write_file(File_Buf);
// 항상 나가기 전에 WSACleanup 을 수행합니다
WSACleanup(); // 나가기 전에 청소
exit(0);
return 0; }
void handle_error(void) {
/* Error 는 WSAGetLastError 루틴을 써서 받아 오는 최근의 오류를 받습니다. 이 교제 배우면서 우리는 좀더 무엇 때문에 이 오류가 나는지 배우게 될 것입니다
*/
switch( WSAGetLastError() ) { case WSANOTINITIALISED : printf("소켓을 시작 할수가 없습니다. \n"); break;
case WSAEAFNOSUPPORT : printf("이 주소 계열은 지원하지 않습니다.\n"); break;
case WSAEADDRNOTAVAIL : printf("이 주소는 로컬 컴퓨터에서 할당 받을수 없습니다.\n"); break;
case WSAECONNREFUSED : printf("이 시도는 의도적으로 거절당했습니다.\n"); break;
case WSAEDESTADDRREQ : printf("목적지 주소가 필요합니다.\n"); break; case WSAEFAULT : printf("namelen 인수가 틀립니다.\n"); break;
case WSAEINVAL : printf("소켓이 아직 그 주소로 가지(bound) 않았습니다.\n"); break;
case WSAEISCONN : printf("소켓이 이미 연결 되었습니다.\n"); break;
case WSAEADDRINUSE : printf("그 주소는 이미 사용 중입니다.\n"); break;
case WSAEMFILE : printf("더 이상 기술자 파일이 존재 하지 않습니다\n"); break;
case WSAENOBUFS : printf("더 이상 버퍼 공간이 있지 않습니다. 소켓이 만들어 질수 없습니다.\n"); break;
case WSAEPROTONOSUPPORT : printf("명시한 프로토콜은 지원하지 않습니다.\n"); break;
case WSAEPROTOTYPE : printf("명시한 프로토콜은 이 소켓과 맞지 않은 형식입니다.\n"); break;
case WSAENETUNREACH : printf("지금 시간에는 이 호스트에서 네트워크에 접근 할수가 없습니다.\n"); break;
case WSAENOTSOCK : printf("기술자가 소켓이 아닙니다.\n"); break;
case WSAETIMEDOUT : printf("접속이 되지 않은 시도의 시간이 초과되었습니다.\n"); break;
case WSAENETDOWN : printf("네트워크 하부시스템이 망가졌습니다.\n"); break;
case WSAHOST_NOT_FOUND: printf("인증 응답 호스트가 발견되지 않았습니다.\n"); break;
case WSATRY_AGAIN : printf("비-인증 호스트가 없거나 서버가 실패(SERVERFAIL)했습니다.\n"); break;;
case WSANO_RECOVERY : printf("되돌릴수 없는 에러 입니다. FORMERR, REFUSED, NOTIMP.\n"); break; case WSANO_DATA : printf("유요한 이름이지만 요구된 형식의 자료가 없습니다.\n"); break;
case WSAEINPROGRESS : printf("주소 막기 윈도우즈 소켓 작업이 진행 중입니다.\n"); break;
case WSAEINTR : printf("(막힌) 호출이 WSACancelBlockingCall() 함수에 의해 취소 되었습니다.\n"); break;
프로젝트 옵션에서 Link 탭, Object/library modules 에 ws2_32.lib 를 추가(Lib 링크 오류)하시고 Project Options 에 보시면 ws2_32.lib /nologo /subsystem:console 로 맞춰 주시야지 Link 오류(WinMain16 링크 오류)가 생기지 않습니다.
실행 결과 : 원본 코드 / 친 코드 모드 작동되지 않았습니다. Finger 서버 자체에 접속이 안되었습니다. 컴파일 환경이 달라서 그런거 같습니다.