Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Клиент-серверное приложение


Автор: Alexey_2007 30.1.2007, 22:23
Может ли мне кто-нибудь подкинуть пример работы с WinSock2. Смотрел в разделе исходники, но не нашел. 

Мне нужно именно клиент-серверное приложение, такое чтобы сервер мог получать информацию от несольких клиентов(что очевидно... но у меня что то не получается именно так) TCP\IP.

Если вместе с этим будут каким-либо образом прикручены диалоги - это будет вообще здорово!!!



Автор: witex 31.1.2007, 02:26
http://www.citforum.ru/book/cook/winsock.shtml и исходники и маленькое пособие! Только вот ты разделом ошибся! Всё таки сетевое программирование!

Автор: Alexey_2007 31.1.2007, 15:47
thanks!!

Автор: kleks 2.5.2007, 15:43
У меня возник вопрос....по поводу модели "клиент-сервер"....предположим у меня есть сервер и два клиента подключившиеся к этому серверу, как мне организовать банальный чат между этими клиентами??? на примере клиента и сервера описанные по предыдущей ссылке. Т.е. как мне организовать связь между двумя потоками??

Автор: -Kp0T- 2.5.2007, 18:40
Vivat Крис Касперский, но я тебе не отвечу на вопрос.
Когда знакомился с winsock библиотекой тоже обдумывал реализацию своей задачи в мультипоточности с блокирующими сокетами, но это не лучшее решение, ибо возрастает сложность  программирования.
Более гибкое решение все таки с сокетами, работающими в асинхронном режиме.

Привожу пример эхо-сервера c обоработкой CTRL+BREAK, CTRL+C, VK_RETURN. 
Ну про клиент думаю догадаешься :) ( telnet 127.0.0.1 7777 ).
[ упор на WSAAsyncSelect(), но его использование ведет к увиличению количества потоков на 1 ]

Код

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>

HINSTANCE hInst;
HWND hWnd;

LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);

