setsockopt(IN SOCKET socket, IN int name, IN int optlevel, IN const char* optvalue, IN int optlen);

[1] 입출력 버퍼크기의 변경

SOCKET sock = socket(PF_INET, SOCK_STREAM, 0);
int send_buf = 500;
i
nt rcv_buf = 1000;
int state = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&rcv_buf, sizeof(rcv_buf));
if(state) errorhandling("setsockopt() error");
state = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&send_buf, sizeof(send_buf));
if(state) errorhandling("setsockopt() error");
 

[2] Nagle 알고리즘의 적용 - 한번에 모아서 전송
TCP 소켓은 기본적으로 Nagle 알고림을 사용하여 한번에 모아서 전송함. 지연 발생(100~200ms).
이 옵션 사용시 리턴과 동시에 데이터 전송이 이루어지나 회선 부하가 많아짐.

int sock;
int flag = 1; // 네이글 알고리즘 off
sock = open(...);
if ( setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) < 0)
{
printf("setsockopt error\n");
....
}


[3] 송/수신 TIMEOUT 설정 - 블러킹 소켓일 경우

SCOKET hSocket;
int nErrorCode;
hSocket = socket(AF_INET,SOCK_STREAM,0);
~~~~
nErrorCode = connect(~~~~~~);
~~~
// RECEIVE & SEND TIMEOUT 설정법
// hSocket이 블럭킹상태(Blocking) 일경우 해당된다. 논 블럭킹 상태(None-Blocking) 이면 recv에서 SOCKET_ERROR를 반환하고
//WSAGetLastError()로 확인 하면 WSAEWOULDBLOCK를 반환 한다. 
//WSAEWOULDBLOCK이 에러가 아니고, 다른 에러 이면 에러 코드를 참조 하여 에러 처리를 한다. 
// Receive Time Out Value : 3000 (약 3초)

int nTimeOutValue = 3000;
nErrorCode = setsockopt(hSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*) & nTimeOutValue, sizeof(nTimeOutValue));
if(SOCKET_ERROR == nErrorCode){ // 에러 처리
}
nErrorCode = setsockopt(hSocket, SOL_SOCKET, SO_SNDTIMEO, (const char*) & nTimeOutValue, sizeof(nTimeOutValue));
if(SOCKET_ERROR == nErrorCode) { // 에러 처리
}
nErrorCode = send(hSocket,버퍼,전송할 버퍼크기, 0);
~~~
nErrorCode = recv(hSocket,버퍼, 버퍼크기, 0);
~~~

 

[4] 소켓 종료시 종료방식 설정

LINGER 구조체의 _onoff _linger 두개의 값에 플래그를 지정하고 setsockopt 에 설정
l_onoff = 0, l_linger = 0(또는 1) : 버퍼에 있는 내용을 모두 전송후 연결 종료한다.
l_onoff = 1, l_linger = 0 : 즉시 연결을 종료한다. 상대방에게는 FIN이나 RTS 시그널이 전달된다.
l_linger = 1 : 버퍼에 있는 내용을 모두 전송후에 연결을 종료한다. 이 동안 closesocket 은 block 된다.

LINGER opt = {onoff, linger}; // 값을 설정
setsockopt(socket, SOL_SOCKET, SO_LINGER, (char *)&opt, sizeof(opt));


[5] 소켓 비정상 종료시 재 bind 를 허용하도록 함

bind 되었던 소켓이 서버의 비정상 종료로 커널이 아직 그 정보를 갖고 있을 경우, 다시금 bind 할 수 없는 경우가 있는데, 이때 선점된 주소로 인해 bind에 실패할 수 있다. 이 옵션은 재 bind 할 수 있도록 한다.

bool reuseflag = true;
setsockop(listen_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseflag, sizeof(reuseflag));
 

[6] UDP 소켓을 브로드캐스트 가능하도록 설정

디폴트로 생성되는 UDP소켓은 브로드캐스트가 불가능하도록 설정되어있다. 이 소켓을 브로트캐스트가 가능하도록 한다.
 

// serverside
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr(szServerAddress);
serverAddr.sin_port = htons(nPort); // 포트는 serverside 와 clientside 모두 통일
state = setsockopt(hSock, SOL_SOCKET, SO_BROADCAST, (char *)&serverAddr, sizeof(serverAddr)); 

Posted by cyj4369
,
winsock을 이용한 파일 보내기(C++) 소스
 
#include<winsock2.h>
#include<stdio.h>
#include<stdlib.h>
 
#define BUFSIZE 4096
 
// 소켓 함수 오류 출력 후 종료
void err_quit(char *msg)
{
           LPVOID lpMsgBuf;
           FormatMessage(
                     FORMAT_MESSAGE_ALLOCATE_BUFFER|
                     FORMAT_MESSAGE_FROM_SYSTEM,
                     NULL, WSAGetLastError(),
                     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                     (LPTSTR)&lpMsgBuf, 0, NULL);
           MessageBox(NULL, (LPCTSTR)lpMsgBuf, msg, MB_ICONERROR);
           LocalFree(lpMsgBuf);
           exit(-1);
}
 
