WSAWaitForMultipleEvents
От: k732  
Дата: 08.08.06 11:23
Оценка: 3 (1)
запускаю сервер. Дпи соединении клиента — серверу приходит событие FD_ACCEPT.

Далее создается сокет клиента, ассоциируется с новым событием и добавляется в массив событий.

Клиент шлет данные. WSASend говорит, что все прошло без ошибок, но сервер не получает события FD_READ.

Вот примерный код:

создается слушающий сокет

    SOCKET socket = WSASocket ( protocol, type, 0, NULL, 0, 0);

    sockaddr_in address = {0};
    address.sin_family = protocol;
    address.sin_port = htons (port);
    address.sin_addr.s_addr = INADDR_ANY;

    OnSelect (socket, FD_ACCEPT | FD_CLOSE);
    bind (socket, (sockaddr*) &address, sizeof (address));
    listen (socket, 5);


функции

bool TcpServer::OnAccept (const Socket& socket, long flags)
{
    sockaddr_in address = {0};
    address.sin_family = m_protocol;
    address.sin_port = htons (m_port);
    address.sin_addr.s_addr = INADDR_ANY;

    int size = sizeof (address);

    if (m_map.size() >= WSA_MAXIMUM_WAIT_EVENTS - 1) return false;
    Socket client = WSAAccept (socket, (sockaddr*) &address, &size, NULL, 0);
    if (client == INVALID_SOCKET)
    {
        SocketDebug ("TcpServer::OnAccept (WSAAccept)");
        return false;
    }

    return OnSelect (client, flags);
}
bool TcpServer::OnSelect (const Socket& socket, long flags)
{
    WsaEvent event;
    if (WSAEventSelect (socket, event, flags) == SOCKET_ERROR)
    {
        SocketDebug ("TcpServer::OnAccept (WSAEventSelect)");
        return false;
    }

    m_map[event] = socket;
    update ();
    return true;
}



главная функция

unsigned TcpServer::MainThread (void* arg)
{

    WSANETWORKEVENTS net_events = {0};
    while (!m_stopped)
    {
        DWORD index = WSAWaitForMultipleEvents ((DWORD)m_events.size(),
            &m_events[0], FALSE, WAIT_TIME, FALSE);

        if ((index == WSA_WAIT_FAILED) || (index == WSA_WAIT_TIMEOUT))
        {
            continue;
        }

        WsaEvent event = m_events [index - WSA_WAIT_EVENT_0];
        Socket& socket = m_map[event];

        if (WSAEnumNetworkEvents (socket, event, &net_events) == SOCKET_ERROR)
        {
            SocketDebug ("TcpServer::MainThread (WSAEnumNetworkEvents)");
            break;
        }

        event.reset();
        if (net_events.lNetworkEvents & FD_CONNECT)
        {
            if (net_events.iErrorCode[FD_CONNECT_BIT] != 0)
            {
                OnClose (event);
                continue;
            }
            OnConnect (socket);
            StreamDebug ("Connect...");
        }
        if (net_events.lNetworkEvents & FD_ACCEPT)
        {
            if (net_events.iErrorCode[FD_ACCEPT_BIT] != 0)
            {
                OnClose (event);
                continue;
            }
            OnAccept (socket, FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE);
            StreamDebug ("Accept...");
        }
        if (net_events.lNetworkEvents & FD_CLOSE)
        {
            OnClose (event);
            StreamDebug ("Disconnect...");
        }
        if (net_events.lNetworkEvents & FD_READ)
        {
            if (net_events.iErrorCode[FD_READ_BIT] != 0)
            {
                OnClose (event);
                continue;
            }
            OnReceive (socket);
            StreamDebug ("Receive...");
        }
        if (net_events.lNetworkEvents & FD_WRITE)
        {
            if (net_events.iErrorCode[FD_WRITE_BIT] != 0)
            {
                OnClose (event);
                continue;
            }
            OnSend (socket);
            StreamDebug ("Send...");
        }
    }

    return 0;
}




он не получает не только FD_READ но и FD_CLOSE при закрытии клиента.

В чем может быть проблемма ?
Re[2]: WSAWaitForMultipleEvents
От: cod3r_200  
Дата: 08.08.06 13:19
Оценка: -1
Здравствуйте, butcher, Вы писали:

B>FD_CLOSE вы не получаете, вероятно, потому что вы не считали данные, которые послал клиент.

B>FD_READ вы, возможно, теряете, сбрасывая событие при event.reset(). WSAEnumNetworkEvents атомарно сбрасывает событие, после чего оно может вновь установиться до вызова reset.

B>ЗЫ. Разбираться в таком коде не имея всех исходников — достаточно сложно. Никогда не понимал, для чего нужно делать обёртки для системных вызовов и типов, когда их можно использовать напрямую. При чём, какой-то выгоды именно для этого кода обёртки не дают, IMHO.


Что за чушь вы говорите? В чем ошибка — Тут ясно как день (написал сообщением выше).
Обёртка делается обычно в виде классов, чтобы во время разработки приложения забыть про сокеты вообще. Это важно, когда сокеты не на первом месте стоят в реализации. Да и всегда так выгоднее сделать, чтобы не делать лишней работы.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: WSAWaitForMultipleEvents
От: butcher Россия http://bu7cher.blogspot.com
Дата: 08.08.06 11:59
Оценка:
Здравствуйте, k732, Вы писали:

K>главная функция

K>
K>        if (WSAEnumNetworkEvents (socket, event, &net_events) == SOCKET_ERROR)
K>        {
K>            SocketDebug ("TcpServer::MainThread (WSAEnumNetworkEvents)");
K>            break;
K>        }
K>        event.reset();
K>


K>он не получает не только FD_READ но и FD_CLOSE при закрытии клиента.

K>В чем может быть проблемма ?

FD_CLOSE вы не получаете, вероятно, потому что вы не считали данные, которые послал клиент.
FD_READ вы, возможно, теряете, сбрасывая событие при event.reset(). WSAEnumNetworkEvents атомарно сбрасывает событие, после чего оно может вновь установиться до вызова reset.

ЗЫ. Разбираться в таком коде не имея всех исходников — достаточно сложно. Никогда не понимал, для чего нужно делать обёртки для системных вызовов и типов, когда их можно использовать напрямую. При чём, какой-то выгоды именно для этого кода обёртки не дают, IMHO.

Нет ничего невозможного..
Re: WSAWaitForMultipleEvents
От: cod3r_200  
Дата: 08.08.06 13:13
Оценка:
Здравствуйте, k732, Вы писали:

K>В чем может быть проблемма ?


во первых WSACreateEvent, во вторых WSAEventSelect делается после bind и до listen. и в третих проверяй ошибки от ф-ций. ВСЕ ОШИБКИ !
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: WSAWaitForMultipleEvents
От: k732  
Дата: 08.08.06 17:02
Оценка:
Здравствуйте, cod3r_200, Вы писали:

_>Здравствуйте, k732, Вы писали:


K>>В чем может быть проблемма ?


_>во первых WSACreateEvent, во вторых WSAEventSelect делается после bind и до listen. и в третих проверяй ошибки от ф-ций. ВСЕ ОШИБКИ !


на счет WSACreateEvent не понял

сделат WSAEventSelect после bind и до listen — тоже самое
Re[2]: WSAWaitForMultipleEvents
От: k732  
Дата: 08.08.06 17:05
Оценка:
Здравствуйте, butcher, Вы писали:

B>Здравствуйте, k732, Вы писали:


K>>главная функция

K>>
K>>        if (WSAEnumNetworkEvents (socket, event, &net_events) == SOCKET_ERROR)
K>>        {
K>>            SocketDebug ("TcpServer::MainThread (WSAEnumNetworkEvents)");
K>>            break;
K>>        }
K>>        event.reset();
K>>


K>>он не получает не только FD_READ но и FD_CLOSE при закрытии клиента.

K>>В чем может быть проблемма ?

B>FD_CLOSE вы не получаете, вероятно, потому что вы не считали данные, которые послал клиент.

B>FD_READ вы, возможно, теряете, сбрасывая событие при event.reset(). WSAEnumNetworkEvents атомарно сбрасывает событие, после чего оно может вновь установиться до вызова reset.

закоментировал reset — ничего не поменялось

