国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【網(wǎng)絡(luò)編程】利用套接字實(shí)現(xiàn)一個(gè)簡單的網(wǎng)絡(luò)通信(UDP實(shí)現(xiàn)聊天室 附上源碼)

這篇具有很好參考價(jià)值的文章主要介紹了【網(wǎng)絡(luò)編程】利用套接字實(shí)現(xiàn)一個(gè)簡單的網(wǎng)絡(luò)通信(UDP實(shí)現(xiàn)聊天室 附上源碼)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

【網(wǎng)絡(luò)編程】利用套接字實(shí)現(xiàn)一個(gè)簡單的網(wǎng)絡(luò)通信(UDP實(shí)現(xiàn)聊天室 附上源碼),Linux,網(wǎng)絡(luò),udp,服務(wù)器

??預(yù)備知識(shí)

??理解源IP地址和目的IP地址

源IP地址(Source IP Address):

源IP地址是數(shù)據(jù)包發(fā)送方(或數(shù)據(jù)流出發(fā)點(diǎn))的唯一標(biāo)識(shí)符。它用于在互聯(lián)網(wǎng)或本地網(wǎng)絡(luò)中定位發(fā)送數(shù)據(jù)包的設(shè)備或主機(jī)。源IP地址是數(shù)據(jù)包的出發(fā)點(diǎn),即數(shù)據(jù)從這個(gè)地址開始傳送,向目的IP地址指示的設(shè)備發(fā)送。
在TCP/IP協(xié)議中,源IP地址通常由發(fā)送方的操作系統(tǒng)或網(wǎng)絡(luò)棧分配,并在數(shù)據(jù)包的IP首部中進(jìn)行標(biāo)記。

目的IP地址(Destination IP Address):

目的IP地址是數(shù)據(jù)包的接收方(或數(shù)據(jù)流的目標(biāo)點(diǎn))的唯一標(biāo)識(shí)符。它用于在互聯(lián)網(wǎng)或本地網(wǎng)絡(luò)中定位接收數(shù)據(jù)包的設(shè)備或主機(jī)。目的IP地址是數(shù)據(jù)包的終點(diǎn),即數(shù)據(jù)傳輸?shù)哪繕?biāo)地址,數(shù)據(jù)包應(yīng)該傳輸?shù)竭@個(gè)地址。
在TCP/IP協(xié)議中,目的IP地址通常由應(yīng)用程序或網(wǎng)絡(luò)棧設(shè)置,并在數(shù)據(jù)包的IP首部中進(jìn)行標(biāo)記。
這兩個(gè)地址在數(shù)據(jù)包傳輸過程中起著非常重要的作用,確保數(shù)據(jù)從源設(shè)備正確地傳遞到目標(biāo)設(shè)備,實(shí)現(xiàn)網(wǎng)絡(luò)通信。IP地址是一個(gè)32位的二進(jìn)制數(shù),通常用點(diǎn)分十進(jìn)制表示(例如,192.168.0.1),其中前24位表示網(wǎng)絡(luò)地址,后8位表示主機(jī)地址。

??認(rèn)識(shí)端口號(hào)

端口號(hào)(port)是傳輸層協(xié)議的內(nèi)容.
端口號(hào)是一個(gè)2字節(jié)16位的整數(shù);
端口號(hào)用來標(biāo)識(shí)一個(gè)進(jìn)程, 告訴操作系統(tǒng), 當(dāng)前的這個(gè)數(shù)據(jù)要交給哪一個(gè)進(jìn)程來處理;
IP地址 + 端口號(hào)能夠標(biāo)識(shí)網(wǎng)絡(luò)上的某一臺(tái)主機(jī)的某一個(gè)進(jìn)程;
一個(gè)端口號(hào)只能被一個(gè)進(jìn)程占用.

?? 理解 “端口號(hào)” 和 “進(jìn)程ID”

  • 端口號(hào)(Port Number)和進(jìn)程ID(Process ID)是在計(jì)算機(jī)網(wǎng)絡(luò)和操作系統(tǒng)中用于不同目的的標(biāo)識(shí)符。

端口號(hào):

端口號(hào)是在計(jì)算機(jī)網(wǎng)絡(luò)中用于標(biāo)識(shí)特定進(jìn)程或服務(wù)的數(shù)字。 它是一個(gè)16位的無符號(hào)整數(shù),取值范圍是0到65535。
在TCP/IP網(wǎng)絡(luò)中,每個(gè)傳輸控制協(xié)議(TCP)和用戶數(shù)據(jù)報(bào)協(xié)議(UDP)的通信端點(diǎn)都與一個(gè)端口號(hào)相關(guān)聯(lián)。 這樣,計(jì)算機(jī)上的不同進(jìn)程或服務(wù)可以通過不同的端口號(hào)進(jìn)行通信,從而實(shí)現(xiàn)多個(gè)應(yīng)用程序的并發(fā)運(yùn)行。
例如,HTTP服務(wù)通常使用端口號(hào)80,HTTPS使用端口號(hào)443,SMTP使用端口號(hào)25等。

