19
Tags:
c++
Skrevet af
Bruger #2695
@ 09.03.2004
Introduktion
Så skal vi igen til at kode noget netværk. Denne gang skal vi kigge på server programmering, og som sædvanligt bygger vi videre på det projekt, vi har arbejdet med hidtil.
En server er et program, som tager imod indkommende opkald fra en klient. Derefter kommunikerer serveren og klienten indtil, forbindelsen afbrydes. En server kan godt servicere flere klienter samtidigt, og det kigger vi også på. Så er det jo rart, at vi allerede har kigget på multithreading, for det får vi brug for. Vi vil også synkronisere adgangen til klient objekterne, og det gør vi som beskrevet i artiklen om tråd synkronisering. Hvis du ikke har læst artiklerne om multithreading og tråd synkronisering, så kom i gang!!! Og tilføj Thread og Mutex klasserne til projektet.
En echo server
Projektet denne gang bliver en slags echo server. Traditionelt er en echo server et program, du forbinder til, siger noget til, og får det samme tilbage, hvorefter forbindelsen lukkes. De bruges ofte til tests.
Vores echo server tillader flere forbindelser samtidig og lukker først forbindelsen, når teksten "GOODBYE" modtages. Selve serveren lukkes når teksten "CLOSEDOWN" modtages.
Der er faktisk ikke mange nye funktioner at lære, fordi en server faktisk også er en socket. Man forbinder bare ikke sin socket nogen steder hen men binder den til en adresse og en port, som man vil lytte på, definerer hvor mange klienter, der skal være plads til i operativ systemets kø, og derefter kan man så acceptere klienter. Det gøres med følgende funktionskald:
int bind(int sockfd, struct sockaddr * my_addr, socklen_t addrlen);
int listen(int s, int backlog);
int accept(int s, struct sockaddr * addr, socklen_t * addrlen);
Man bruger
bind til at fortælle, hvilken netforbindelse og hvilken port, man vil lytte på. Hvis man kun vil acceptere forbindelser fra ét ud af flere netkort, så specificerer man IP adressen på dette netkort i
my_addr. Ellers kan man specificere IN_ADDR_ANY for at tillade forbindelser fra alle netkort (inklusive loopback adressen 127.0.0.1).
listen opretter en kø i operativ systemet. Når en klient forbinder til din server, bliver han først lagt i denne kø, medmindre den er fuld. Så når dit server software har accepteret klienten (med
accept), så fjernes han fra køen igen. Det betyder, at man ikke behøver at oprette en kø på 100, for at kunne servicere 100 klienter samtidig. 10 burde være mere end nok.
accept fjerner en klient fra køen og returnerer en socket, som kan kommunikere med denne klient. Denne funktion blokerer, men vi kan bruge
select til at vente, og det gør vi på samme måde som i vores
TCPSocket klasse.
Jeg har midliertidigt gjort den private constructor i
TCPSocket klassen public for at kunne oprette objekter, som vores
TCPServerSocket skal gøre det senere.
Vi prøver engang:
//test.cpp
#include "Net.h"
#include "Thread.h"
#include "TCPSocket.h"
#include "Exception.h"
#include "TimerException.h"
#include "ThreadException.h"
#include "Mutex.h"
#include <cstdlib>
#include <vector>
#if defined(__linux__)
#include <unistd.h>
extern int h_errno;
#endif
using namespace std;
Mutex mutex; //Bruges til at synkronisere adgangen til klienterne
//Forward declaration
class Server;
class Client : public Thread
{
private:
TCPSocket * m_socket;
Server * m_owner;
public:
Client(TCPSocket * sock, Server * owner);
virtual ~Client();
void run();
static bool s_running;
};
bool Client::s_running = true;
typedef vector<Client*> ClientList;
class Server
{
private:
UInt16 m_port;
ClientList m_clients;
public:
Server(UInt16 port);
void run();
void removeClient(Client * client);
};
Client::Client(TCPSocket * sock, Server * owner)
: m_socket(sock), m_owner(owner)
{
m_socket->setTimeOut(1000);
}
Client::~Client()
{
if(m_socket != NULL)
{
delete m_socket;
m_socket = NULL;
}
}
void Client::run()
{
//Buffer til at indeholde data fra klienten
char buffer[1024];
//Antallet af læste bytes
SInt32 bytesRead;
try
{
//Så længe vi kører...
while(Client::s_running)
{
try
{
//Læs en masse data
bytesRead = m_socket->read(buffer,sizeof(buffer));
//Strengen indeholder to bytes linjeskift
string whatHeSaid(buffer,0,bytesRead - 2);
//Fortæl hvad der blev sagt
cout << (string)(*m_socket->getAddress()) << " sagde '" << whatHeSaid << "'" << endl;
if(whatHeSaid == string("GOODBYE"))
{
//Luk forbindelsen ned hvis han sagde "GOODBYE"
break;
}
if(whatHeSaid == string("CLOSEDOWN"))
{
//Luk hele serveren ned hvis han sagde "CLOSEDOWN"
Client::s_running = false;
}
//Send klientens data tilbage
m_socket->write(buffer,bytesRead);
} catch (TimerException & te)
{
//Vi har ikke fået data i 1 sekund
//Vi tjekker om vi skal fortsætte
}
}
} catch (Exception & ex)
{
}
//Luk forbindelsen
m_socket->close();
//Fjern os fra serverens klient liste
m_owner->removeClient(this);
}
Server::Server(UInt16 port)
: m_port(port)
{
}
void Server::run()
{
//Bruges til at indeholde adresse og port som vi vil binde til
struct sockaddr_in local_addr;
//Opret vores server socket
int server = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
#if defined(__linux__)
if(server == -1)
#elif defined(_WIN32)
if(server == INVALID_SOCKET)
#endif
{
//Det kunne vi ikke, så fortæl at det gik galt
cout << "Kunne ikke starte server." << endl;
//Og returnér
return;
}
//Nulstil adressen som vi vil lytte på
memset(&local_addr, 0, sizeof(local_addr));
//Adressen er af typen AF_INET (Internet adresse)
local_addr.sin_family = AF_INET;
//Specificér porten, som vi vil lytte på
local_addr.sin_port = htons(m_port);
//Specificér at vi vil lytte på alle netkort
local_addr.sin_addr.s_addr = INADDR_ANY;
//Bind vores server socket til adressen og porten
if(bind(server,(struct sockaddr*)&local_addr,sizeof(local_addr)) != 0)
{
//Det kunne vi ikke, så frigiv vores socket
#if defined(__linux__)
close(server);
#elif defined(_WIN32)
closesocket(server);
#endif
//Fortæl at det gik galt
cout << "Kunne ikke binde." << endl;
//...og returnér
return;
}
//Specificér at operativ systemets kø skal kunne indeholder 10 klienter
if(listen(server,10) != 0)
{
//Det kunne vi ikke, så frigiv vores socket
#if defined(__linux__)
close(server);
#elif defined(_WIN32)
closesocket(server);
#endif
//Fortæl at det gik galt
cout << "Kunne ikke oprette kø." << endl;
//...og returnér
return;
}
struct sockaddr_in client_addr;//Til at indeholde adressen på klienten
fd_set set;//Til select kaldet
struct timeval timeout;//Hvor længe vi tjekker efter klienter
int err;//Til at indeholde fejlkoder
#if defined(__linux__)
socklen_t len = sizeof(client_addr);
#elif defined(_WIN32)
int len = sizeof(client_addr);
#endif
while(Client::s_running)
{
timeout.tv_sec = 1;//Vent 1 sekund på en klient
timeout.tv_usec = 0;//..og ingen mikrosekunder
FD_ZERO(&set);//Nulstil sættetwmsd
FD_SET(server,&set);//Tilføj vores server socket
err = select(FD_SETSIZE,&set,NULL,NULL,&timeout);//Vent et sekund på klienter
if(err == 0)continue; //Vi har timeout
else if(err == -1)break; //Der er sket en fejl
//Ellers har vi en klient
int client = accept(server,(struct sockaddr*)&client_addr,&len);
if(client != 0)
{
//Opret et nyt TCPSocket objekt til at servicere klienten
TCPSocket * c = new TCPSocket(client,client_addr);
//Fortæl hvem vi fik forbindelse fra
cout << "Fik forbindelse fra: " << (string)(*c->getAddress()) << endl;
//Opret et Client objekt til klienten
Client * cl = new Client(c,this);
try
{
//Start tråden
cl->start();
//Dette er en kritisk sektion, så vi synkroniserer
mutex.enter();
//Tilføj klienten til listen
m_clients.push_back(cl);
//Og forlad den kritiske sektion igen
mutex.leave();
} catch (ThreadException & te)
{
delete cl;
}
}
}
//Frigiv vores server socket
#if defined(__linux__)
close(server);
#elif defined(_WIN32)
closesocket(server);
#endif
}
void Server::removeClient(Client * client)
{
//Dette er en kritisk sektion, så vi synkroniserer
mutex.enter();
for(ClientList::iterator ite = m_clients.begin(); ite != m_clients.end(); ite++)
{
//Hvis dette er klienten, som vi leder efter
if((*ite) == client)
{
//Så fjern den fra listen
m_clients.erase(ite);
//Og deallokér den
delete client;
//Og stop med at lede
break;
}
}
//Og forlad den kritiske sektion igen
mutex.leave();
}
int main(int argc, char ** argv)
{
try
{
if(argc != 2)
{
cout << "Brug: " << argv[0] << " <port>" << endl;
return 0;
}
netInit();
//Find porten, som vi vil lytte på
UInt16 port = (UInt16)atoi(argv[1]);
//Start serveren på denne port
Server ser(port);
//Server klassen er IKKE et Thread objekt så den kører i main tråden
ser.run();
netRelease();
} catch (Exception & e)
{
cout << e << endl;
}
return 0;
}
Føj for en masse kode. Godt vi skal pakke det ind senere og så aldrig se på det mere. Følgende billede viser et screenshot, hvor jeg har serveren kørende i øverste billede og telnetter med tre klienter. Første klient siger først "Hej, Verden" efterfulgt af "GOODBYE", hvorefter forbindelsen til ham bliver lukket. Anden klient siger "Hej fra mig" efterfulgt af "CLOSEDOWN", hvorefter forbindelsen til både ham og den sidste klient lukkes, og serveren lukkes ned.
Hvis du bruger Windows' version af telnet vil du måske få et problem, for den sender hvert tegn for sig. Du kan downloade Putty fra
http://www.chiark.greenend.org.uk/~sgtatham/putty/. Det er en telnet (og meget andet) klient til Windows. Åben den, udfyld "Host Name (or IP address) feltet, sæt kryds i "Telnet" og skriv porten. Skift derefter til "Connection->Telnet" vinduet og sæt kryds i "Passive" under "Telnet negotiation mode" og tryk på "Open". Så skulle du få samme funktionalitet.
En TCPServerSocket klasse
Nu kan du godt gøre constructoren i
TCPSocket privat igen, for nu kommer kommer
TCPSocket's friend klasse, som jo kan bruge de private dele.
Vi vil straks gå videre med udviklingen af en klasse, til at indkapsle al denne funktionalitet. Jeg har defineret følgende interface til klassen, som jeg har valgt at kalde
TCPServerSocket:
//TCPServerSocket.h
#if !defined(TCPSERVERSOCKET_H)
#define TCPSERVERSOCKET_H
#include "TCPSocket.h"
#include "IPAddress.h"
#include "Types.h"
#include <string>
using namespace std;
class TCPServerSocket
{
public:
//Opretter et TCPServerSocket som kan lytte på alle adresser
//og på den angivne port og med det givne timeout
TCPServerSocket(UInt16 port, UInt32 timeOut = 0);
//Opretter et TCPServerSocket som kun lytter på adressen
//angivet som en streng og på den angivne port og med det givne timeout
TCPServerSocket(string adr, UInt16 port, UInt32 timeOut = 0);
//Opretter et TCPServerSocket som kun lytter på adressen
//angivet som en IPAddress og på den angivne port og med det givne timeout
TCPServerSocket(IPAddress adr, UInt16 port, UInt32 timeOut = 0);
//Deallokerer og lukker evt. serveren
~TCPServerSocket();
//Lukker serveren. En lukket server kan ikke sættes til at lytte igen.
void close();
//Sætter serveren til at lytte
void listen();
//Returnerer en dynamisk allokeret TCPSocket til en klient som har forbundet.
//Hvis 'm_timeOut' er større end 0 ventes dette antal
//millisekunder eller til der er en klient. Udløber
//tiden kastes en TimerException.
TCPSocket * accept();
//Sætter et nyt timeout som virker fra næste gang vi kalder 'accept()'
//Der ventes 'timeOut' millisekunder hvis 'timeOut' > 0. Ellers ventes
//til der er en klient
void setTimeOut(UInt32 timeOut);
//Returnerer timeout
UInt32 getTimeOut();
//Returnerer porten, der lyttes på
UInt16 getPort();
private:
//Opretter vores server socket og binder den til den angivne adresse og port
void makeAndBind(struct in_addr address);
//Denne funktion blokerer i 'm_timeOut' millisekunder, eller til
//der er en klient
void waitForClient();
//Porten som vi lytter på
UInt16 m_port;
//Antallet af millisekunder vi skal vente på blokerende kald.
//Hvis denne er 0, venter vi evigt.
UInt32 m_timeOut;
//Vores server socket
int m_serverSocket;
};
#endif
Her er implementeringen:
//TCPServerSocket.cpp
#include "TCPServerSocket.h"
#include "NetException.h"
#include "TimerException.h"
#if defined(__linux__)
#include <unistd.h>
#elif defined(_WIN32)
#include <winsock2.h>
#endif
TCPServerSocket::TCPServerSocket(UInt16 port, UInt32 timeOut)
: m_port(port), m_timeOut(timeOut), m_serverSocket(0)
{
//Bind til alle adresser og den givne port
struct in_addr adr = {INADDR_ANY};
makeAndBind(adr);
}
TCPServerSocket::TCPServerSocket(string adr, UInt16 port, UInt32 timeOut)
: m_port(port), m_timeOut(timeOut), m_serverSocket(0)
{
//Slå adressen op
IPAddress address(adr);
//Bind til adressen og den givne port
makeAndBind(address.getAddress(0));
}
TCPServerSocket::TCPServerSocket(IPAddress adr, UInt16 port, UInt32 timeOut)
: m_port(port), m_timeOut(timeOut), m_serverSocket(0)
{
//Bind til adressen og den givne port
makeAndBind(adr.getAddress(0));
}
TCPServerSocket::~TCPServerSocket()
{
//Frigiv vores socket
close();
}
void TCPServerSocket::close()
{
if(m_serverSocket != 0)
{
#if defined(__linux__)
::close(m_serverSocket);
#elif defined(_WIN32)
closesocket(m_serverSocket);
#endif
m_serverSocket = 0;
}
}
void TCPServerSocket::listen()
{
if(m_serverSocket != 0)
{
//En kø på 10 burde være nok
if(::listen(m_serverSocket,10) != 0)
{
//Det kunne vi ikke, så frigiv vores socket
close();
//Fortæl at det gik galt
#if defined(__linux__)
throw NetException(strerror(h_errno));
#elif defined(_WIN32)
LPVOID lpMsgBuf;
int err = WSAGetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
string description((char*)lpMsgBuf);
LocalFree( lpMsgBuf );
throw NetException(description);
#endif
}
}
}
TCPSocket * TCPServerSocket::accept()
{
TCPSocket * client;
if(m_serverSocket != 0)
{
//Vent på en klient eller et timeout
waitForClient();
struct sockaddr_in client_addr;//Til at indeholde adressen på klienten
#if defined(__linux__)
socklen_t len = sizeof(client_addr);
#elif defined(_WIN32)
int len = sizeof(client_addr);
#endif
//Acceptér klienten
int c = ::accept(m_serverSocket,(struct sockaddr*)&client_addr,&len);
if(client != 0)
{
//Opret en dynamisk allokeret TCPSocket
client = new TCPSocket(c,client_addr);
}
}
return client;
}
void TCPServerSocket::setTimeOut(UInt32 timeOut)
{
m_timeOut = timeOut;
}
UInt32 TCPServerSocket::getTimeOut()
{
return m_timeOut;
}
UInt16 TCPServerSocket::getPort()
{
return m_port;
}
void TCPServerSocket::makeAndBind(struct in_addr address)
{
struct sockaddr_in local_addr;
m_serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
#if defined(__linux__)
if(m_serverSocket == -1)
#elif defined(_WIN32)
if(m_serverSocket == INVALID_SOCKET)
#endif
{
//Det kunne vi ikke, så fortæl at det gik galt
#if defined(__linux__)
throw NetException(strerror(h_errno));
#elif defined(_WIN32)
LPVOID lpMsgBuf;
int err = WSAGetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
string description((char*)lpMsgBuf);
LocalFree( lpMsgBuf );
throw NetException(description);
#endif
}
//Nulstil adressen som vi vil lytte på
memset(&local_addr, 0, sizeof(local_addr));
//Adressen er af typen AF_INET (Internet adresse)
local_addr.sin_family = AF_INET;
//Specificér porten, som vi vil lytte på
local_addr.sin_port = htons(m_port);
//Specificér at vi vil lytte på alle netkort
local_addr.sin_addr = address;
//Bind vores server socket til adressen og porten
if(bind(m_serverSocket,(struct sockaddr*)&local_addr,sizeof(local_addr)) != 0)
{
//Det kunne vi ikke, så frigiv vores socket
close();
//Fortæl at det gik galt
#if defined(__linux__)
throw NetException(strerror(h_errno));
#elif defined(_WIN32)
LPVOID lpMsgBuf;
int err = WSAGetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
string description((char*)lpMsgBuf);
LocalFree( lpMsgBuf );
throw NetException(description);
#endif
}
}
void TCPServerSocket::waitForClient()
{
fd_set set;
struct timeval timeout;
int err;
FD_ZERO(&set);
FD_SET(m_serverSocket,&set);
//Hvis m_timeOut er større end 0
if(m_timeOut > 0)
{
//Specificér hvor lang tid vi skal vente
timeout.tv_sec = (unsigned int)(m_timeOut / 1000);
timeout.tv_usec = (unsigned int)(m_timeOut % 1000) * 1000;
err = select(FD_SETSIZE,&set,NULL,NULL,&timeout);
}
else
{
//Vent til der er data eller til forbindelsen bliver lukket
err = select(FD_SETSIZE,&set,NULL,NULL,NULL);
}
//Hvis 'err' er 0 er tiden gået
if(err == 0)
{
throw TimerException("Timer timed out");
}
//Eller hvis 'err' er -1, er der gået noget galt
else if(err == -1)
{
#if defined(__linux__)
throw NetException(strerror(h_errno));
#elif defined(_WIN32)
LPVOID lpMsgBuf;
int err = WSAGetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
string description((char*)lpMsgBuf);
LocalFree( lpMsgBuf );
throw NetException(description);
#endif
}
}
Revideret echo server
Nu er det så på tide at skrive vores echo server om, så den bruger vores TCPServerSocket klasse. Den første implementering var på 277 linjer mens den nye er på 202 linjer og lidt lettere at følge med i. Ændringen er udelukkende i include delen og i
Server-klassens
run() metode:
//test.cpp
#include "Net.h"
#include "Thread.h"
#include "TCPSocket.h"
#include "TCPServerSocket.h"
#include "Exception.h"
#include "TimerException.h"
#include "ThreadException.h"
#include "Mutex.h"
#include <cstdlib>
#include <vector>
using namespace std;
Mutex mutex; //Bruges til at synkronisere adgangen til klienterne
//Forward declaration
class Server;
class Client : public Thread
{
private:
TCPSocket * m_socket;
Server * m_owner;
public:
Client(TCPSocket * sock, Server * owner);
virtual ~Client();
void run();
static bool s_running;
};
bool Client::s_running = true;
typedef vector<Client*> ClientList;
class Server
{
private:
UInt16 m_port;
ClientList m_clients;
public:
Server(UInt16 port);
void run();
void removeClient(Client * client);
};
Client::Client(TCPSocket * sock, Server * owner)
: m_socket(sock), m_owner(owner)
{
m_socket->setTimeOut(1000);
}
Client::~Client()
{
if(m_socket != NULL)
{
delete m_socket;
m_socket = NULL;
}
}
void Client::run()
{
//Buffer til at indeholde data fra klienten
char buffer[1024];
//Antallet af læste bytes
SInt32 bytesRead;
try
{
//Så længe vi kører...
while(Client::s_running)
{
try
{
//Læs en masse data
bytesRead = m_socket->read(buffer,sizeof(buffer));
//Strengen indeholder to bytes linjeskift
string whatHeSaid(buffer,0,bytesRead - 2);
//Fortæl hvad der blev sagt
cout << (string)(*m_socket->getAddress()) << " sagde '" << whatHeSaid << "'" << endl;
if(whatHeSaid == string("GOODBYE"))
{
//Luk forbindelsen ned hvis han sagde "GOODBYE"
break;
}
if(whatHeSaid == string("CLOSEDOWN"))
{
//Luk hele serveren ned hvis han sagde "CLOSEDOWN"
Client::s_running = false;
}
//Send klientens data tilbage
m_socket->write(buffer,bytesRead);
} catch (TimerException & te)
{
//Vi har ikke fået data i 1 sekund
//Vi tjekker om vi skal fortsætte
}
}
} catch (Exception & ex)
{
}
//Luk forbindelsen
m_socket->close();
//Fjern os fra serverens klient liste
m_owner->removeClient(this);
}
Server::Server(UInt16 port)
: m_port(port)
{
}
void Server::run()
{
try
{
//Opret en server med et sekunds timeout
TCPServerSocket server(m_port,1000);
//Begynd at lytte
server.listen();
while(Client::s_running)
{
try
{
//Skaf en klient
TCPSocket * c = server.accept();
//Fortæl hvem vi fik forbindelse fra
cout << "Fik forbindelse fra: " << (string)(*c->getAddress()) << endl;
//Opret et Client objekt til klienten
Client * cl = new Client(c,this);
try
{
//Start tråden
cl->start();
//Dette er en kritisk sektion, så vi synkroniserer
mutex.enter();
//Tilføj klienten til listen
m_clients.push_back(cl);
//Og forlad den kritiske sektion igen
mutex.leave();
} catch (ThreadException & te)
{
delete cl;
}
} catch (TimerException & timer)
{
//Vi har ikke fået en klient i et sekund
}
}
} catch (Exception & e)
{
cout << e << endl;
}
}
void Server::removeClient(Client * client)
{
//Dette er en kritisk sektion, så vi synkroniserer
mutex.enter();
for(ClientList::iterator ite = m_clients.begin(); ite != m_clients.end(); ite++)
{
//Hvis dette er klienten, som vi leder efter
if((*ite) == client)
{
//Så fjern den fra listen
m_clients.erase(ite);
//Og deallokér den
delete client;
//Og stop med at lede
break;
}
}
//Og forlad den kritiske sektion igen
mutex.leave();
}
int main(int argc, char ** argv)
{
try
{
if(argc != 2)
{
cout << "Brug: " << argv[0] << " <port>" << endl;
return 0;
}
netInit();
//Find porten, som vi vil lytte på
UInt16 port = (UInt16)atoi(argv[1]);
//Start serveren på denne port
Server ser(port);
//Server klassen er IKKE et Thread objekt så den kører i main tråden
ser.run();
netRelease();
} catch (Exception & e)
{
cout << e << endl;
}
return 0;
}
Jeg har tilføjet Mutex og TCPServerSocket koden til min Linux makefil:
#Makefile
#Brug g++ som compiler
CXX=g++
#Lænk med pthread biblioteket
LDFLAGS=-lpthread
#Compile med debugging support og alle warnings slået til
CXXFLAGS=-ggdb -Wall -c
CPPSOURCE= Exception.cpp IPAddress.cpp Net.cpp \ NetException.cpp test.cpp Thread.cpp \ ThreadException.cpp UnknownHostException.cpp \ TimerException.cpp ConnectionLostException.cpp \ TCPSocket.cpp Mutex.cpp TCPServerSocket.cpp
OBJECTS=$(CPPSOURCE:.cpp=.o)
test: $(OBJECTS)
$(CXX) $(LDFLAGS) -o $@ $(OBJECTS)
#Ovenstående linje har et tabulator stop i starten af linjen
Exception.o: Exception.cpp
ThreadException.o: ThreadException.cpp
NetException.o: NetException.cpp
UnknownHostException.o: UnknownHostException.cpp
IPAddress.o: IPAddress.cpp
Net.o: Net.cpp
Thread.o: Thread.cpp
TimerException.o: TimerException.cpp
ConnectionLostException.o: ConnectionLostException.cpp
TCPSocket.o: TCPSocket.cpp
TCPServerSocket.o: TCPServerSocket.cpp
Mutex.o: Mutex.cpp
test.o: test.cpp
%.o:
$(CXX) $(CXXFLAGS) -o $@ $<
#Ovenstående linje har et tabulator stop i starten af linjen
.PHONY: clean
clean:
rm -f $(OBJECTS) test
#Ovenstående linje har et tabulator stop i starten af linjen
Så er det så nemt at compile.
Konklusion
Det var alt, hvad jeg havde at sige om TCP netværks programmering, men legen slutter heldigvis ikke der. Vi kan nu slå netværks navne op i DNS, oprette forbindelser til servere og lave server programmer med meget få linjer kode. I næste artikel skal vi se på UDP sockets, så hæng på lidt endnu.
Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.
Del også gerne artiklen med dine Facebook venner:
Kommentarer (3)
Hej Robert
Bruger Dev-C++ under windows og får følgende fejl:
[Linker error] undefined reference to `TCPSocket::setTimeOut(unsigned int)'
[Linker error] undefined reference to `TCPSocket::~TCPSocket()'
[Linker error] undefined reference to `TCPSocket::read(void*, int)'
[Linker error] undefined reference to `operator<<(std::ostream&, Exception&
'
[Linker error] undefined reference to `std::__default_alloc_template<true, 0>::deallocate(void*, unsigned int)'
... også videre
collect2 C:\\Programmer\\Dev-Cpp\\projekter\\echo server\\collect2 ld returned 1 exit status
C:\\Programmer\\Dev-Cpp\\projekter\\echo server\\Makefile.win [Build Error] ["Echo] Error 1
Jeg har tilføjet libws2_32.a til listen over filer til linkeren.
hilsen
Martin
Hejsa. Jeg har lige læst og kigget denne artikel igennem. Men jeg har et spørgsmål, hvor har du alle de header filer fra?
- #include "TCPSocket.h"
- #include "IPAddress.h"
- #include "Types.h"
Kan ikke redigere.
Men jeg har stadigvæk ingen af disse header filer. Jeg har kigget lidt på min Dev-C++ installation og umildbart kan jeg ikke finde dem.
Du skal være
logget ind for at skrive en kommentar.