char szAppName[]="Example";

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE,LPTSTR lpCmdLine,int nCmdShow)
{
    MSG msg;
    WNDCLASSEX    wndclass;

    wndclass.cbSize = sizeof(wndclass);
    wndclass.style    = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = 0;
    wndclass.hInstance    =    hInstance;
    wndclass.hIcon        =    LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor    =    LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground    =    (HBRUSH) GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName    =    NULL;
    wndclass.lpszClassName    =    szAppName;
    wndclass.hIconSm    =    LoadIcon(NULL, IDI_APPLICATION);

    RegisterClassEx(&wndclass);
    hWnd = CreateWindow(szAppName,"WSAAsyncSelect Example", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
    if (!hWnd)return FALSE;

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    
    WSADATA       wsd;

    if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
    {
        MessageBox(0, "Can't load WinSock", "Error", 0);
        return 0;
    }

    SOCKET        sServerListen,
                  sClient;
    struct sockaddr_in localaddr,
                       clientaddr;
    HANDLE        hThread;
    DWORD         dwThreadId;
    int           iSize;

    sServerListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if (sServerListen == SOCKET_ERROR)
    {
        MessageBox(0, "Can't load WinSock", "Error", 0);
        return 0;
    }

    ULONG ulBlock;
    ulBlock = 1;
    if (ioctlsocket(sServerListen, FIONBIO, &ulBlock) == SOCKET_ERROR)
    {
        return 0;
    }

    localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    localaddr.sin_family = AF_INET;
    localaddr.sin_port = htons(7777);

    if (bind(sServerListen, (struct sockaddr *)&localaddr, 
            sizeof(localaddr)) == SOCKET_ERROR)
    {
        MessageBox(0, "Can't bind", "Error", 0);
        return 1;
    }
    
    WSAAsyncSelect(sServerListen, hWnd, WM_USER+1, FD_ACCEPT);
    listen(sServerListen, 4);

    while (GetMessage(&msg, NULL, 0, 0)) 
    {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
    }

    closesocket(sServerListen);
    WSACleanup();
    return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    SOCKET ClientSocket;
    int ret;
    char szRecvBuff[1024], szSendBuff[1024];
    

    switch (message) 
    {
    case WM_USER+1:
        switch (WSAGETSELECTEVENT(lParam))
        {
        case FD_ACCEPT:
            ClientSocket = accept(wParam, 0, 0);
            WSAAsyncSelect(ClientSocket, hWnd, WM_USER+1, FD_READ | FD_WRITE | FD_CLOSE);
            break;

        case FD_READ:
            ZeroMemory(szRecvBuff,1024);
            ZeroMemory(szSendBuff,1024);
            ret = recv(wParam, szRecvBuff, 1024, 0);
            if (ret == 0)
                break;
            else if (ret == SOCKET_ERROR)
            {
                MessageBox(0, "Recive data filed", "Error", 0);
                break;
            }
            szRecvBuff[ret] = '\0';

            if(szRecvBuff[0]==0x03)closesocket(wParam);
            if(szRecvBuff[0]==0x0D){szRecvBuff[0]='~';szRecvBuff[1]='\0';}

            strncpy(szSendBuff,szRecvBuff,ret);
            szRecvBuff[ret] = '\0';
            ret=send(wParam, szSendBuff,ret, 0);
            break;

        case FD_WRITE://SOCKET READY  to send data
            break;

        case FD_CLOSE:
            closesocket(wParam);
            break;
    }
    case WM_COMMAND:
        wmId    = LOWORD(wParam); 
        wmEvent = HIWORD(wParam); 

    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);

        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

Автор: kleks 2.5.2007, 19:38
...конечно спасибо огромное, но я всё-таки так и не понял. Меня по сути интересует следующие:
Два клиента подключенны к одному серверу, как сделать так чтобы эти два клиента могли обмениваться между собой информацией!!!??? 
Вот вариант моего сервера...

Код

#include "stdafx.h"
#include <winsock2.h>
#include <iostream>

#pragma comment(lib,"ws2_32.lib")

DWORD WINAPI ConnThread(void* pParam)
{
SOCKET sockClient=(SOCKET)pParam;
SOCKADDR_IN addr;
int length=sizeof(addr);
getpeername(sockClient,(SOCKADDR*)&addr,&length);
printf("Client %s connected\n",inet_ntoa(addr.sin_addr));
char buf[256];
int result=1;
while((result=recv(sockClient,buf,sizeof(buf)-1,0))!=SOCKET_ERROR && result>0)
    {
    buf[result]=0;
    printf(buf);
    printf("\n");
    }
closesocket(sockClient);
printf("Client %s disconnected\n",inet_ntoa(addr.sin_addr));
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////

int main()
{
WORD wVersion;
WSADATA wsaData;
int result;
wVersion=MAKEWORD(1,1);
result=WSAStartup(wVersion,&wsaData);
if (result!=0)
    {
    printf("ERROR: Initialization failed.\n");
    return -1;
    }
SOCKET sockServer=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (sockServer==INVALID_SOCKET)
    {
    printf("ERROR: Cannot create a socket.\n");
    return -1;
    }
SOCKADDR_IN addr;
addr.sin_family=AF_INET;
addr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addr.sin_port=htons(1500);
result=bind(sockServer,(SOCKADDR*)&addr,sizeof(SOCKADDR_IN));
if (result!=0)
    {
    printf("ERROR: Cannot bind socket.\n");
    return -1;
    }
result=listen(sockServer,SOMAXCONN);
if (result!=0)
    {
    printf("ERROR: Cannot connect.\n");
    return -1;
    }
while(1)
    {
    SOCKET sockClient=accept(sockServer,NULL,NULL);
    if (sockClient!=INVALID_SOCKET)
        {
        DWORD dwID;
        HANDLE hThread=CreateThread(NULL,0,ConnThread,(void*)sockClient,0,&dwID);
        CloseHandle(hThread);
        }
    }
return 0;
}
/////////////////////////

Автор: -Kp0T- 4.5.2007, 02:02
Вообще не зря я тебе привел тут про асинхронные сокеты и WSAAsyncSelect(). Функция действительно мощная. Да ладно.
Как бы я поступил с твоим кодом.
- В пространстве глобальных переменных определил бы стек (как класс).
- Плохо то что поток замерз по функции recv и ждет данные или ошибку :) 
- Тем не менее можно данные о подключений клиентов (в частности нам надо SOCKET) вынести опять таки в globalnamespace или инкапсулировать в класс (как душе угодно)
- Пусть "потоки висят" в ожидании данных, их уже не спасешь. Чтоб срочно отослать данные (которые упали в стек) нужен другой поток (только чтобы отправить второму клиенту).

А теперь собственно программный код, тока на русском языке :)

Поток[i] принял данные по recv, положил в стек данные (что и от кого пришло), отправил сообщение оконной процедуре, [ например, WM_USER+1. Как мы помним, ОСь не использует сообщения выше WM_USER - они зарезервированы для потребителя :) ] и снова поток замерз в ожидании отклика функции recv.

Тем временем, как пришло сообщение WM_USER+1, оконная процедура извлекла данные из стека, решила что и кому слать по полученным данным и создает новый поток (или потоки - сам реши всем слать в одном потоке или в отдельном. - Случай более 2-х клиентов) который займется отправкой данных. После отправки поток должен завершиться с кодом доставки (ошибки).

Я бы тебе код набросал, да сроки поджимать начинают по своему проекту...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)