B>ЗЫ. Разбираться в таком коде не имея всех исходников — достаточно сложно. Никогда не понимал, для чего нужно делать обёртки для системных вызовов и типов, когда их можно использовать напрямую. При чём, какой-то выгоды именно для этого кода обёртки не дают, IMHO.

у меня событие (WsaEvent) и сокет (Socket) хранятся в std::map

и я забыл думать про закрытие сокета и т.п. все сделается в деструкторах — помоему очень удобно
Re[3]: WSAWaitForMultipleEvents
От: Michael Chelnokov Украина  
Дата: 08.08.06 17:10
Оценка:
Здравствуйте, cod3r_200, Вы писали:

B>>ЗЫ. Разбираться в таком коде не имея всех исходников — достаточно сложно. Никогда не понимал, для чего нужно делать обёртки для системных вызовов и типов, когда их можно использовать напрямую. При чём, какой-то выгоды именно для этого кода обёртки не дают, IMHO.


_>Что за чушь вы говорите? В чем ошибка — Тут ясно как день (написал сообщением выше).

_>Обёртка делается обычно в виде классов, чтобы во время разработки приложения забыть про сокеты вообще. Это важно, когда сокеты не на первом месте стоят в реализации. Да и всегда так выгоднее сделать, чтобы не делать лишней работы.

Это не чушь. Не похоже что вы забыли про сокеты при разработке приложения. Но зато Ваш код стал неудобоваримым для всех кроме Вас самого. И "лишней работы" в нём не поубавилось...
Re: WSAWaitForMultipleEvents
От: Michael Chelnokov Украина  
Дата: 08.08.06 17:20
Оценка:
Здравствуйте, k732, Вы писали:

K>Клиент шлет данные. WSASend говорит, что все прошло без ошибок, но сервер не получает события FD_READ.

<супер-мега-объектно-ориентированный код поскипан>
K>В чем может быть проблемма ?

Неохота ковыряться в Вашем коде, но проблема скорее всего в том что сервер никак не дал знать WinSock2 что он хочет читать из сокета. Вызовите recv сразу после accept (вызов, ессно, обломится с WSAEWOULDBLOCK) и FD_READ установится, когда появятся данные для чтения.
Re[2]: WSAWaitForMultipleEvents
От: k732  
Дата: 08.08.06 17:39
Оценка:
Здравствуйте, Michael Chelnokov, Вы писали:

MC>Здравствуйте, k732, Вы писали:


K>>Клиент шлет данные. WSASend говорит, что все прошло без ошибок, но сервер не получает события FD_READ.

MC><супер-мега-объектно-ориентированный код поскипан>
K>>В чем может быть проблемма ?

MC>Неохота ковыряться в Вашем коде, но проблема скорее всего в том что сервер никак не дал знать WinSock2 что он хочет читать из сокета. Вызовите recv сразу после accept (вызов, ессно, обломится с WSAEWOULDBLOCK) и FD_READ установится, когда появятся данные для чтения.


попробовал — вызов обломался, но события так и не приходят
Re[3]: WSAWaitForMultipleEvents
От: butcher Россия http://bu7cher.blogspot.com
Дата: 08.08.06 17:39
Оценка:
Здравствуйте, k732, Вы писали:

K>у меня событие (WsaEvent) и сокет (Socket) хранятся в std::map


Да.. Я бы проверил, все ли API функции получают параметры тех типов, которые они хотят, а не указатели на объекты каких-то непонятных им классов, массивы непонятного содержания и т.п.

K>и я забыл думать про закрытие сокета и т.п. все сделается в деструкторах — помоему очень удобно


По мне так лучше помнить всё что нужно и делать правильно, чтобы через пару лет взглянув на код не пришлось долго и мучительно рыться по всем исходникам, что бы удоствериться "делается ли в таком-то констукторе такое-то действие".
Но это так, лирика.. возможно это просто я привык ковыряться в Си'шном коде..

Нет ничего невозможного..
Re[3]: WSAWaitForMultipleEvents
От: cod3r_200  
Дата: 08.08.06 17:44
Оценка:
Здравствуйте, k732, Вы писали:

K>на счет WSACreateEvent не понял


K>сделат WSAEventSelect после bind и до listen — тоже самое


извиняюсь, я загнул насчет ошибок, ты всё верно проверяешь. И поменять местами тоже ничего не поменяет. А вот WSACreateEvent надо делать перед WSAEventSelect. А иначе оно просто на пустом евенте будет делать, и работать не будет. У сервера принцип примерно такой:
createsocket();  // серв сокет
bind(); // серв сокет
WSACreateEvent(); // для серв сокет. парная закрывающая - WSACloseEvent
WSAEventSelect(); //серв сокет
while (true) {
  //цикл бесконеч.
    //внимательно!!! для TCP указывать нада 
    //таймаут обязательно. в районе 10-15 мс. Иначе попа.
    Err = WSAWaitForMultipleEvents;  
    if(Err OK?) {
        Err2 = WSAEnumNetworkEvents();
        if(Err2 OK?) {
          //проверяем нужные поля
            //если есть FD_ACCEPT
            //то мы принимаем сокет и опять крутим цикл. 
            //т.е. мы создаем Event новый и добавляем в массив. 
            //для него тоже потребуется WSAWaitForMultipleEvents, как для сервер. сокета.
        } else {
          //проверим ошибку
        }
    } else {
      //проверить ошибку
    }
}


задачу можно усложнить вплоть до пула потоков и расчета нагрузки + защиты от DDoS атаки.
так что это очень простой пример.



.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: WSAWaitForMultipleEvents
От: cod3r_200  
Дата: 08.08.06 17:47
Оценка:
Здравствуйте, Michael Chelnokov, Вы писали:

MC>Это не чушь. Не похоже что вы забыли про сокеты при разработке приложения. Но зато Ваш код стал неудобоваримым для всех кроме Вас самого. И "лишней работы" в нём не поубавилось...


думал я над этим, в итоге реализовал "обертку" в виде удобных классов. Они подойдут для большинства проектов, с поддержкой сети. Но есть вещи поважнее, чем эти сокеты. лучше сразу сделать высокоуровневую обертку и забыть про них до конца разработки проги. ИМХО.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: WSAWaitForMultipleEvents
От: k732  
Дата: 08.08.06 18:26
Оценка:
Здравствуйте, cod3r_200, Вы писали:

_>Здравствуйте, k732, Вы писали:


K>>на счет WSACreateEvent не понял


K>>сделат WSAEventSelect после bind и до listen — тоже самое


_>извиняюсь, я загнул насчет ошибок, ты всё верно проверяешь. И поменять местами тоже ничего не поменяет. А вот WSACreateEvent надо делать перед WSAEventSelect. А иначе оно просто на пустом евенте будет делать, и работать не будет. У сервера принцип примерно такой:

_>
_>createsocket();  // серв сокет
_>bind(); // серв сокет
_>WSACreateEvent(); // для серв сокет. парная закрывающая - WSACloseEvent
_>WSAEventSelect(); //серв сокет
_>while (true) {
_>  //цикл бесконеч.
_>    //внимательно!!! для TCP указывать нада 
_>    //таймаут обязательно. в районе 10-15 мс. Иначе попа.
_>    Err = WSAWaitForMultipleEvents;  
_>    if(Err OK?) {
_>        Err2 = WSAEnumNetworkEvents();
_>        if(Err2 OK?) {
_>          //проверяем нужные поля
_>            //если есть FD_ACCEPT
_>            //то мы принимаем сокет и опять крутим цикл. 
_>            //т.е. мы создаем Event новый и добавляем в массив. 
_>            //для него тоже потребуется WSAWaitForMultipleEvents, как для сервер. сокета.
_>        } else {
_>          //проверим ошибку
_>        }
_>    } else {
_>      //проверить ошибку
_>    }
_>}
_>


_>задачу можно усложнить вплоть до пула потоков и расчета нагрузки + защиты от DDoS атаки.

_>так что это очень простой пример.

так у меня так и есть

    WsaEvent event; // <- в конструкторе вызывается WSACreateEvent
    if (WSAEventSelect (socket, event, flags) == SOCKET_ERROR)
    {
        return false;
    }

    m_map[event] = socket; // добавляем в std::map<WsaEvent, Socket>
    return true;


а вот про timeout не совсем понял
Re[5]: WSAWaitForMultipleEvents
От: cod3r_200  
Дата: 08.08.06 19:15
Оценка:
Здравствуйте, k732, Вы писали:

K>так у меня так и есть


K>
K>    WsaEvent event; // <- в конструкторе вызывается WSACreateEvent
K>    if (WSAEventSelect (socket, event, flags) == SOCKET_ERROR)
K>    {
K>        return false;
K>    }

K>    m_map[event] = socket; // добавляем в std::map<WsaEvent, Socket>
K>    return true;
K>


K>а вот про timeout не совсем понял


WsaEvent это обычный HANDLE если по русски. делай так:
WSAEVENT event = WSAEventCreate();


так, дальше. Таймаут у тебя тут (4-й параметр):
WSAWaitForMultipleEvents(.., .., .., 15, ..);
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[6]: WSAWaitForMultipleEvents
От: k732  
Дата: 08.08.06 20:06
Оценка:
Здравствуйте, cod3r_200, Вы писали:

_>Здравствуйте, k732, Вы писали:


K>>так у меня так и есть


K>>
K>>    WsaEvent event; // <- в конструкторе вызывается WSACreateEvent
K>>    if (WSAEventSelect (socket, event, flags) == SOCKET_ERROR)
K>>    {
K>>        return false;
K>>    }

K>>    m_map[event] = socket; // добавляем в std::map<WsaEvent, Socket>
K>>    return true;
K>>


K>>а вот про timeout не совсем понял


_>WsaEvent это обычный HANDLE если по русски. делай так:

_>
_>WSAEVENT event = WSAEventCreate();
_>



ну это дело вкуса — поверьте — тав все нормально

_>так, дальше. Таймаут у тебя тут (4-й параметр):

_>
_>WSAWaitForMultipleEvents(.., .., .., 15, ..);
_>


ааа... ну там у меня 100

блин — я уже голову сломал
Re[7]: WSAWaitForMultipleEvents
От: cod3r_200  
Дата: 08.08.06 21:45
Оценка:
Здравствуйте, k732, Вы писали:

K>блин — я уже голову сломал


просто сделай как говорю, я с этой штукой тоже не раз голову сломал
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: WSAWaitForMultipleEvents
От: butcher Россия http://bu7cher.blogspot.com
Дата: 09.08.06 04:10
Оценка:
Здравствуйте, k732, Вы писали:

K>
K>    WsaEvent event; // <- в конструкторе вызывается WSACreateEvent
K>    if (WSAEventSelect (socket, event, flags) == SOCKET_ERROR)
K>    {
K>        return false;
K>    }

K>    m_map[event] = socket; // добавляем в std::map<WsaEvent, Socket>
K>    return true; 
K>


Конструктор, конструктор копирования и деструктор WsaEvent в студию.

Нет ничего невозможного..
Re[6]: WSAWaitForMultipleEvents
От: k732  
Дата: 09.08.06 07:12
Оценка:
Здравствуйте, butcher, Вы писали:

B>Здравствуйте, k732, Вы писали:


K>>
K>>    WsaEvent event; // <- в конструкторе вызывается WSACreateEvent
K>>    if (WSAEventSelect (socket, event, flags) == SOCKET_ERROR)
K>>    {
K>>        return false;
K>>    }

K>>    m_map[event] = socket; // добавляем в std::map<WsaEvent, Socket>
K>>    return true; 
K>>


B>Конструктор, конструктор копирования и деструктор WsaEvent в студию.



class WsaEvent {
private :
    boost::shared_ptr<void> m_event;

public :
    WsaEvent (WSAEVENT event = WSA_INVALID_EVENT);
    ~WsaEvent() {;}

    bool reset (void);
    operator WSAEVENT() const { return m_event.get(); }
};

WsaEvent::WsaEvent (WSAEVENT event) : m_event (event, WSACloseEvent)
{
    if (event != WSA_INVALID_EVENT) return;

    event = WSACreateEvent();
    if (event == WSA_INVALID_EVENT)
    {
        SocketDebug ("WsaEvent::WsaEvent (WSACreateEvent)");
        throw std::exception ("Ошибка создания события");
    }

    m_event.reset (event, WSACloseEvent);
}