// 소켓 함수 오류 출력
void err_display(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", msg, (LPCTSTR)lpMsgBuf);
           LocalFree(lpMsgBuf);
}
 
int main(int argc, char *argv[])
{
 
    int err;
 
           if(argc < 2){
                     fprintf(stderr, "Usage: %s <FileName>\n", argv[0]);
                     return -1;
           }
 
          
           WSADATA wsa;
           if(WSAStartup(MAKEWORD(2,2),&wsa) !=0 )
                     return -1;
 
           SOCKET senderSock = socket(AF_INET,SOCK_STREAM, 0);
    if(senderSock == INVALID_SOCKET){
                                             err_quit("socket()");
                                                                                               }
          
           // connect()
           SOCKADDR_IN senderaddr;
           ZeroMemory(&senderaddr,sizeof(senderaddr));
           senderaddr.sin_family = AF_INET;
           senderaddr.sin_port = htons(9000);
           senderaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
          
           err = connect(senderSock, (SOCKADDR *)&senderaddr,sizeof(senderaddr));    
           if(err == SOCKET_ERROR) err_quit("connect()");
 
 
           //파일 열기
           FILE *fp = fopen(argv[1],"rb");
 
           if(fp== NULL)
           {
            perror("파일 입출력 오류");
            return -1;
           }
 
           //파일 이름 보내기
           char filename[256];
           ZeroMemory(filename,256);
           sprintf(filename,argv[1]);
           err = send(senderSock,filename,256,0);
           //if(err == SOCKET_ERROR) err_quit("send()");
 
 
           //파일 크기 얻기
           fseek(fp,0,SEEK_END);//파일 포인터를 파일의 끝으로 옮김
           int totalbytes = ftell(fp);
 
           printf("크기 :%d bytes\n",totalbytes); //ansi로 된 text파일에 있는 문자
 
           err = send(senderSock,(char *)&totalbytes,sizeof(totalbytes),0);
           if(err == SOCKET_ERROR) err_quit("send()");
 
 
           //파일 데이터 전송에 사용할 변수
           char buf[BUFSIZE];
           int numread;
           int numtotal = 0;
 
           //파일 데이터 보내기
 
           rewind(fp); // 파일 포인터를 제일 앞으로 이동
           while(1){
                     numread = fread(buf, 1, BUFSIZE, fp);
                     if(numread > 0){
                                err = send(senderSock, buf, numread, 0);
                                if(err == SOCKET_ERROR){
                                          err_display("send()");
                                          break;
                                }
                                numtotal += numread;
                     }
                     else if(numread == 0 && numtotal == totalbytes){
                                printf("파일 전송 완료!: %d 바이트\n", numtotal);
                                break;
                     }
                     else{
                                perror("파일 입출력 오류");
                                break;
                     }
           }
 
           fclose(fp);         
    closesocket(senderSock);
           WSACleanup();
 
return 0;
 
}
Posted by cyj4369
,
*HOSTENT*

 
HOSTENT 구조체는 윈도우즈 소켓 시스템에 할당됩니다. 애플리케이션에서는 이 구조체를 수정하거나 내용을 비워서는 안 됩니다. 
또한 각각의 쓰레드에 대하여 이구조체의 한개의 카피본 만이 할당되어야 합니다. 유효성은 다른 윈도우즈 소켓 함수가 호출될 때 까지만 보장되므로 애플리케이션은 이 정보가 필요하다면, 다른 윈도우즈 소켓 함수가 호출되기 전에 특정한 정보를 카피해야 합니다.
 gethostbyname, gethostbyaddr함수 사용시 hostent구조체가 반환됩니다.
 
 
 
struct hostent { 
        char FAR *              h_name; 
        char FAR * FAR *     h_aliases; 
        short                       h_addrtype; 
        short                       h_length; 
        char FAR * FAR *     h_addr_list; 
};
 
 
 
*Members*
 
h_name
호스트의 공식적인 이름입니다. 만약 DNS 나 그와 유사한 시스템이 사용되었을 때, 이 멤버는 FQDN(Fully Qualified Domain Name)을 가지게 됩니다.
 
h_aliases
호스트 이름과 관계되는 이름(앨리어스)으로, 널로 끝나는 스트링 배열 입니다.
 
h_addrtype
어드레스 타입을 지정하는데, 윈도우즈 소켓의 경우 항상 PF_INET 입니다.
 
h_length
각 어드레스의 길이를 지정하며, PF_INET 어드레스에 대해 항상 4 입니다.
 
h_addr_list
널 문자로 끝나는 호스트의 주소 리스트입니다. 주소는 네트웍 바이트 오더 입니다. 또한 이전 소프트웨어와 호환을 위해서 h_addr_list[0]을 의미하는 h_addr 매크로가 정의되어 있습니다.
 
 
 
*See Also*
 
overview, PROTOENT, SERVENT

Posted by cyj4369
,
*WSACleanup*
WSACleanup 함수는 WS2_32.DLL의 사용을 종료하는 함수입니다.
int WSACleanup (void);
 
