Socket programozás C-ben
Socket programozás a hálózat két csomópontjának összekapcsolásának módja, hogy kommunikáljanak egymással. Az egyik socket(csomópont) egy adott IP-porton figyel, míg a másik socket eléri a másikat, hogy kapcsolatot hozzon létre. A kiszolgáló képezi a figyelő socketet, miközben a kliens eléri a szervert.
A socket programozást széles körben használják azonnali üzenetküldő alkalmazásokban, bináris streamingben és dokumentum-együttműködésben online streaming platformokon stb.
Példa
Ebben a C programban egy üdvözlő üzenetet váltunk a szerver és a kliens között, hogy bemutassuk a kliens/szerver modellt.
szerver.c
C #include #include #include #include #include #include #define PORT 8080 int main ( int argc char const * argv []) { int server_fd new_socket ; ssize_t valread ; struct sockaddr_in address ; int opt = 1 ; socklen_t addrlen = sizeof ( address ); char buffer [ 1024 ] = { 0 }; char * hello = 'Hello from server' ; // Creating socket file descriptor if (( server_fd = socket ( AF_INET SOCK_STREAM 0 )) < 0 ) { perror ( 'socket failed' ); exit ( EXIT_FAILURE ); } // Forcefully attaching socket to the port 8080 if ( setsockopt ( server_fd SOL_SOCKET SO_REUSEADDR | SO_REUSEPORT & opt sizeof ( opt ))) { perror ( 'setsockopt' ); exit ( EXIT_FAILURE ); } address . sin_family = AF_INET ; address . sin_addr . s_addr = INADDR_ANY ; address . sin_port = htons ( PORT ); // Forcefully attaching socket to the port 8080 if ( bind ( server_fd ( struct sockaddr * ) & address sizeof ( address )) < 0 ) { perror ( 'bind failed' ); exit ( EXIT_FAILURE ); } if ( listen ( server_fd 3 ) < 0 ) { perror ( 'listen' ); exit ( EXIT_FAILURE ); } if (( new_socket = accept ( server_fd ( struct sockaddr * ) & address & addrlen )) < 0 ) { perror ( 'accept' ); exit ( EXIT_FAILURE ); } // subtract 1 for the null // terminator at the end valread = read ( new_socket buffer 1024 - 1 ); printf ( '%s n ' buffer ); send ( new_socket hello strlen ( hello ) 0 ); printf ( 'Hello message sent n ' ); // closing the connected socket close ( new_socket ); // closing the listening socket close ( server_fd ); return 0 ; }
ügyfél.c
C #include #include #include #include #include #define PORT 8080 int main ( int argc char const * argv []) { int status valread client_fd ; struct sockaddr_in serv_addr ; char * hello = 'Hello from client' ; char buffer [ 1024 ] = { 0 }; if (( client_fd = socket ( AF_INET SOCK_STREAM 0 )) < 0 ) { printf ( ' n Socket creation error n ' ); return -1 ; } serv_addr . sin_family = AF_INET ; serv_addr . sin_port = htons ( PORT ); // Convert IPv4 and IPv6 addresses from text to binary // form if ( inet_pton ( AF_INET '127.0.0.1' & serv_addr . sin_addr ) <= 0 ) { printf ( ' n Invalid address/ Address not supported n ' ); return -1 ; } if (( status = connect ( client_fd ( struct sockaddr * ) & serv_addr sizeof ( serv_addr ))) < 0 ) { printf ( ' n Connection Failed n ' ); return -1 ; } // subtract 1 for the null // terminator at the end send ( client_fd hello strlen ( hello ) 0 ); printf ( 'Hello message sent n ' ); valread = read ( client_fd buffer 1024 - 1 ); printf ( '%s n ' buffer ); // closing the connected socket close ( client_fd ); return 0 ; }
Összeállítás
gcc client.c -o clientgcc server.c -o server
KimenetClient:Hello message sentHello from serverServer:Hello from clientHello message sentA Socket programozás összetevői
1. Aljzatok
Aljzatok a program által a hálózat eléréséhez használt alapvető összetevők egyike, hogy a hálózaton keresztül más folyamatokkal/csomópontokkal kommunikálhasson. Ez egyszerűen egy IP-cím és egy portszám kombinációja, amely a kommunikáció végpontjaként működik.
Példa: 192.168.1.1:8080 ahol a kettősponttal elválasztott két rész a IP-cím (192.168.1.1) és a portszám (8080).Aljzatok típusai:
- TCP Socket (Stream Socket): Megbízható kapcsolat alapú kommunikációt biztosít (pl. TCP protokoll ).
- UDP Socket (Datagram Socket): Gyorsabb, de megbízhatatlanabb kapcsolat nélküli kommunikációt biztosít (pl. UDP protokoll ).
2. Kliens-szerver modell
A kliens-szerver modell a socket programozásban használt architektúrára utal, ahol a kliens és a szerver interakcióba lép egymással információ- vagy szolgáltatáscsere céljából. Ez az architektúra lehetővé teszi, hogy az ügyfél szolgáltatáskéréseket küldjön, a szerver pedig feldolgozza és válaszokat küldjön ezekre a szolgáltatáskérésekre.
Állapotdiagram kiszolgáló- és ügyfélmodellhez
Állapotdiagram a Socket szerver és kliens modelljéhez A socket programozás C nyelven hatékony módja a hálózati kommunikáció kezelésének.
Szerveroldali folyamat létrehozása
A szerver a következő lépésekkel jön létre:
1. Socket létrehozása
Ez a lépés magában foglalja a socket létrehozását a socket() függvény segítségével.
Paraméterek:
- sockfd: socket leíró egész szám (például fájlkezelő)
- domain: integer a kommunikációs tartományt adja meg. Az ugyanazon a gazdagépen lévő folyamatok közötti kommunikációhoz a POSIX szabványban meghatározott AF_LOCAL-t használjuk. Az IPV4 által összekapcsolt különböző gazdagépeken lévő folyamatok közötti kommunikációhoz az AF_INET és az AF_I NET 6 protokollt használjuk az IPV6 által összekapcsolt folyamatokhoz.
- típus: kommunikációs típus
SOCK_STREAM: TCP (megbízható kapcsolat-orientált)
SOCK_DGRAM: UDP (megbízhatatlan kapcsolat nélküli) - jegyzőkönyv: Az Internet Protocol(IP) protokollértéke, amely 0. Ez ugyanaz a szám, amely a csomag IP-fejlécében a protokoll mezőben jelenik meg. (a további részletekért a man protokollok)
sockfd = socket ( domain type protocol )
2. Állítsa be az aljzat opt
Ez segít a sockfd fájlleíró által hivatkozott socket opcióinak manipulálásában. Ez teljesen opcionális, de segít a cím és a port újrafelhasználásában. Megakadályozza az olyan hibákat, mint például: a cím már használatban van.
C setsockopt ( sockfd level optname optval socklen_t optlen );
3. Köt
A socket létrehozása után a bind() függvény a socketet az addr-ban (egyéni adatstruktúra) megadott címhez és portszámhoz köti. A példakódban a szervert a localhost-hoz kötjük, ezért az INADDR_ANY-t használjuk az IP-cím megadására.
C++ bind ( sockfd sockaddr * addr socklen_t addrlen );
Paraméterek:
- sockfd : a socket() függvény segítségével létrehozott socket fájlleíró.
- cím : mutató a sockaddr struct-ra, amely tartalmazza a socket összekapcsolásához szükséges IP-címet és portszámot.
- addrlen : az addr szerkezet hossza.
4. Figyelj
Ebben a lépésben a kiszolgáló a listen() függvényt használja, amely passzív módba helyezi a szerver socketet, ahol megvárja, amíg a kliens megkeresi a szervert, hogy kapcsolatot létesítsen. A hátralék határozza meg azt a maximális hosszt, ameddig a sockfd függőben lévő kapcsolatok sora növekedhet. Ha a csatlakozási kérelem akkor érkezik, amikor a sor megtelt, az ügyfél hibaüzenetet kaphat az ECONNREFUSED jelzéssel.
C listen ( sockfd backlog );
Paraméterek :
- sockfd : a socket() függvény segítségével létrehozott socket fájlleíró.
- hátralék : szám, amely a függőben lévő kapcsolatokat tároló sor méretét jelzi, miközben a szerver a kapcsolat elfogadására vár.
5. Elfogadás
Ebben a lépésben a szerver kibontja az első csatlakozási kérelmet a függőben lévő kapcsolatok sorából a hallgatási sockethez. A sockfd új csatlakoztatott socketet hoz létre a elfogad () függvényt, és egy új fájlleírót ad vissza az adott socketre hivatkozva. Ekkor létrejön a kapcsolat a kliens és a szerver között, és készen állnak az adatátvitelre.
C new_socket = accept ( sockfd sockaddr * addr socklen_t * addrlen );
Paraméterek:
- sockfd : a socket fájlleírót a socket() és a bind() adja vissza.
- cím : mutat egy struct sockaddr-re, amely az ügyfél IP-címét és portszámát fogja tárolni.
- addrlen : mutató a címstruktúra hosszát meghatározó változóra.
6. Küldés/fogadás
Ebben a lépésben a szerver adatokat küldhet vagy fogadhat a klienstől.
Elküld(): adatokat küldeni az ügyfélnek
C send ( sockfd * buf len flags );
Paraméterek:
- sockfd : a socket() függvény által visszaadott socket fájlleíró.
- buf : mutató a küldendő adatokat tartalmazó pufferre.
- csak : az elküldendő adatok bájtjainak száma.
- zászlókat : egész szám, amely különféle beállításokat ad meg az adatok küldésének módjára vonatkozóan, általában a 0 az alapértelmezett viselkedés.
Fogadás() : hogy megkapja az adatokat az ügyféltől.
C recv ( sockfd * buf len flags );
Paraméterek:
- sockfd : a socket() függvény által visszaadott socket fájlleíró.
- buf : mutató a tárolandó adatokat tartalmazó pufferre.
- csak : az elküldendő adatok bájtjainak száma.
- zászlókat : egész szám, amely különféle beállításokat ad meg az adatok küldésének módjára vonatkozóan, általában a 0 az alapértelmezett viselkedés.
6. Zárja be
Az információcsere befejezése után a szerver a close() függvény segítségével bezárja a socketet, és felszabadítja a rendszererőforrásokat.
C close ( fd );
Paraméterek:
- fd: a socket fájlleírója.
Ügyféloldali folyamat létrehozása
Kövesse az alábbi lépéseket az ügyféloldali folyamat létrehozásához:
1. Aljzatcsatlakozás
Ez a lépés magában foglalja a socket létrehozását, amely ugyanúgy történik, mint a szerver socket létrehozása
2. Csatlakozás
A connect() rendszerhívás a sockfd fájlleíró által hivatkozott socketet az addr által megadott címhez köti. A szerver címe és portja a címben van megadva.
C++ connect ( sockfd sockaddr * addr socklen_t addrlen );
Paraméterek
- sockfd : a socket() függvény által visszaadott socket fájlleíró.
- cím : mutató a struct sockaddr-hez, amely tartalmazza a szerver IP-címét és portszámát.
- addrlen : a cím mérete.
3. Küldés/fogadás
Ebben a lépésben a kliens adatokat küldhet vagy fogadhat a szerverről, ami a send() és recive() függvényekkel történik, hasonlóan ahhoz, ahogyan a szerver adatokat küld/fogad a klienstől.
4. Zárja be
Az információcsere befejezése után a kliensnek be kell zárnia a létrehozott socketet, és fel kell szabadítania a rendszererőforrásokat a close() függvény használatával, ugyanúgy, mint a szerver.
Gyakori problémák és megoldásaik a socket programozásban
- Csatlakozási hibák: A csatlakozási hibák elkerülése érdekében meg kell győződnünk arról, hogy a kliens a megfelelő hálózathoz próbál csatlakozni IP cím és port .
- Portkötési hibák: Ezek a hibák akkor fordulnak elő, ha egy portot egy másik alkalmazás már használ ebben a forgatókönyvben az adott porthoz való kötődés meghiúsul. Próbáljon meg másik portot használni, vagy zárja be az előző alkalmazást a port használatával.
- Blokkoló aljzatok: Alapértelmezés szerint az aljzatok blokkolva vannak. Ez azt jelenti, hogy az olyan hívások, mint az accept() vagy recv(), határozatlan ideig várnak, ha nincs ügyfélkapcsolat vagy adat. Szükség esetén az aljzatot nem blokkoló módba állíthatja.