進(jìn)程ID:

進(jìn)程ID是操作系統(tǒng)中用于標(biāo)識(shí)運(yùn)行中進(jìn)程(或線程)的唯一標(biāo)識(shí)符。 它是一個(gè)非負(fù)整數(shù),通常由操作系統(tǒng)在進(jìn)程創(chuàng)建時(shí)分配。
在多任務(wù)操作系統(tǒng)中,每個(gè)運(yùn)行中的進(jìn)程都有一個(gè)唯一的進(jìn)程ID,操作系統(tǒng)通過進(jìn)程ID來跟蹤和管理不同的進(jìn)程。
進(jìn)程ID的范圍通常由操作系統(tǒng)定義,可以是一個(gè)較小的數(shù)值范圍,也可以是一個(gè)較大的范圍。

區(qū)別:

端口號(hào)是在計(jì)算機(jī)網(wǎng)絡(luò)中用于標(biāo)識(shí)不同進(jìn)程或服務(wù)的通信端點(diǎn),用于實(shí)現(xiàn)多個(gè)應(yīng)用程序的并發(fā)運(yùn)行。 進(jìn)程ID是操作系統(tǒng)中用于標(biāo)識(shí)運(yùn)行中進(jìn)程的唯一標(biāo)識(shí)符,用于跟蹤和管理不同的進(jìn)程。
端口號(hào)是在網(wǎng)絡(luò)通信中使用的,而進(jìn)程ID是在操作系統(tǒng)層級(jí)使用的。
端口號(hào)是一個(gè)16位的數(shù)字,進(jìn)程ID是一個(gè)非負(fù)整數(shù)。

??簡單認(rèn)識(shí)TCP協(xié)議

傳輸層協(xié)議
有連接
可靠傳輸
面向字節(jié)流

??簡單認(rèn)識(shí)UDP協(xié)議

傳輸層協(xié)議
無連接
不可靠傳輸
面向數(shù)據(jù)報(bào)

?? 什么是網(wǎng)絡(luò)字節(jié)序

網(wǎng)絡(luò)字節(jié)序是一種在計(jì)算機(jī)網(wǎng)絡(luò)中使用的固定字節(jié)順序,用于在不同計(jì)算機(jī)體系結(jié)構(gòu)和操作系統(tǒng)之間傳遞數(shù)據(jù)。 在計(jì)算機(jī)內(nèi)部,不同的體系結(jié)構(gòu)(例如x86、ARM、SPARC等)和操作系統(tǒng)(例如Windows、Linux、iOS等)可能使用不同的字節(jié)順序,這可能導(dǎo)致在網(wǎng)絡(luò)通信中出現(xiàn)問題。

為了解決這個(gè)問題,網(wǎng)絡(luò)通信中使用了統(tǒng)一的字節(jié)順序,即網(wǎng)絡(luò)字節(jié)序。 網(wǎng)絡(luò)字節(jié)序采用大端字節(jié)序(Big-Endian)表示法,其中較高的字節(jié)位于較低的內(nèi)存地址上,較低的字節(jié)位于較高的內(nèi)存地址上。

舉例說明:

假設(shè)一個(gè)16位整數(shù)0x1234(十進(jìn)制為4660)在內(nèi)存中存儲(chǔ)為兩個(gè)字節(jié):0x12和0x34。

在大端字節(jié)序中,較高的字節(jié)(0x12)存儲(chǔ)在較低的內(nèi)存地址,較低的字節(jié)(0x34)存儲(chǔ)在較高的內(nèi)存地址。 即內(nèi)存地址由高到低,數(shù)據(jù)依次為0x12 0x34。
在小端字節(jié)序中,較低的字節(jié)(0x34)存儲(chǔ)在較低的內(nèi)存地址,較高的字節(jié)(0x12)存儲(chǔ)在較高的內(nèi)存地址。 即內(nèi)存地址由高到低,數(shù)據(jù)依次為0x34 0x12。

在網(wǎng)絡(luò)通信中,發(fā)送方將數(shù)據(jù)轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序后發(fā)送,接收方收到數(shù)據(jù)后將其轉(zhuǎn)換為本地字節(jié)序進(jìn)行處理,以保證在不同計(jì)算機(jī)體系結(jié)構(gòu)和操作系統(tǒng)之間正確地傳遞數(shù)據(jù)。 常用的網(wǎng)絡(luò)編程庫(例如Socket編程)通常會(huì)自動(dòng)處理字節(jié)序的轉(zhuǎn)換。

??相關(guān)函數(shù)端口介紹

?? socket相關(guān)API介紹

  • man手冊(cè)
    【網(wǎng)絡(luò)編程】利用套接字實(shí)現(xiàn)一個(gè)簡單的網(wǎng)絡(luò)通信(UDP實(shí)現(xiàn)聊天室 附上源碼),Linux,網(wǎng)絡(luò),udp,服務(wù)器