*Remarks*
이 함수가 사용되기 전에 WSAStartup함수의 성공적이 수행이 있어야 합니다. 이 함수는 WSAStartup함수로 윈속을 초기화 하고 윈도즈 소켓을 다 사용하고 나서 사용한 소켓 리소스를 반환할 때 사용되는 함수이므로 반드시 윈도즈 소켓 사용후에 더 이상 소켓 자원이 필요 없을 때, 꼭 호출되어야 합니다. 
또한 이함수가 수행될 때, 블록킹 상태에 있는 처리나, 비동기 작업은 통지 메시지나 이벤트의 포스팅 작업이 없이 바로 취소됩니다. 결국 완벽한 작업을 끝마치지 못한 상태에서 중지되는 것이라 하겠죠. 이러한 경우 연산은 실패로 돌아가고, WSA_OPERATION_ABORTED라는 에러코드를 발생하게 됩니다.
WSACleanup함수가 호출되었을 때 개방된 소켓은 리셋되고 WSACleanup함수가 호출되기 전에 closesocket 함수에 의해 닫힌 소켓은 정상적으로 닫히게 됩니다. 이 때 소켓은 전송 큐에 걸려 있는 데이터를 여전히 가지게 됩니다. WSACleanup 함수가 호출되지 않을 경우 걸려있는 데이터는 그대로 전송되게 됩니다.
참고의 말씀을 드리자면, 이렇게 걸려있는 데이터(pending data)를 다 전송하도록 하려면, 접속을 종료하기 위해 shutdown 함수를 호출해야 합니다. 이렇게 하면 걸려있는 데이터를 모두다 전송 할 때까지 대기하게 되고, 그다음에 closesocket 그리고, WSACleanup 함수를 호출하게 된다면, 아주 이상적으로 소켓을 종료 할 수 있습니다.
블록킹 후크 상태에서 WSACleanup 함수를 호출하려면, 먼저 WSACancelBlockingCall 함수를 먼저 호출하고 나서 WSACleanup 함수를 호출해야만 한다는 점 주의하시구요. 음... 또 주의 해야 할점은 멀티 쓰레드 환경에서, WSACleanup 함수의 호출은 모든 쓰레드에 대해서 윈속 연산을 종료시킨 다는 것입니다.
 
*Return Values*
성공할 경우 0을 반환하고, 에러가 발생할 경우 SOCKET_ERROR 에러가 반환되고, WSAGetLastError 함수를 호출해서 특정한 에러코드를 얻어낼 수 있습니다.
 
*Error Codes*
WSANOTINITIALISED 이 함수를 사용하기 이전에 WSAStartup 함수를 성공적으로 호출해야 합니다.
WSAENETDOWN 네트웍 서브 시스템에서 에러가 났습니다.
WSAEINPROGRESS 블럭킹 윈속 v1.1이 진행상태에 있거나, 서비스 프로바이더가 아직 콜백함수를 진행하고 있습니다.
 
*QuickInfo*
Windows NT : 사용가능
Windows : 사용가능
Windows CE : 버젼 1.0 그리고 그이후의 버젼에서 사용가능
Header :
          Win16/32 : winsock.h
          Win32-II : winsock2.h
Import Library :
          Win16 : winsock.lib
          Win32 : wsock32.lib
          Win32-II : ws2_32.lib

*See Also*
overview, closesocket, shutdown, WSAStartup

<출처>
http://blog.naver.com/a71045851?Redirect=Log&logNo=150002011123
Posted by cyj4369
,
윈도우 소켓을 이용하는 서비스의 종류에는 HTTP, FTP, GOPHER, TCP/IP 등의 프로토콜 서비스가 있다.
언급한 서비스 중 하나라도 사용을 원한다면, 윈도우 소켓을 사용할 수 있도록 초기화 작업을 해야한다.


윈도우 소켓을 초기화하는 함수로는 WSAStartup 함수 하나 밖에 없다. 
소켓 관련 함수를 사용하기 전에 가장 먼저 이 함수를 호출한다. 
WSAStartup 함수는 Ws2_32.dll 함수를 응용 프로그램의 영역으로 로드한다.
(#pragma comment(lib, "ws2_32.lib") 필요...)
더불어 로드한 dll 파일로부터 사용할 수 있는 윈도우 소켓의 최상위 버전을 알아내거나 어떤 버전의 소켓을 사용할 것인지 
알려주는 역할도 함께한다. 
 
WSAStartup 함수의 호출이 실패할  경우, 윈도우 소켓을 반드시 사용해야 하는 프로그램이라면 어쩔 수 없이 프로그램을 종료해야 한다.
아니면, 지원되지 않는 버전을 사용하겠다고 요청했다면, 하위 버전으로 호출이 성공할 때까지 계속적으로 시도할 수 있다.
그러나, 이 함수는 기본적으로 Ws2_32.dll 파일을 사용하기 때문에, Ws2_32.dll 파일은 반드시 존재해야 한다.
 
 
WSADATA 구조체 변수는 WSAStartup 함수가 반환하는 윈도우 소켓의 세부 정보의 저장에 사용된다.

사용예>
WSADATA wsaData;
 if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
  return -1;
Posted by cyj4369
,