Клиент-серверный чат с использованием сокетов (эхочат) сокеты windows

Forums:

Код сервера:

// Пример простого TCP – эхо сервера

#include "stdafx.h"
#include < iostream >
#pragma comment( lib, "ws2_32.lib" )
#include < Windows.h >
#include < conio.h > 
using namespace std;

  #define MY_PORT    666 // Порт, который слушает сервер

 // заведём глобальную переменную  ниже для обмена данным между нитями сервера   
char* cbuff= "common buffer"; // общий буфер для записи сообщений от всех клиентов
           

  // макрос для печати количества активных
  // пользователей 
  #define PRINTNUSERS if (nclients)\
  printf("%d user on-line\n",nclients);\
  else printf("No User on line\n");

  // прототип функции, обслуживающий
  // подключившихся пользователей
  DWORD WINAPI WorkWithClient(LPVOID client_socket);

  //будет просто циклически проверять знкачение глобальной переменной
  DWORD WINAPI CheckCommonBuffer(LPVOID client_socket); 

  // глобальная переменная – количество
  // активных пользователей 
  int nclients = 0;

  int main(int argc, char* argv[])
  {
    char buff[1024];    // Буфер для различных нужд

    printf("TCP SERVER DEMO\n");

    // Шаг 1 - Инициализация Библиотеки Сокетов
    // Т.к. возвращенная функцией информация
    // не используется ей передается указатель на
    // рабочий буфер, преобразуемый
    // к указателю  на структуру WSADATA.
    // Такой прием позволяет сэкономить одну
    // переменную, однако, буфер должен быть не менее
    // полкилобайта размером (структура WSADATA
    // занимает 400 байт)
    if (WSAStartup(0x0202,(WSADATA *) &buff[0])) 
    {
      // Ошибка!
	      printf("Error WSAStartup %d\n",
             WSAGetLastError());
      return -1;
    }

    // Шаг 2 - создание сокета
    SOCKET mysocket;
    // AF_INET     - сокет Интернета
    // SOCK_STREAM  - потоковый сокет (с
    //      установкой соединения)
    // 0      - по умолчанию выбирается TCP протокол
    if ((mysocket=socket(AF_INET,SOCK_STREAM,0))<0)
    {
      // Ошибка!
      printf("Error socket %d\n",WSAGetLastError());
      WSACleanup();
        // Деиницилизация библиотеки Winsock
      return -1;
    }

    // Шаг 3 связывание сокета с локальным адресом
    sockaddr_in local_addr;
    local_addr.sin_family=AF_INET;
    local_addr.sin_port=htons(MY_PORT);
             // не забываем о сетевом порядке!!!
    local_addr.sin_addr.s_addr=0;
             // сервер принимает подключения
             // на все IP-адреса

    // вызываем bind для связывания
    if (bind(mysocket,(sockaddr *) &local_addr,
                sizeof(local_addr)))
    {
      // Ошибка
      printf("Error bind %d\n",WSAGetLastError());
      closesocket(mysocket);  // закрываем сокет!
      WSACleanup();
      return -1;
    }

    // Шаг 4 ожидание подключений
    // размер очереди – 0x100
    if (listen(mysocket, 20))
    {
      // Ошибка
      printf("Error listen %d\n",WSAGetLastError());
      closesocket(mysocket);
      WSACleanup();
      return -1;
    }

    printf("Waiting for connection\n");

    // Шаг 5 извлекаем сообщение из очереди
    SOCKET client_socket;    // сокет для клиента
    sockaddr_in client_addr;    // адрес клиента
              // (заполняется системой)

    // функции accept необходимо передать размер
    // структуры
    int client_addr_size=sizeof(client_addr);

    // цикл извлечения запросов на подключение из
    // очереди
	/* accept - держит управление и не даёт циклу вращаться
	(то есть не даёт потоку- нити выполняться вообще)
	пока не поступит очередной запрос на соединение*/
    while((client_socket = accept(mysocket, (sockaddr *)
            &client_addr, &client_addr_size)))
    {
      nclients++;      // увеличиваем счетчик
              // подключившихся клиентов

      // пытаемся получить имя хоста
      HOSTENT *hst;
      hst=gethostbyaddr((char *)
          &client_addr.sin_addr.s_addr,4, AF_INET);

      // вывод сведений о клиенте
      printf("+%s [%s] new connect!\n",
      (hst)?hst->h_name:"",
      inet_ntoa(client_addr.sin_addr));
      PRINTNUSERS

      // Вызов нового потока для обслужвания клиента
      // Да, для этого рекомендуется использовать
      // _beginthreadex но, поскольку никаких вызов
      // функций стандартной Си библиотеки поток не
      // делает, можно обойтись и CreateThread
      DWORD thID;
      CreateThread(NULL,NULL,WorkWithClient,
              &client_socket,NULL,&thID);

		/* запускаем функции прослушивания чужих сообщений
		нам нужен именно отдельный поток, потому что recv()
		забирает управление и не позволит послать клиенту 
		сообщение пока от этого самого ("родного" для данной
		нити сервера) клиента что-нибудь не придёт */
	  // CreateThread(NULL,NULL, CheckCommonBuffer, &client_socket,NULL,&thID);
    }
    return 0;
  }



  // Эта функция создается в отдельном потоке и
  // обсуживает очередного подключившегося клиента
  // независимо от остальных
  DWORD WINAPI WorkWithClient(LPVOID client_socket)
  {
    SOCKET my_sock;
    my_sock=((SOCKET *) client_socket)[0];
    char buff[1024] = "123124";
//	char buff2[20*1024];
    #define sHELLO "Hello, new member! This's our win socket chat!))\r\n"
	 //printf(" \n new thread is started connect!\n");

    // отправляем клиенту приветствие 
    send(my_sock, sHELLO, sizeof(sHELLO), 0);

	
	
	// цикл эхо-сервера: прием строки от клиента и
       int bytes_recv;
    // возвращение ее клиенту
    while ( (bytes_recv=recv(my_sock,&buff[0],sizeof(buff),0)) && (bytes_recv !=SOCKET_ERROR))
	// while (1)
	 {
		//bytes_recv = recv(my_sock,buff,sizeof(buff),0);
		cbuff = buff; // пишем в глобальную переменную чтобы другие участники
		// узнали о сообщении
		send(my_sock,&buff[0],bytes_recv,0);
	

		/*давайте организуем в консоли сервера общий чат-
		будем выводить туда все сообщение приходящие отразных клиентов-
		для этого просто используем printf*/
		cout << buff << std::endl; // печатает сообщения всех клиентов в консоли сервера
	 }

    // если мы здесь, то произошел выход из цикла по
    // причине возращения функцией recv ошибки –
    // соединение клиентом разорвано
    nclients--; // уменьшаем счетчик активных клиентов
    printf("-disconnect\n"); PRINTNUSERS

    // закрываем сокет
    closesocket(my_sock);
    return 0;
  }

  /*эту функцию надо допилить так как есть проблемы 
  с передаваемым и принимаемы м размерами буфера*/
  DWORD WINAPI CheckCommonBuffer(LPVOID client_socket)
  {

	SOCKET my_sock;
    my_sock=((SOCKET *) client_socket)[0];
	char buf[1024]; // для контроля размерности буфера
	while (1)
	{
		/* изменение этого значения - признак того, что пришло новое сообщение
						   buff является глобальной и поправить её может любая нить сервера -
						   просто проверяем значение первого байта(это не слишком надёжный способ)*/
		if (cbuff[0] != 9) 

		{ 
			//char buf[1024] =cbuff ;
			send(my_sock, cbuff, sizeof(cbuff), 0);
			cbuff[0] = 9;
			//Sleep(2000);
		}
		/*давайте организуем в консоли сервера общий чат-
		будем выводить туда все сообщение приходящие отразных клиентов-
		для этого просто используем printf*/
		//cout << buff << std::endl; // печатает сообщения всех клиентов в консоли сервера
	}
  }



Код клиента: