Windows server socket in C++
Chiariamo subito che parliamo di Visual C++, quindi il programma userà le librerie di Windows e non sarà usabile su altri sistemi (ho in programma di creare una versione per Linux).
Nello specifico useremo le librerie Winsock.
Iniziamo a creare un progetto in Visual Studio di tipo VC++ console; aggiungete anche le librerie MFC in fase di creazione.
MFC ci servirà per implementare il multi-thread!
Una volta creato il progetto aprite il file stdafx.h e aggiungete queste righe:
#include <WinSock2.h>
#include <conio.h>
using namespace std;
Così facendo avremo disponibile il tutto per tutto il progetto.
Dopo di che creiamo una nuova classe che chiameremo Server.
Nel file di intestazione ci sarà questo:
#pragma once
using namespace std;
class Server {
public:
Server();
virtual ~Server();
UINT initConnection();
void stopConnection();
private:
SOCKET server;
SOCKET inAscolto;
};
In pratica abbiamo aggiunto due funzioni (una per la connessione e una per la disconnessione) e due variabili private.
Fin qui direi nulla di difficile.
Vediamo adesso le due funzioni:
UINT Server::initConnection() {
cout << "SERVER STARTED. PRESS ENTER EXIT" << endl;
WSADATA wsaData;
sockaddr_in local;
sockaddr_in from;
int wsaret = WSAStartup(0x101, &wsaData);
int fromlen = sizeof(from);if(wsaret != 0)
{
return 0;
}local.sin_family = AF_INET; // ADDRESS FAMILY
local.sin_addr.s_addr = INADDR_ANY; // WILD CARD IP ADDRESS
local.sin_port = htons((u_short) 20248); // PORT
server = socket(AF_INET, SOCK_STREAM, 0);if (server == INVALID_SOCKET)
{
return 0;
}if (bind(server, (sockaddr*) & local, sizeof (local)) != 0)
{
return 0;
}if (listen(server, 10) != 0)
{
return 0;
}while(true)
{
char temp[512];
inAscolto = accept(server, (struct sockaddr*) &from, &fromlen);
sprintf_s(temp, "YOUR IP IS: %s", inet_ntoa(from.sin_addr));
send(inAscolto, temp, strlen(temp), 0);
cout << "CONNECTION FROM " << inet_ntoa(from.sin_addr) << endl;
}}
void Server::stopConnection() {
closesocket(inAscolto);
closesocket(server);
WSACleanup();
}
Su stopConnection() direi che non c'è molto da dire; disconnettiamo e ripuliamo.
Per il resto spiegare il tutto è un pò troppo......
In generale usiamo il protocollo TCP (e non l'UDP).
Il server sarà in ascolto sulla porta 20248.
Più sotto, il secondo oggetto SOCKET (inAscolto) ci serve per accettare le connessioni in ingresso.
Nel while con la funzione send() inviamo al client il suo indirizzo IP.
Più sotto invece stampiamo, lato server, chi si è connesso.
A questo punto guardiamo il metodo main, che troviamo nel SocketWindows.cpp (il file ha lo stesso nome del progetto).
Considerando che la struttura generale viene creata di default, vediamolo nel complesso:
#include "stdafx.h"
#include "SocketWindows.h"
#include "Server.h"
#pragma comment(lib, "ws2_32.lib")#ifdef _DEBUG
#define new DEBUG_NEW
#endifCWinApp theApp;
UINT initConnectionThreadFunc(LPVOID param)
{
Server *obj = static_cast<Server *>(param);
return obj->initConnection();
}int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;HMODULE hModule = ::GetModuleHandle(NULL);
if (hModule != NULL)
{
// inizializza MFC e visualizza un messaggio in caso di errore
if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
{
// TODO: modificare il codice di errore in base alle esigenze
_tprintf(_T("Errore irreversibile: inizializzazione di MFC non riuscita\n"));
nRetCode = 1;
}
else
{
Server s;
AfxBeginThread(initConnectionThreadFunc, (LPVOID) &s);
if(_getch() == 13)
{
s.stopConnection();
}
}
}
else
{
// TODO: modificare il codice di errore in base alle esigenze.
_tprintf(_T("Errore irreversibile: GetModuleHandle non riuscito\n"));
nRetCode = 1;
}return nRetCode;
}
Prima di tutto aggiungete la riga #pragma comment(lib, "ws2_32.lib"); questo ci serve per linkare le librerie Winsock.
Poi abbiamo una funzione initConnectionThreadFunc().
Questa la richiameremo più sotto nel metodo main, e ci serve per implementare il multi-threading.
Per questo tip ringrazio il forum di HTML.it.
Nel metodo main, superati i controlli, ci interessano queste righe:
Server s;
AfxBeginThread(initConnectionThreadFunc, (LPVOID) &s);
if(_getch() == 13)
{
s.stopConnection();
}
Come vedete nella funzione AfxBeginThread() richiamo il metodo di cui sopra, e l'oggetto Server, che in effetti è quello da richiamare.
La spiegazione di ciò è abbastanza complicata, e vi consiglio di guardare al link del forum postato sopra.
Sotto semplicemente controlliamo che non venga spinto il tasto ENTER; nel caso richiamiamo la funzione per lo stop della connessione.
Per testarlo avviate il programma (anche da Visual Studio) e poi aprite un terminale / prompt e digitate:
telnet indirizzo_ip porta
Quindi se state sulla stessa macchina e avete la porta 20248:
telnet localhost 20248
Controllate gli output.
c++ c++ visual c++ vc++ socket winsock afxbe
Commentami!