2011년 9월 30일 금요일

윈도우에서 gettimeofday 함수구현 - windows gettimeofday function

< 상대시간 >

int gettimeofday(struct timeval* tp, int* tz)
{
LARGE_INTEGER tickNow;
static LARGE_INTEGER tickFrequency;
static BOOL tickFrequencySet = FALSE;
if (tickFrequencySet == FALSE) {
QueryPerformanceFrequency(&tickFrequency);
tickFrequencySet = TRUE;
}
QueryPerformanceCounter(&tickNow);
tp->tv_sec = (long) (tickNow.QuadPart / tickFrequency.QuadPart);
tp->tv_usec = (long) (((tickNow.QuadPart % tickFrequency.QuadPart) * 1000000L) / tickFrequency.QuadPart);

return 0;
}


< 절대시간 - unix 타임 >

#include <stdio.h>
#include < time.h >
#include <windows.h>

#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
  #define DELTA_EPOCH_IN_MICROSECS  11644473600000000Ui64
#else
  #define DELTA_EPOCH_IN_MICROSECS  11644473600000000ULL
#endif

struct timezone
{
  int  tz_minuteswest; /* minutes W of Greenwich */
  int  tz_dsttime;     /* type of dst correction */
};

int GetTimeOfDay(struct timeval *tv, struct timezone *tz)
{
FILETIME ft;
uint64_t tmpres = 0;
static int tzflag;

if (NULL != tv)
{
// system time을 구하기
GetSystemTimeAsFileTime(&ft);

// unsigned 64 bit로 만들기
tmpres |= ft.dwHighDateTime;
tmpres <<= 32;
tmpres |= ft.dwLowDateTime;

// 100nano를 1micro로 변환하기
tmpres /= 10;

// epoch time으로 변환하기
tmpres -= DELTA_EPOCH_IN_MICROSECS;  

// sec와 micorsec으로 맞추기
tv->tv_sec = (tmpres / 1000000UL);
tv->tv_usec = (tmpres % 1000000UL);
}

// timezone 처리
if (NULL != tz)
{
if (!tzflag)
{
_tzset();
tzflag++;
}
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}

return 0;
}

int64_t GetTimeOfDay()
{
struct timeval now;
GetTimeOfDay(&now, NULL);

int64_t millisecond = (int64_t)(now.tv_sec)*1000 + (int64_t)(now.tv_usec)/1000;

return millisecond;
}

윈도우에서 writev 함수구현 - windows writev function implementation


#include <winsock2.h>

int writev(int sock, struct iovec *iov, int nvecs)
{
DWORD ret;
if (WSASend(sock, (LPWSABUF)iov, nvecs, &ret, 0, NULL, NULL) == 0) {
return ret;
}
return -1;
}

윈도우/리눅스 non-blocking socket 만들기 - making windows/linux non-blocking socket


Boolean makeSocketNonBlocking(int sock) {
#if defined(WIN32) || defined(_WIN32) || defined(IMN_PIM)
  unsigned long arg = 1;
  return ioctlsocket(sock, FIONBIO, &arg) == 0;
#elif defined(VXWORKS)
  int arg = 1;
  return ioctl(sock, FIONBIO, (int)&arg) == 0;
#else
  int curFlags = fcntl(sock, F_GETFL, 0);
  return fcntl(sock, F_SETFL, curFlags|O_NONBLOCK) >= 0;
#endif
}

윈도우는 ioctlsocket, 리눅스는 fcntl 사용

2011년 9월 29일 목요일

윈도우 소켓버퍼 사이즈 늘리기 - increasing windows socket buffer size

< 현재 소켓 버퍼 사이즈 읽어오기 >

unsigned getBufferSize(int bufOptName,
     int socket) {
  unsigned curSize;
  SOCKLEN_T sizeSize = sizeof curSize;
  if (getsockopt(socket, SOL_SOCKET, bufOptName,
(char*)&curSize, &sizeSize) < 0) {
    socketErr("getBufferSize() error: ");
    return 0;
  }

  return curSize;
}

< 소켓 버퍼 사이즈 늘리기 설정 >

#ifndef SOCKLEN_T
#define SOCKLEN_T socklen_t
#endif

unsigned increaseBufferTo( int bufOptName,
int socket, unsigned requestedSize) {
  // First, get the current buffer size.  If it's already at least
  // as big as what we're requesting, do nothing.
  unsigned curSize = getBufferSize(bufOptName, socket);

  // Next, try to increase the buffer to the requested size,
  // or to some smaller size, if that's not possible:
  while (requestedSize > curSize) {
    SOCKLEN_T sizeSize = sizeof requestedSize;
    if (setsockopt(socket, SOL_SOCKET, bufOptName,
  (char*)&requestedSize, sizeSize) >= 0) {
      // success
      return requestedSize;
    }
    requestedSize = (requestedSize+curSize)/2;
  }

  return getBufferSize( bufOptName, socket);
}

< 함수 사용 >

// 송신버퍼 사이즈 늘리기
unsigned increaseSendBufferTo(int socket, unsigned requestedSize) {
  return increaseBufferTo( SO_SNDBUF, socket, requestedSize);
}

// 수신버퍼 사이즈 늘리기
unsigned increaseReceiveBufferTo( int socket, unsigned requestedSize) {
  return increaseBufferTo( SO_RCVBUF, socket, requestedSize);
}

< 함수 사용 >

int ret, size = 1024*1024;

if ((ret=increaseSendBufferTo(fsock, size)) != size)
printf("%s failed to increase send buffer size (size:%d, ret:%d)\r\n", __FUNCTION__, size, ret);


*** 버퍼 사이즈를 원하는 만큼 늘릴 수 없으면 윈도우 레지스트리 설정을 추가/수정해야한다.

[HKEY_LOCAL_MACHINE \SYSTEM \CurrentControlSet \Services \Afd \Parameters]
DefaultReceiveWindow = 16384
DefaultSendWindow = 16384

* 리부팅할 필요없고 위 레지스트리 값만 넣어주면된다.






winsock socket 연결 64개 이상으로 늘리는 법 - increasing winsock 64 sockets limitation

윈속 소켓 연결시 한 쓰레드에 64개까지만 연결을 제한한다. (서버측 accept 혹은 select 할 소켓 디스크립터 갯수)
이것은 winsock2.h 에 FD_SET 이 64 로 정의되어있기 때문인데 FD_SET 을 아래 코드와 같이 정의해서 연결을 늘려줄 수 있다.


#define FD_SETSIZE 1024
#include <winsock2.h>

< 출처 : http://tangentsoft.net/wskfaq/advanced.html >

c++ 에서 객체 delete 했는데 소멸자 호출안되는 경우

c++ 에서 클래스를 동적으로 생성/소멸해서 쓰는 때가 많은데 delete 로 객체를 메모리에서 해제 시켜도 해당 객체의 소멸자가 호출되지않는 귀신이 곡할때가 있다.
바로 아래의 경우에 해당한다.


< MyClass.h 파일 >

class MyClass {
public:
   MyClass();
   ~MyClass();
};

< MyClass.cpp 파일 >

MyClass::MyClass()
{
   printf("contructor\n");
}

MyClass::~MyClass()
{
   printf("destructor\n");
}

< MainClass.h 파일 >

class MyClass;

class MainClass {
...
   MyClass *myClass;
   void free();
};

< MainClass.cpp 파일 >

#include "MainClass.h"

MainClass::MainClass()
{
   myClass = new MyClass();
}
...
void MainClass::free()
{
   delete myClass;   => myClass 객체변수는 분명히 메모리해제되지만 MyClass::~MyClass() 소멸자 함수는 호출되지않는다
}

위와 같은 상황은 MainClass 쪽에서 MyClass 헤더파일을 include 해주지않고
MainClass.h 에 class MyClass; 와 같이 선언만 해주었기때문에 발생한다.
MainClass 에서 myClass 인스턴스의 어떤 변수/함수 필드도 엑세스하지않기때문에
컴파일 에러도 발생하지않으며 delete 시 메모리에서도 정상해제된다. 단지 소멸자 함수만
호출되지않는다.
이런 경우 반드시 MyClass.h 파일을 include 해주어야한다.

< MainClass.cpp >

#include "MyClass.h"   <= !!!

2011년 9월 26일 월요일

vc++에서 winsock2 헤더파일 include 및 link(링크)

< winsock2 include >

#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif

빌드에러가 발생하면 프로젝트 속성에서 C/C++ -> Preprocessor -> Preprocessor Definitions 에
_WINSOCKAPI_ 추가


< winsock2 library 링크 >

프로젝트 속성 -> Librarian -> General -> Additional Dependencies 에
ws2_32.lib 추가

WSAStartup() 함수로 winsock 초기화


윈도우에서 winsock 사용시 WSAStartup 함수로 winsock 을 초기화해야한다.


#if defined(__WIN32__) || defined(_WIN32)
#ifndef IMN_PIM
#define WS_VERSION_CHOICE1 0x202/*MAKEWORD(2,2)*/
#define WS_VERSION_CHOICE2 0x101/*MAKEWORD(1,1)*/
int initializeWinsockIfNecessary(void) {
/* We need to call an initialization routine before
* we can do anything with winsock.  (How fucking lame!):
*/
static int _haveInitializedWinsock = 0;
WSADATA wsadata;

if (!_haveInitializedWinsock) {
if ((WSAStartup(WS_VERSION_CHOICE1, &wsadata) != 0)
   && ((WSAStartup(WS_VERSION_CHOICE2, &wsadata)) != 0)) {
return 0; /* error in initialization */
}
    if ((wsadata.wVersion != WS_VERSION_CHOICE1)
       && (wsadata.wVersion != WS_VERSION_CHOICE2)) {
        WSACleanup();
return 0; /* desired Winsock version was not available */
}
_haveInitializedWinsock = 1;
}

return 1;
}
#else
int initializeWinsockIfNecessary(void) { return 1; }
#endif
#else
#define initializeWinsockIfNecessary() 1
#endif

...

// 윈속 초기화

  if (!initializeWinsockIfNecessary()) {
    socketErr(env, "Failed to initialize 'winsock': ");
    return -1;
  }


windows 에서 winsock select 함수사용


winsock select 함수 사용시 fd_set 이 비어있으면 select 함수에서 에러발생
이때 WSAGetLastError() 함수로 에러값을 보고 dummy 소켓을 생성해서 fd_set에
넣어줘야함. 아래 소스 참조


  fd_set readSet = fReadSet; // make a copy for this select() call

  struct timeval tv_timeToDelay;
  tv_timeToDelay.tv_sec = 1;
  tv_timeToDelay.tv_usec = 0;


  int selectResult = select(fMaxNumSockets, &readSet, NULL, NULL,
   &tv_timeToDelay);
  if (selectResult < 0) {
#if defined(__WIN32__) || defined(_WIN32)
    int err = WSAGetLastError();
    // For some unknown reason, select() in Windoze sometimes fails with WSAEINVAL if
    // it was called with no entries set in "readSet".  If this happens, ignore it:
    if (err == WSAEINVAL && readSet.fd_count == 0) {
      err = 0;
      // To stop this from happening again, create a dummy readable socket:
      int dummySocketNum = socket(AF_INET, SOCK_DGRAM, 0);
      FD_SET((unsigned)dummySocketNum, &fReadSet);
    }
    if (err != 0) {
#else
    if (errno != EINTR && errno != EAGAIN) {
#endif
// Unexpected error - treat this as fatal:
#if !defined(_WIN32_WCE)
perror("BasicTaskScheduler::SingleStep(): select() fails");
#endif
// exit(0);
      }
  }