Socket(套接字)是一種用于網(wǎng)絡(luò)通信的編程接口,它允許計(jì)算機(jī)之間通過網(wǎng)絡(luò)傳輸數(shù)據(jù)。 在Socket編程中,我們可以使用一組API函數(shù)來創(chuàng)建、綁定、連接、發(fā)送和接收數(shù)據(jù)等操作。

  • 以下是常用的Socket API函數(shù)及其參數(shù):
  1. socket()函數(shù):
  • 描述:創(chuàng)建一個(gè)新的套接字。
  • 參數(shù):int socket(int domain, int type, int protocol)
  • domain:套接字的地址族,常用的有AF_INET(IPv4)和AF_INET6(IPv6)。
  • type:套接字的類型,常用的有SOCK_STREAM(流式套接字,用于TCP)和SOCK_DGRAM(數(shù)據(jù)報(bào)套接字,用于UDP)。
  • protocol:使用的協(xié)議,通常為0(自動(dòng)選擇與type相關(guān)的默認(rèn)協(xié)議)。
  1. bind()函數(shù):
  • 描述:將套接字綁定到一個(gè)特定的地址和端口。
  • 參數(shù):int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
  • sockfd:套接字描述符。
  • addr:指向要綁定的地址結(jié)構(gòu)的指針,通常是struct sockaddr_in(IPv4)或struct sockaddr_in6(IPv6)。
  • addrlen:地址結(jié)構(gòu)的長度。
  1. connect()函數(shù):
  • 描述:建立與遠(yuǎn)程服務(wù)器的連接。
  • 參數(shù):int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
  • sockfd:套接字描述符。
  • addr:指向目標(biāo)服務(wù)器地址的指針,通常是struct sockaddr_in(IPv4)或struct sockaddr_in6(IPv6)。
  • addrlen:地址結(jié)構(gòu)的長度。
  1. listen()函數(shù):
  • 描述:將套接字設(shè)置為監(jiān)聽模式,準(zhǔn)備接受連接請(qǐng)求。
  • 參數(shù):int listen(int sockfd, int backlog)
  • sockfd:套接字描述符。
  • backlog:請(qǐng)求隊(duì)列的最大長度,即等待接受連接的連接數(shù)。
  1. accept()函數(shù):
  • 描述:接受連接請(qǐng)求,創(chuàng)建一個(gè)新的套接字用于與客戶端通信。
  • 參數(shù):int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
  • sockfd:監(jiān)聽套接字描述符。
  • addr:指向客戶端地址的指針,用于存儲(chǔ)客戶端的信息。
  • addrlen:地址結(jié)構(gòu)的長度。
  1. send()函數(shù):
  • 描述:發(fā)送數(shù)據(jù)。
  • 參數(shù):ssize_t send(int sockfd, const void *buf, size_t len, int flags)
  • sockfd:套接字描述符。
  • buf:要發(fā)送的數(shù)據(jù)緩沖區(qū)。
  • len:要發(fā)送的數(shù)據(jù)長度。
  • flags:發(fā)送標(biāo)志,通常為0。
  1. recv()函數(shù):
  • 描述:接收數(shù)據(jù)。
  • 參數(shù):ssize_t recv(int sockfd, void *buf, size_t len, int flags)
  • sockfd:套接字描述符。
  • buf:接收數(shù)據(jù)的緩沖區(qū)。
  • len:要接收的數(shù)據(jù)長度。
  • flags:接收標(biāo)志,通常為0。
  1. close()函數(shù):
  • 描述:關(guān)閉套接字。
  • 參數(shù):int close(int sockfd)
  • sockfd:套接字描述符。

使用Socket API時(shí),通常的流程是創(chuàng)建一個(gè)套接字、綁定到一個(gè)地址和端口(可選)、監(jiān)聽連接請(qǐng)求(可選)、接受連接請(qǐng)求、發(fā)送和接收數(shù)據(jù),最后關(guān)閉套接字。 服務(wù)器端和客戶端使用不同的套接字操作,服務(wù)器端用于監(jiān)聽連接請(qǐng)求和處理客戶端請(qǐng)求,而客戶端用于建立連接和發(fā)送請(qǐng)求給服務(wù)器端。

??sockaddr結(jié)構(gòu)

sockaddr 是一個(gè)通用的套接字地址結(jié)構(gòu),在Socket編程中經(jīng)常用于存儲(chǔ)網(wǎng)絡(luò)地址信息。由于不同的協(xié)議族(IPv4、IPv6等)具有不同的地址結(jié)構(gòu),因此 sockaddr 用作地址結(jié)構(gòu)的基類,而實(shí)際使用時(shí)通常會(huì)使用具體的子結(jié)構(gòu) sockaddr_in(IPv4)或 sockaddr_in6(IPv6)。