bool WsaEvent::reset (void)
{
    if (!WSAResetEvent (*this))
    {
        SocketDebug ("WsaEvent::reset (WSAResetEvent)");
        return false;
    }
    return true;
}
Re: WSAWaitForMultipleEvents
От: Adios  
Дата: 28.01.08 20:17
Оценка:
K> SOCKET socket = WSASocket ( protocol, type, 0, NULL, 0, 0);

K> sockaddr_in address = {0};

K> address.sin_family = protocol;
K> address.sin_port = htons (port);
K> address.sin_addr.s_addr = INADDR_ANY;

K> OnSelect (socket, FD_ACCEPT | FD_CLOSE);

K> bind (socket, (sockaddr*) &address, sizeof (address));
K> listen (socket, 5);

Может я ошибаюсь, но в строчке "OnSelect (socket, FD_ACCEPT | FD_CLOSE);" ты явно указываеш флаги событий которые будут срабатывать. Попробуй поставить туда FD_READ и FD_CLOSE

P.S. Сам занимаюсь подобной проблемой. Очень понравился код — респект.
Re: WSAWaitForMultipleEvents
От: Adios  
Дата: 28.01.08 23:08
Оценка:
Здравствуйте, k732, Вы писали:

K>В чем может быть проблемма ?


знаю что дата уже давно просрочена . И всё таки истина должна востаржествовать.
Проблема в том что нельзя просто копировать HANDLE и сокеты. Они будут работать только в единственной копии. А сдесь же:

K>m_map[event] = socket;

K>WsaEvent event = m_events [index — WSA_WAIT_EVENT_0];
и т.д.

при замене таких копий на операции взятия адреса или просто исключая их, всё начинает работать. проверил.
Re[2]: WSAWaitForMultipleEvents
От: AlexCrush Россия  
Дата: 30.01.08 12:28
Оценка:
Здравствуйте, Adios, Вы писали:


A>знаю что дата уже давно просрочена . И всё таки истина должна востаржествовать.

A>Проблема в том что нельзя просто копировать HANDLE и сокеты. Они будут работать только в единственной копии. А сдесь же:

Неправда. Нельзя копировать хендлы между потоками. Но указатели тоже нельзя. А так — хендл и дескриптор сокета — это просто число, которое хоть закопируйся. Причем, хендл даже не в курсе что его скопировали.

A>при замене таких копий на операции взятия адреса или просто исключая их, всё начинает работать. проверил.

Мистика. В чем то Вы явно не разобрались.
Re[3]: WSAWaitForMultipleEvents
От: Аноним  
Дата: 09.02.08 11:33
Оценка:
Здравствуйте, AlexCrush, Вы писали:

AC>Мистика. В чем то Вы явно не разобрались.


В данном вопросе разобрался полностью. Написал рабочую обёртку, при чём с динамическим кол-вом серверов и входящих клиентов, а так же клиентов кот. подключаються к другим серверам.
Re[2]: WSAWaitForMultipleEvents
От: Аноним  
Дата: 09.02.08 11:41
Оценка:
B>ЗЫ. Разбираться в таком коде не имея всех исходников — достаточно сложно. Никогда не понимал, для чего нужно делать обёртки для системных вызовов и типов, когда их можно использовать напрямую. При чём, какой-то выгоды именно для этого кода обёртки не дают, IMHO.
Пример №1 — написал я както FTP клиента. А потом за один час обучил его работать по FTPS юзая OpenSSL, просто написав вторую имплементацию интерфайса транспорта.
Пример №2 — про test-driven development слышали? Так вот чтобы оттестировать код юзающий АПИ системы, требуется писать обертки вокруг АПИ, которые в случае прогона тестов просто эмулируют поведение АПИ.
Re[4]: WSAWaitForMultipleEvents
От: AlexCrush Россия  
Дата: 12.02.08 06:14
Оценка:
Здравствуйте, Аноним, Вы писали:

А>В данном вопросе разобрался полностью.

И что? Всё еще утверждаете, что нельзя копировать сокетный дескриптор, который в винде объявлен как

typedef u_int           SOCKET;

а в юниксах так и вообще принято просто интом его передавать
?
Или Вы уже о другом?
Я утверждаю что копировать дескрипторы можно ничуть не хуже чем инты. Разумеется, если речь идет о копировании внутри одного процесса
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.