Esimerkki miten TCP-socketteja voi yksinkertaisimmillaan käyttää.
Clienttiin kirjoitettu viesti siirretään serverille, joka tulostaa sen ja lähettää takaisin ja clientti tarkistaa onko data sama mikä lähetettiin.
[EDIT] Nyt toimii kaikilla kun ei ole conio.h käytössä.
Ainakin Dev-C++:n käyttäjien pitää lisätä projektin asetuksista linkkeriin rivi "-lwsock32", jotta socketit toimisivat.
// server.cpp
#include <winsock2.h>
#include <iostream>
using std::cout;
using std::endl;
int VastOtaViesti( SOCKET, void*, size_t );
int Vastaa( SOCKET, const void*, size_t );
int main( )
{
WSADATA ws;
SOCKET TCP;
BOOL opt = TRUE;
// Otetaan käyttöön winsocket 2.2
if( WSAStartup( MAKEWORD(2,2), &ws ) != 0 ) {
cout << "Ei tukea winsocket 2.2:lle";
WSACleanup();
return 2;
}
// luodaan socket
if( (TCP = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP )) == INVALID_SOCKET ) {
cout << "Ei voi luoda sockettia! Error: " << WSAGetLastError();
WSACleanup();
return 3;
}
servent* pent;
if( (pent = getservbyname( "echo", 0 )) == 0 ) {
cout << "Socket error: " << WSAGetLastError();
WSACleanup();
return 4;
}
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = pent-> s_port;
sa.sin_addr.s_addr = htonl(INADDR_ANY); // sallitaan yhteys keneltä tahansa
if( bind( TCP, (sockaddr*) &sa, sizeof(sockaddr_in) ) == SOCKET_ERROR ) {
cout << "Socket error: " << WSAGetLastError();
WSACleanup();
return 5;
}
if( setsockopt( TCP, SOL_SOCKET, SO_REUSEADDR, (char*) &opt, sizeof(BOOL) ) == SOCKET_ERROR )
{
cout << "Socket error: " << WSAGetLastError();
WSACleanup();
return 6;
}
if( listen( TCP, 0 ) == SOCKET_ERROR )
{
cout << "Socket error: " << WSAGetLastError();
WSACleanup();
return 7;
}
SOCKET TCPC;
sockaddr_in srcaddr;
int srcaddrlen = sizeof(sockaddr_in);
cout << "Linkku's %sber TCP server. ", "\x81\x81";
cout << "Odotetaan yhteytt\x84...\n";
// Sallitaan sisään tuleva yhteys accept:lla
if( (TCPC = accept( TCP, (sockaddr*) &srcaddr, &srcaddrlen )) == INVALID_SOCKET )
{
cout << "Socket error: " << WSAGetLastError();
WSACleanup();
return 8;
}
cout << "Yhteys avattu!\n" << "<" << inet_ntoa( srcaddr.sin_addr ) << ">" << endl;
int iNum = 1, iPit;
char cBuf[1024];
while(1)
{
// vastaanotetaan viestit
if( (iPit = VastOtaViesti( TCPC, cBuf, sizeof(cBuf) )) == SOCKET_ERROR ) {
cout << endl << iNum-1 << " viesti\x84 vastaanotettu!";
getchar();
return 9;
}
cBuf[iPit] = '\0';
cout << iNum++ << ": " << iPit << " - " << cBuf << endl;
if( Vastaa( TCPC, cBuf, iPit ) == SOCKET_ERROR ) {
cout << "Ei voi vastata viestiin! Error: " << WSAGetLastError();
WSACleanup();
getchar();
return -1;
}
}
getchar();
// vapautetaan resurssit
WSACleanup();
return 0;
}
int VastOtaViesti( SOCKET S, void* cBuf, size_t Palautus )
{
long expect;
Palautus = recv( S, (char*) &expect, sizeof(long), 0 );
expect = ntohl( expect );
Palautus = recv( S, (char*) cBuf, (size_t) expect, 0 );
return Palautus;
}
int Vastaa( SOCKET S, const void* cBuffer, size_t iSize )
{
long len = htonl( (long) iSize );
send( S, (const char*) &len, sizeof(long), 0 );
send( S, (const char*) cBuffer, iSize, 0 );
return 0;
}// client.cpp
#include <winsock2.h>
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
int Vastaus( SOCKET, void*, size_t );
int LahetaViesti( SOCKET, const void*, size_t );
int main( int ac, char* av[] )
{
unsigned long N = 0;
BOOL opt = TRUE;
HOSTENT* Hostent;
WSADATA ws;
SOCKET TCP;
// Otetaan käyttöön winsocket 2.2
if(WSAStartup(MAKEWORD(2,2), &ws )!= 0 ) {
std::cout << "Ei tukea winsocket 2.2:lle!";
WSACleanup();
getchar();
return 2;
}
// luodaan TCP socket
if( (TCP = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP )) == INVALID_SOCKET ) {
std::cout << "Ei voi luoda sockettia! Error: " << WSAGetLastError();
WSACleanup();
return 3;
}
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = 0; // otetaan nollasta seuraava vapaa portti
sa.sin_addr.s_addr = htonl(INADDR_ANY);
if( bind( TCP, (sockaddr*) &sa, sizeof(sockaddr_in) ) == SOCKET_ERROR ) {
std::cout << "Socket error: " << WSAGetLastError();
WSACleanup();
return 4;
}
// Säädetään asetukset
if( setsockopt( TCP, SOL_SOCKET, SO_REUSEADDR, (char*) &opt, sizeof(BOOL) ) == SOCKET_ERROR )
{
std::cout << "Socket error: " << WSAGetLastError();
WSACleanup();
return 5;
}
servent* dd;
if( (dd = getservbyname( "echo", 0 )) == 0 ) {
std::cout << "Socket error: " << WSAGetLastError();
WSACleanup();
return 6;
}
sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = dd-> s_port;
if( (Hostent = gethostbyname("localhost")) != 0 )
srvaddr.sin_addr.s_addr = *(long*) Hostent-> h_addr;
else if( (srvaddr.sin_addr.s_addr = inet_addr( av[1] )) == INADDR_NONE ) {
std::cout << "Socket error: " << WSAGetLastError() ;
WSACleanup();
return 7;
}
cout << "Linkku's %sber TCP client. ", "\x81\x81";
cout << "Yhdistet\x84\x84n...\n";
// Yritetään etsiä serveri
if( connect( TCP, (sockaddr*) &srvaddr, sizeof(srvaddr) ) == SOCKET_ERROR ) {
std::cout << "Ei voi yhdist\x84\x84 palvelimeen [palvelin ei ehk\x84 k\x84ynniss\x84]\nError: ";
std::cout << WSAGetLastError();
WSACleanup();
getchar();
return 8;
}
std::cout << "Yhdistetty!\n";
int iKor = 3;
while(1)
{
char cBuf[1024];
cout << "\n> " ;
cin.getline( cBuf, sizeof(cBuf));
size_t iPit = strlen(cBuf);
if( iPit==0 ) {
break;
}
N++;
// siirretään viesti
if( LahetaViesti( TCP, cBuf, iPit ) == SOCKET_ERROR ) {
cout << "Ei voi l\x84hett\x84\x84 viesti\x84! Error: " << WSAGetLastError() ;
WSACleanup();
getchar();
return 9;
}
char cVBuf[sizeof(cBuf)] = " ";
size_t rlen = SOCKET_ERROR;
size_t tlen = strlen(cBuf);
// odotetaan vastausta
if( (rlen = Vastaus( TCP, cVBuf, sizeof(cVBuf) )) == SOCKET_ERROR )
{
std::cout << "Ei saatu vastausta palvelimelta! Error: " << WSAGetLastError();
getchar();
WSACleanup();
return -1;
}
cout << cVBuf << endl;
// tarkistetaan serverin lähettämä data
// ei kuitenkaan mikään übervarma tarkistus
if( rlen != tlen || memcmp( cVBuf, cBuf, rlen ) ) {
cout << "\nViesti korruptoitunut!\n" ;
}
}
std::cout << "\n\n" << N << " viesti\x84 l\x84hetetty";
// vapautetaan resurssit
getchar();
WSACleanup();
return 0;
}
int LahetaViesti( SOCKET S, const void* cBuffer, size_t iSize )
{
long len = htonl( (long) iSize );
send( S, (const char*) &len, sizeof(long), 0 );
send( S, (const char*) cBuffer, iSize, 0 );
return 0;
}
int Vastaus( SOCKET S, void* cBuffer, size_t Palautus )
{
long expect;
Palautus = recv( S, (char*) &expect, sizeof(long), 0 );
expect = ntohl( expect );
Palautus = recv( S, (char*) cBuffer, (size_t) expect, 0 );
return Palautus;
}Mukavan oloinen koodi... Varmasti hyötyä monelle.
Tosiaan koodi on selkeetä, ja hyötyä löytyy varmasti.
Hieno on. Sitten kun tartten c++:ssa winsockia niin käytän varmana tätä apuna. Linkku, oot taitava!
Arvelin kyllä että ku C:llä kirjotettu että koodia olisi vieläkin enemmän. Ihan mukava pakettihan tuo on.
Koodi taittaa oottaa aina yhteyden localhostiin, tuo muuten aikalailla lisää hasteetta kun tehdään yhteys hitaan yhteyden yli ;)
Linkku, eikös tuossa kannattaisi yhdistää nuo koodit ja ohjelma kysyisi käynnistyksen yhteydessä että onko serveri vai client?
lainaus:
Koodi taittaa oottaa aina yhteyden localhostiin, tuo muuten aikalailla lisää hasteetta kun tehdään yhteys hitaan yhteyden yli ;)
Yhteys otetaan localhostiin ja portista 0 seuraava vapaa portti mulla oli ainakin 7, eli se yhdisti sen kautta.
lainaus:
Linkku, eikös tuossa kannattaisi yhdistää nuo koodit ja ohjelma kysyisi käynnistyksen yhteydessä että onko serveri vai client?
Tämä kävi kyllä mielessä, mutta päädyin nyt tähän.
Miten WinSocketissa voi määrittää IP:n ja PORTin??? Voisiko joku kirjoittaa pienen esimerkki koodin???? Olisin erittäin kiitollinen siitä!!!!
Linkku!!! Yritin kääntää ton Serverin, niin kääntäjä itki jotain tuosta gotoxy -lauseesta.
Tarkalleen ottaen se sanoi:
C:\Documents and Settings\Jussi\C++ Testit\systeemi.cpp(99) : error C2065: 'gotoxy' : undeclared identifier
Mistä johtunee??????
Olisi mukava saada vastaus.
AdeRide: Nyt toimii ja ip haetaan tuossa "gethostbyname("localhost")"
Voiko tuohon serveriin ottaa useampi clientti yhteyden yhtäaikaa, ilman, että serveri sekoaa täysin? Vai onko mahdollista avata yhteys vain yhdelle clientille?
AdeRide: Tähän versioon voi yhdistää vain yksi klientti kerralla. Kehitteillä on toinenkin versio mihin voi yhdistää useampi klientti. Ehkä vielä jonain päivänä postaan sen tänne :)
Mulla MSVC++ ärähtää:
Console Source.obj : error LNK2001: unresolved external symbol __imp__WSAGetLastError@0
Console Source.obj : error LNK2001: unresolved external symbol __imp__socket@12
Console Source.obj : error LNK2001: unresolved external symbol __imp__WSACleanup@0
Console Source.obj : error LNK2001: unresolved external symbol __imp__WSAStartup@8
Console Source.obj : error LNK2001: unresolved external symbol __imp__ntohl@4
ja vielä vähän enemmän. Mitä pitäis tehdä?
Linkku kirjoitti:
Ainakin Dev-C++:n käyttäjien pitää lisätä projektin asetuksista linkkeriin rivi "-lwsock32", jotta socketit toimisivat.
Toimiiko tuo borlandin c++:lla.
tuo serveri valittaa jotain "Socket error 11004"
ei ei ei, ennen recv kannattee käyttää selectiä, entä jos palautusta ei tulekkaan, recv jää blokkaamaan iäisyyksiin.
Socket error 10004 - Interrupted function call
Socket error 10013 - Permission denied
Socket error 10014 - Bad address
Socket error 10022 - Invalid argument
Socket error 10024 - Too many open files
Socket error 10035 - Resource temporarily unavailable
Socket error 10036 - Operation now in progress
Socket error 10037 - Operation already in progress
Socket error 10038 - Socket operation on non-socket
Socket error 10039 - Destination address required
Socket error 10040 - Message too long
Socket error 10041 - Protocol wrong type for socket
Socket error 10042 - Bad protocol option
Socket error 10043 - Protocol not supported
Socket error 10044 - Socket type not supported
Socket error 10045 - Operation not supported
Socket error 10046 - Protocol family not supported
Socket error 10047 - Address family not supported by protocol family
Socket error 10048 - Address already in use
Socket error 10049 - Cannot assign requested address
Socket error 10050 - Network is down
Socket error 10051 - Network is unreachable
Socket error 10052 - Network dropped connection on reset
Socket error 10053 - Software caused connection abort
Socket error 10054 - Connection reset by peer
Socket error 10055 - No buffer space available
Socket error 10056 - Socket is already connected
Socket error 10057 - Socket is not connected
Socket error 10058 - Cannot send after socket shutdown
Socket error 10060 - Connection timed out
Socket error 10061 - Connection refused
Socket error 10064 - Host is down
Socket error 10065 - No route to host
Socket error 10067 - Too many processes
Socket error 10091 - Network subsystem is unavailable
Socket error 10092 - WINSOCK.DLL version out of range
Socket error 10093 - Successful WSAStartup not yet performed
Socket error 10094 - Graceful shutdown in progress
Socket error 11001 - Host not found
Socket error 11002 - Non-authoritative host not found
Socket error 11003 - This is a non-recoverable error
Socket error 11004 - Valid name, no data record of requested type
EDIT:
Vastasinkin näköjään puolitoista vuotta myöhässä :P
Toimiiko tämä esimerkki internetin kautta ?
En itse saanut sitä toimimaan kun pelkästään lanissa.
Eli en oikeen osannut tuosta koodista avata porttia...
Mutta toimiiko toi internetin välityksellä tuo ohjelma ?
sa.sin_addr.s_addr = htonl(INADDR_ANY);
^^ Eik tuon kuuluisi olla htons? Minulla se toimi ainoastaan näin.
Voisiko joku laitella esimerkkiä monen clientin käytöstä yhellä serverillä? :)
Aihe on jo aika vanha, joten et voi enää vastata siihen.