struct sockaddr {
    sa_family_t sa_family;      // 地址族,通常為 AF_INET 或 AF_INET6
    char sa_data[14];           // 存放地址信息的緩沖區(qū),不同協(xié)議族具有不同的結(jié)構(gòu)
};

在實(shí)際使用時(shí),通常會(huì)將 sockaddr 結(jié)構(gòu)轉(zhuǎn)換為適合當(dāng)前協(xié)議族的具體地址結(jié)構(gòu)。例如,在IPv4協(xié)議中,使用 sockaddr_in 結(jié)構(gòu),其定義如下:

struct sockaddr_in {
    sa_family_t sin_family;     // 地址族,固定為 AF_INET
    in_port_t sin_port;         // 16位端口號(hào),使用網(wǎng)絡(luò)字節(jié)序
    struct in_addr sin_addr;    // 32位IPv4地址,使用網(wǎng)絡(luò)字節(jié)序
    char sin_zero[8];           // 不使用,填充字段
};

在IPv6協(xié)議中,使用 sockaddr_in6 結(jié)構(gòu),其定義如下:

struct sockaddr_in6 {
    sa_family_t sin6_family;     // 地址族,固定為 AF_INET6
    in_port_t sin6_port;         // 16位端口號(hào),使用網(wǎng)絡(luò)字節(jié)序
    uint32_t sin6_flowinfo;      // 流標(biāo)識(shí),通常為0
    struct in6_addr sin6_addr;   // 128位IPv6地址,使用網(wǎng)絡(luò)字節(jié)序
    uint32_t sin6_scope_id;      // 接口范圍標(biāo)識(shí)
};

在使用 結(jié)構(gòu)時(shí),通常需要進(jìn)行類型轉(zhuǎn)換,將其轉(zhuǎn)換為適用于當(dāng)前協(xié)議族的地址結(jié)構(gòu),并根據(jù)需要填充具體的地址信息,然后傳遞給套接字相關(guān)的函數(shù)使用。

?? sockaddr_in結(jié)構(gòu)

sockaddr_in 是用于存儲(chǔ)IPv4地址的套接字地址結(jié)構(gòu),是在網(wǎng)絡(luò)編程中非常常用的結(jié)構(gòu)。它用于在IPv4協(xié)議族中表示網(wǎng)絡(luò)地址和端口號(hào)。下面是 sockaddr_in 結(jié)構(gòu)的定義:

struct sockaddr_in {
    sa_family_t sin_family;     // 地址族,固定為 AF_INET
    in_port_t sin_port;         // 16位端口號(hào),使用網(wǎng)絡(luò)字節(jié)序
    struct in_addr sin_addr;    // 32位IPv4地址,使用網(wǎng)絡(luò)字節(jié)序
    char sin_zero[8];           // 不使用,填充字段
};

其中,各字段的含義如下:

sin_family:地址族,固定為 AF_INET,表示IPv4地址族。
sin_port:16位端口號(hào),使用網(wǎng)絡(luò)字節(jié)序,需要使用 htons 函數(shù)進(jìn)行字節(jié)序轉(zhuǎn)換。
sin_addr:32位IPv4地址,使用網(wǎng)絡(luò)字節(jié)序,需要使用 inet_pton 函數(shù)將點(diǎn)分十進(jìn)制形式的IPv4地址轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序。
sin_zero:不使用,填充字段,通常設(shè)置為0。
使用 結(jié)構(gòu)時(shí),通常先將IPv4地址和端口號(hào)填充到該結(jié)構(gòu)中,然后將其轉(zhuǎn)換為通用的 sockaddr_insockaddr 結(jié)構(gòu),在套接字相關(guān)的函數(shù)中使用。

例如,在服務(wù)器端綁定一個(gè)IPv4地址和端口號(hào),可以這樣做:

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

int main() {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket");
        return -1;
    }

    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080); // 將端口號(hào)轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); // 將IPv4地址轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序
    memset(server_addr.sin_zero, 0, sizeof(server_addr.sin_zero));

    if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind");
        close(sockfd);
        return -1;
    }

    // 其他操作...
    close(sockfd);
    return 0;
}

注意,在使用 sockaddr_in 結(jié)構(gòu)時(shí),需要包含 <netinet/in.h> 頭文件,并且要進(jìn)行網(wǎng)絡(luò)字節(jié)序的轉(zhuǎn)換。

?? 簡單的UDP網(wǎng)絡(luò)程序

??log.hpp 日志文件

#pragma once

#include <cstdio>
#include <ctime>
#include <cstdarg>
#include <cassert>
#include <cstring>
#include <cerrno>
#include <stdlib.h>

#define DEBUG 0
#define NOTICE 1
#define WARINING 2
#define FATAL 3

const char *log_level[]={"DEBUG", "NOTICE", "WARINING", "FATAL"};

// logMessage(DEBUG, "%d", 10);
void logMessage(int level, const char *format, ...)
{
    assert(level >= DEBUG);
    assert(level <= FATAL);

    char *name = getenv("USER");

    char logInfo[1024];
    va_list ap; // ap -> char*
    va_start(ap, format);

    vsnprintf(logInfo, sizeof(logInfo)-1, format, ap);

    va_end(ap); // ap = NULL


    FILE *out = (level == FATAL) ? stderr:stdout;

    fprintf(out, "%s | %u | %s | %s\n", \
        log_level[level], \
        (unsigned int)time(nullptr),\
        name == nullptr ? "unknow":name,\
        logInfo);

    // char *s = format;
    // while(s){
    //     case '%':
    //         if(*(s+1) == 'd')  int x = va_arg(ap, int);
    //     break;
    // }
}

?? udpClient.cc 客戶端

#include <iostream>
#include <string>
#include <cstdlib>
#include <cassert>
#include <unistd.h>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>

struct sockaddr_in server;

static void Usage(std::string name)
{
    std::cout << "Usage:\n\t" << name << " server_ip server_port" << std::endl;
}

void *recverAndPrint(void *args)
{
    while (true)
    {
        int sockfd = *(int *)args;
        char buffer[1024];
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);
        ssize_t s = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&temp, &len);
        if (s > 0)
        {
            buffer[s] = 0;
            std::cout << "server echo# " << buffer << std::endl;
        }
    }
}

// ./udpClient server_ip server_port
// 如果一個(gè)客戶端要連接server必須知道server對(duì)應(yīng)的ip和port
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    // 1. 根據(jù)命令行,設(shè)置要訪問的服務(wù)器IP
    std::string server_ip = argv[1];
    uint16_t server_port = atoi(argv[2]);

    // 2. 創(chuàng)建客戶端
    // 2.1 創(chuàng)建socket
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    assert(sockfd > 0);

    // 2.2 client 需不需要bind??? 需要bind,但是不需要用戶自己bind,而是os自動(dòng)給你bind
    // 所謂的"不需要",指的是: 不需要用戶自己bind端口信息!因?yàn)镺S會(huì)自動(dòng)給你綁定,你也最好這么做!
    // 如果我非要自己bind呢?可以!嚴(yán)重不推薦!
    // 所有的客戶端軟件 <-> 服務(wù)器 通信的時(shí)候,必須得有 client[ip:port] <-> server[ip:port]
    // 為什么呢??client很多,不能給客戶端bind指定的port,port可能被別的client使用了,你的client就無法啟動(dòng)了
    // 那么server憑什么要bind呢??server提供的服務(wù),必須被所有人知道!server不能隨便改變!
    // 2.2 填寫服務(wù)器對(duì)應(yīng)的信息

    bzero(&server, sizeof server);
    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());

    pthread_t t;
    pthread_create(&t, nullptr, recverAndPrint, (void *)&sockfd);
    // 3. 通訊過程
    std::string buffer;
    while (true)
    {
        std::cerr << "Please Enter# ";
        std::getline(std::cin, buffer);
        // 發(fā)送消息給server
        sendto(sockfd, buffer.c_str(), buffer.size(), 0,
               (const struct sockaddr *)&server, sizeof(server)); // 首次調(diào)用sendto函數(shù)的時(shí)候,我們的client會(huì)自動(dòng)bind自己的ip和port
    }

    close(sockfd);

    return 0;
}

?? udpServer.cc 服務(wù)器

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <unordered_map>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "log.hpp"
static void Usage(const std::string porc)
{
    std::cout << "Usage:\n\t" << porc << " port [ip]" << std::endl;
}

class udpServer
{
    public:
    udpServer(int port, std::string ip = "") : port_((uint16_t)port), ip_(ip), sockfd_(-1)
    {
    }
    ~udpServer()
    {
    }

    void init()//初始化函數(shù)
    {
        //1.創(chuàng)建套接字
        sockfd_=socket(AF_INET,SOCK_DGRAM,0);
        //  參數(shù)1:套接字的協(xié)議族     `AF_INET`: IPv4協(xié)議族,用于Internet地址。
        //參數(shù)2:套接字類型    `SOCK_DGRAM`: 面向消息的套接字,用于不可靠的、固定長度的數(shù)據(jù)傳輸(如UDP)。
        //參數(shù)3:套接字的協(xié)議    0,讓系統(tǒng)自動(dòng)選擇合適的協(xié)議。
        if(sockfd_<0) //創(chuàng)建失敗 打印日志信息
        {
              logMessage(FATAL, "socket:%s:%d", strerror(errno), sockfd_);
            exit(1);
        }

        //到這說明 創(chuàng)建成功
         logMessage(DEBUG, "socket create success: %d", sockfd_);
         struct sockaddr_in local;  // local在哪里開辟的空間? 用戶棧 -> 臨時(shí)變量 -> 寫入內(nèi)核中
            bzero(&local, sizeof(local)); // memset
        // 填充協(xié)議家族,域
        local.sin_family = AF_INET;
        // 填充服務(wù)器對(duì)應(yīng)的端口號(hào)信息,一定是會(huì)發(fā)給對(duì)方的,port_一定會(huì)到網(wǎng)絡(luò)中
        local.sin_port = htons(port_);
        // 服務(wù)器都必須具有IP地址,"xx.yy.zz.aaa",字符串風(fēng)格點(diǎn)分十進(jìn)制 -> 4字節(jié)IP -> uint32_t ip
        // INADDR_ANY(0): 程序員不關(guān)心會(huì)bind到哪一個(gè)ip, 任意地址bind,強(qiáng)烈推薦的做法,所有服務(wù)器一般的做法
        // inet_addr: 指定填充確定的IP,特殊用途,或者測試時(shí)使用,除了做轉(zhuǎn)化,還會(huì)自動(dòng)給我們進(jìn)行 h—>n
        local.sin_addr.s_addr = ip_.empty() ? htonl(INADDR_ANY) : inet_addr(ip_.c_str());
        // 2.2 bind 網(wǎng)絡(luò)信息
        if (bind(sockfd_, (const struct sockaddr *)&local, sizeof(local)) == -1)
        {
            logMessage(FATAL, "bind: %s:%d", strerror(errno), sockfd_);
            exit(2);
        }
        logMessage(DEBUG, "socket bind success: %d", sockfd_);
        // done
    }

     void start()
    {
        // 服務(wù)器設(shè)計(jì)的時(shí)候,服務(wù)器都是死循環(huán)
        char inbuffer[1024];  //將來讀取到的數(shù)據(jù),都放在這里
        char outbuffer[1024]; //將來發(fā)送的數(shù)據(jù),都放在這里
        while (true)
        {
            struct sockaddr_in peer;      //輸出型參數(shù)
            socklen_t len = sizeof(peer); //輸入輸出型參數(shù)

            // demo2
            //  UDP無連接的
            //  對(duì)方給你發(fā)了消息,你想不想給對(duì)方回消息?要的!后面的兩個(gè)參數(shù)是輸出型參數(shù)
            ssize_t s = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0,
                                 (struct sockaddr *)&peer, &len);
            if (s > 0)
            {
                inbuffer[s] = 0; //當(dāng)做字符串
            }
            else if (s == -1)
            {
                logMessage(WARINING, "recvfrom: %s:%d", strerror(errno), sockfd_);
                continue;
            }
            // 讀取成功的,除了讀取到對(duì)方的數(shù)據(jù),你還要讀取到對(duì)方的網(wǎng)絡(luò)地址[ip:port]
            std::string peerIp = inet_ntoa(peer.sin_addr);       //拿到了對(duì)方的IP
            uint32_t peerPort = ntohs(peer.sin_port); // 拿到了對(duì)方的port

            checkOnlineUser(peerIp, peerPort, peer); //如果存在,什么都不做,如果不存在,就添加

            // 打印出來客戶端給服務(wù)器發(fā)送過來的消息
            logMessage(NOTICE, "[%s:%d]# %s", peerIp.c_str(), peerPort, inbuffer);

            // for(int i = 0; i < strlen(inbuffer); i++)
            // {
            //     if(isalpha(inbuffer[i]) && islower(inbuffer[i])) outbuffer[i] = toupper(inbuffer[i]);
            //     else outbuffer[i] = toupper(inbuffer[i]);
            // }
            messageRoute(peerIp, peerPort,inbuffer); //消息路由

            // 線程池!

            // sendto(sockfd_, outbuffer, strlen(outbuffer), 0, (struct sockaddr*)&peer, len);

            // demo1
            // logMessage(NOTICE, "server 提供 service 中....");
            // sleep(1);
        }
    }

    void checkOnlineUser(std::string &ip, uint32_t port, struct sockaddr_in &peer)
    {
        std::string key = ip;
        key += ":";
        key += std::to_string(port);
        auto iter = users.find(key);
        if(iter == users.end())
        {
            users.insert({key, peer});
        }
        else
        {
            // iter->first, iter->second->
            // do nothing
        }
    }

    void messageRoute(std::string ip, uint32_t port, std::string info)
    {

        std::string message = "[";
        message += ip;
        message += ":";
        message += std::to_string(port);
        message += "]# ";
        message += info;

        for(auto &user : users)
        {
            sendto(sockfd_, message.c_str(), message.size(), 0, (struct sockaddr*)&(user.second), sizeof(user.second));
        }
    }


    private:
    // 服務(wù)器必須得有端口號(hào)信息
    uint16_t port_;
    // 服務(wù)器必須得有ip地址
    std::string ip_;
    // 服務(wù)器的socket fd信息
    int sockfd_;
    // onlineuser
    std::unordered_map<std::string, struct sockaddr_in> users;

};
int main(int argc, char *argv[])
{
    if (argc != 2 && argc != 3) //反面:argc == 2 || argc == 3
    {
        Usage(argv[0]);
        exit(3);
    }
    uint16_t port = atoi(argv[1]);
    std::string ip;
    if (argc == 3)
    {
        ip = argv[2];
    }

    udpServer svr(port, ip);
    svr.init();
    svr.start();

    return 0;
}

?? makefile文件

.PHONY:all
all:udpClient udpServer

udpClient: UCPClient.cc
	g++ -o $@ $^ -std=c++11 -lpthread
udpServer:UDPServer.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -f udpClient udpServer

運(yùn)行:

【網(wǎng)絡(luò)編程】利用套接字實(shí)現(xiàn)一個(gè)簡單的網(wǎng)絡(luò)通信(UDP實(shí)現(xiàn)聊天室 附上源碼),Linux,網(wǎng)絡(luò),udp,服務(wù)器
設(shè)置對(duì)對(duì)應(yīng)的端口號(hào)就行

【網(wǎng)絡(luò)編程】利用套接字實(shí)現(xiàn)一個(gè)簡單的網(wǎng)絡(luò)通信(UDP實(shí)現(xiàn)聊天室 附上源碼),Linux,網(wǎng)絡(luò),udp,服務(wù)器

?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??文章來源地址http://www.zghlxwxcb.cn/news/detail-634935.html

到了這里,關(guān)于【網(wǎng)絡(luò)編程】利用套接字實(shí)現(xiàn)一個(gè)簡單的網(wǎng)絡(luò)通信(UDP實(shí)現(xiàn)聊天室 附上源碼)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【網(wǎng)絡(luò)編程】TCP流套接字編程(TCP實(shí)現(xiàn)回顯服務(wù)器)

    【網(wǎng)絡(luò)編程】TCP流套接字編程(TCP實(shí)現(xiàn)回顯服務(wù)器)

    Socket(既能給客戶端使用,也能給服務(wù)器使用) 構(gòu)造方法 基本方法: ServerSocket(只能給服務(wù)器使用) 構(gòu)造方法: 基本方法: 客戶端代碼示例: 服務(wù)器代碼示例: 運(yùn)行結(jié)果: 代碼執(zhí)行流程: 服務(wù)器啟動(dòng),阻塞在accept,等待客戶端建立連接. 客戶端啟動(dòng).這里的new操作會(huì)觸發(fā)和服務(wù)器之間建立連

    2024年04月25日
    瀏覽(23)
  • 「網(wǎng)絡(luò)編程」第二講:網(wǎng)絡(luò)編程socket套接字(三)_ 簡單TCP網(wǎng)絡(luò)通信程序的實(shí)現(xiàn)

    「網(wǎng)絡(luò)編程」第二講:網(wǎng)絡(luò)編程socket套接字(三)_ 簡單TCP網(wǎng)絡(luò)通信程序的實(shí)現(xiàn)

    「前言」文章是關(guān)于網(wǎng)絡(luò)編程的socket套接字方面的,上一篇是網(wǎng)絡(luò)編程socket套接字(二),下面開始講解!? 「歸屬專欄」網(wǎng)絡(luò)編程 「主頁鏈接」個(gè)人主頁 「筆者」楓葉先生(fy) 「楓葉先生有點(diǎn)文青病」「每篇一句」 I?do?not?know?where?to?go,but?I?have?been?on?the?road. 我不知

    2024年02月11日
    瀏覽(29)
  • 網(wǎng)絡(luò)編程套接字 | UDP套接字

    網(wǎng)絡(luò)編程套接字 | UDP套接字

    前面的文章中我們敘述了網(wǎng)絡(luò)編程套接字的一些預(yù)備知識(shí)點(diǎn),從本文開始我們就將開始UDP套接字的編寫。本文中的服務(wù)端與客戶端都是在阿里云的云服務(wù)器進(jìn)行編寫與測試的。 在v1的版本中我們先來使用一下前面講過得一些接口,簡單的構(gòu)建一個(gè)udp服務(wù)器: 然后運(yùn)行上述的程

    2024年02月09日
    瀏覽(385)
  • 【探索Linux】P.28(網(wǎng)絡(luò)編程套接字 —— 簡單的UDP網(wǎng)絡(luò)程序模擬實(shí)現(xiàn))

    【探索Linux】P.28(網(wǎng)絡(luò)編程套接字 —— 簡單的UDP網(wǎng)絡(luò)程序模擬實(shí)現(xiàn))

    在前一篇文章中,我們?cè)敿?xì)介紹了UDP協(xié)議和TCP協(xié)議的特點(diǎn)以及它們之間的異同點(diǎn)。 本文將延續(xù)上文內(nèi)容,重點(diǎn)討論簡單的UDP網(wǎng)絡(luò)程序模擬實(shí)現(xiàn) 。通過本文的學(xué)習(xí),讀者將能夠深入了解UDP協(xié)議的實(shí)際應(yīng)用,并掌握如何編寫簡單的UDP網(wǎng)絡(luò)程序。讓我們一起深入探討UDP網(wǎng)絡(luò)程序的

    2024年04月08日
    瀏覽(584)
  • 【探索Linux】P.29(網(wǎng)絡(luò)編程套接字 —— 簡單的TCP網(wǎng)絡(luò)程序模擬實(shí)現(xiàn))

    【探索Linux】P.29(網(wǎng)絡(luò)編程套接字 —— 簡單的TCP網(wǎng)絡(luò)程序模擬實(shí)現(xiàn))

    在前一篇文章中,我們?cè)敿?xì)介紹了UDP協(xié)議和TCP協(xié)議的特點(diǎn)以及它們之間的異同點(diǎn)。 本文將延續(xù)上文內(nèi)容,重點(diǎn)討論簡單的TCP網(wǎng)絡(luò)程序模擬實(shí)現(xiàn) 。通過本文的學(xué)習(xí),讀者將能夠深入了解TCP協(xié)議的實(shí)際應(yīng)用,并掌握如何編寫簡單的TCP網(wǎng)絡(luò)程序。讓我們一起深入探討TCP網(wǎng)絡(luò)程序的

    2024年04月14日
    瀏覽(97)
  • 【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字

    【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字

    目錄 一、預(yù)備知識(shí) 1、網(wǎng)絡(luò)通信理解 2、源IP地址和目的IP地址 3、端口號(hào) 二、網(wǎng)絡(luò)字節(jié)序 三、socket編程接口 1、socket常見API 2、sockaddr結(jié)構(gòu) 3、sockaddr結(jié)構(gòu)體 3.1、sockaddr結(jié)構(gòu)體 3.2、sockaddr_in結(jié)構(gòu)體 四、簡單的UDP網(wǎng)絡(luò)程序 1、創(chuàng)建套接字接口 2、綁定端口號(hào) 3、服務(wù)器運(yùn)行 4、創(chuàng)建

    2024年02月14日
    瀏覽(91)
  • 【網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(一)

    【網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(一)

    端口號(hào)(port)是傳輸層協(xié)議的內(nèi)容. 端口號(hào)是一個(gè)2字節(jié)16位的整數(shù); 端口號(hào)用來標(biāo)識(shí)一個(gè)進(jìn)程, 告訴操作系統(tǒng), 當(dāng)前的這個(gè)數(shù)據(jù)要交給哪一個(gè)進(jìn)程來處理; IP地址 + 端口號(hào)能夠標(biāo)識(shí)網(wǎng)絡(luò)上的某一臺(tái)主機(jī)的某一個(gè)進(jìn)程; 一個(gè)端口號(hào)只能被一個(gè)進(jìn)程占用. 既然端口號(hào)是用來標(biāo)識(shí)一個(gè)進(jìn)程,

    2024年02月12日
    瀏覽(646)
  • 網(wǎng)絡(luò)編程2(套接字編程)

    網(wǎng)絡(luò)編程2(套接字編程)

    套接字編程:如何編寫一個(gè)網(wǎng)絡(luò)通信程序 1.網(wǎng)絡(luò)通信的數(shù)據(jù)中都會(huì)包含一個(gè)完整的五元組: sip,sport,dip,dport,protocol(源IP,源端口,對(duì)端IP,對(duì)端端口,協(xié)議) 五元組完整的描述了數(shù)據(jù)從哪來,到哪去,用什么數(shù)據(jù)格式 2.網(wǎng)絡(luò)通信–兩個(gè)主機(jī)進(jìn)程之間的通信:客戶端服務(wù)

    2023年04月09日
    瀏覽(116)
  • 【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一)

    【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一)

    在前面我們說過可以使用IP地址來標(biāo)識(shí)一臺(tái)主機(jī),但是我們光有IP地址就可以完成通信了嘛? 答案是:不可以,當(dāng)我們的主機(jī)接收到了數(shù)據(jù)以后還要確定這個(gè)數(shù)據(jù)是發(fā)送給哪一個(gè)進(jìn)程的,兩臺(tái)主機(jī)的兩個(gè)軟件進(jìn)行網(wǎng)絡(luò)通信時(shí),我們還需要有一個(gè)其他的標(biāo)識(shí)來區(qū)分出這個(gè)數(shù)據(jù)要給

    2024年02月06日
    瀏覽(96)
  • 【JaveEE】網(wǎng)絡(luò)編程之TCP套接字、UDP套接字

    【JaveEE】網(wǎng)絡(luò)編程之TCP套接字、UDP套接字

    目錄 1.網(wǎng)絡(luò)編程的基本概念 1.1為什么需要網(wǎng)絡(luò)編程? 1.2服務(wù)端與用戶端 1.3網(wǎng)絡(luò)編程五元組? 1.4套接字的概念 2.UDP套接字編程 2.1UDP套接字的特點(diǎn) ?2.2UDP套接字API 2.2.1DatagramSocket類 2.2.2DatagramPacket類? 2.2.3基于UDP的回顯程序 2.2.4基于UDP的單詞查詢? 3.TCP套接字編程 3.1TCP套接字的特

    2023年04月13日
    瀏覽(915)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包