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

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器)

這篇具有很好參考價(jià)值的文章主要介紹了【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器)。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器)

作者:愛寫代碼的剛子

時(shí)間:2024.4.4

前言:本篇博客主要介紹TCP及其服務(wù)器編碼

地址轉(zhuǎn)換函數(shù)

只介紹基于IPv4的socket網(wǎng)絡(luò)編程,sockaddr_in中的成員struct in_addr sin_addr表示32位 的IP地址 但是我們通常用點(diǎn)分十進(jìn)制的字符串表示IP地址,以下函數(shù)可以在字符串表示和in_addr表示之間轉(zhuǎn)換

字符串轉(zhuǎn)in_addr的函數(shù)

#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
int inet_pton(int family,const char *strptr,void *addrptr);

in_addr轉(zhuǎn)字符串的函數(shù):

char *inet_ntoa(struct in_addr inaddr);
const char *inet_ntop(int family,const void *addrptr,char *strptr,size_t len);

其中inet_ptoninet_ntop不僅可以轉(zhuǎn)換IPv4的in_addr,還可以轉(zhuǎn)換IPv6的in6_addr,因此函數(shù)接口是void *addrptr。

關(guān)于inet_ntoa

inet_ntoa這個(gè)函數(shù)返回了一個(gè)char*, 很顯然是這個(gè)函數(shù)自己在內(nèi)部為我們申請了一塊內(nèi)存來保存ip的結(jié)果. 那么是否需要調(diào)用者手動釋放呢?

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

inet_ntoa函數(shù)將這個(gè)返回結(jié)果放到了靜態(tài)存儲區(qū),不需要我們進(jìn)行手動釋放,如果多次調(diào)用會出現(xiàn)問題嗎?

  • 進(jìn)行一段代碼演示:

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

因?yàn)閕net_ntoa把結(jié)果放到自己內(nèi)部的一個(gè)靜態(tài)存儲區(qū),這樣第二次調(diào)用時(shí)的結(jié)果會覆蓋掉上一次的結(jié)果

  • 在APUE中,明確提出inet_ntoa不是線程安全函數(shù)
  • 但在centos7上測試沒有出現(xiàn)問題,可能內(nèi)部的實(shí)現(xiàn)加了互斥鎖
  • 在多線程環(huán)境下,推薦使用inet_ntop,這個(gè)函數(shù)由調(diào)用者提供一個(gè)緩沖區(qū)保存結(jié)果,可以規(guī)避線程安全問題
  • 可以用以下代碼進(jìn)行多線程的測試
// Created Time:    2024-04-04 10:48:05
// Modified Time:   2024-04-04 11:00:43

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
int cnt =100;

void* Func1(void* p) {
    struct sockaddr_in* addr = (struct sockaddr_in*)p;
    while (cnt>0) {
      char* ptr = inet_ntoa(addr->sin_addr);
      printf("addr1: %s,cnt: %d\n", ptr,cnt);
      cnt--;
    }
    return NULL;
}
void* Func2(void* p) {
    struct sockaddr_in* addr = (struct sockaddr_in*)p;
    while (cnt>0) {
      char* ptr = inet_ntoa(addr->sin_addr);
      printf("addr2: %s,cnt: %d\n", ptr,cnt);
      cnt--;
    }
    return NULL;
}
int main() {
  pthread_t tid1 = 0;
  pthread_t tid2 = 0;
  struct sockaddr_in addr1;
  struct sockaddr_in addr2;
  addr1.sin_addr.s_addr = 0;
  addr2.sin_addr.s_addr = 0xffffffff;
  pthread_create(&tid1, NULL, Func1, &addr1);
  pthread_create(&tid2, NULL, Func2, &addr2);
  pthread_join(tid1, NULL);
  pthread_join(tid2, NULL);
  return 0;
}

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

所以在我這個(gè)環(huán)境下他并不是線程安全的

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

TCP sockot API詳解

頭文件:<sys/socket.h>

socket()

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

參數(shù)

  • domain(域):這個(gè)參數(shù)指定了套接字的地址簇,也稱為協(xié)議簇
    • AF_INET:IPv4 地址族。
    • AF_INET6:IPv6 地址族。
    • AF_UNIX:本地通信(Unix 域套接字)。
    • AF_PACKET:低級網(wǎng)絡(luò)接口。
  • type(類型)
    • SOCK_STREAM:面向連接的流套接字,提供可靠的、雙向的、基于字節(jié)的數(shù)據(jù)傳輸。
    • SOCK_DGRAM:數(shù)據(jù)報(bào)套接字,提供不可靠的、無連接的、固定長度的數(shù)據(jù)傳輸。
    • SOCK_RAW:原始套接字,直接訪問網(wǎng)絡(luò)層。
  • protocol(協(xié)議)
    • 0:表示使用默認(rèn)協(xié)議。
    • IPPROTO_TCP:TCP 協(xié)議。
    • IPPROTO_UDP:UDP 協(xié)議。
    • IPPROTO_ICMP:ICMP 協(xié)議。
  • 對于 AF_INET(IPv4 地址族),默認(rèn)協(xié)議通常是 IPPROTO_TCP(TCP 協(xié)議)。
  • 對于 AF_INET6(IPv6 地址族),默認(rèn)協(xié)議通常是 IPPROTO_TCP(TCP 協(xié)議)。
  • 對于 AF_UNIX(Unix 域套接字),默認(rèn)協(xié)議是不適用的,因?yàn)樗鼈冊诒镜赝ㄐ派瞎ぷ?,不涉及到傳輸層協(xié)議。
  • socket()打開一個(gè)網(wǎng)絡(luò)通訊端口,如果成功的話,就像open()一樣返回一個(gè)文件描述符;

  • 應(yīng)用程序可以像讀寫文件一樣用read/write在網(wǎng)絡(luò)上收發(fā)數(shù)據(jù);

  • 如果socket()調(diào)用出錯(cuò)則返回-1;

  • 對于IPv4, family參數(shù)指定為AF_INET;

  • 對于TCP協(xié)議,type參數(shù)指定為SOCK_STREAM, 表示面向流的傳輸協(xié)議

  • protocol參數(shù)的介紹從略,指定為0即可。

bind()

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

  • 服務(wù)器程序所監(jiān)聽的網(wǎng)絡(luò)地址和端口號通常是固定不變的,客戶端程序得知服務(wù)器程序的地址和端口號后 就可以向服務(wù)器發(fā)起連接; 服務(wù)器需要調(diào)用bind綁定一個(gè)固定的網(wǎng)絡(luò)地址和端口號;

  • bind()成功返回0,失敗返回-1。

  • bind()的作用是將參數(shù)sockfd和myaddr綁定在一起, 使sockfd這個(gè)用于網(wǎng)絡(luò)通訊的文件描述符監(jiān)聽 myaddr所描述的地址和端口號;

  • struct sockaddr *是一個(gè)通用指針類型,myaddr參數(shù)實(shí)際上可以接受多種協(xié)議的sockaddr結(jié) 構(gòu)體,而它們的長度各不相同,所以需要第三個(gè)參數(shù)addrlen指定結(jié)構(gòu)體的長度;

我們的程序中對myaddr參數(shù)是這樣初始化的:

bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
  1. 將整個(gè)結(jié)構(gòu)體清零;
  2. 設(shè)置地址類型為AF_INET;
  3. 網(wǎng)絡(luò)地址為INADDR_ANY, 這個(gè)宏表示本地的任意IP地址,因?yàn)榉?wù)器可能有多個(gè)網(wǎng)卡,每個(gè)網(wǎng)卡也可能綁定多個(gè)IP 地址, 這樣設(shè)置可以在所有的IP地址上監(jiān)聽,直到與某個(gè)客戶端建立了連接時(shí)才確定下來到底用哪個(gè)IP 地址;
  4. 端口號為SERV_PORT,我們定義為9999
listen()

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

  • listen()聲明sockfd處于監(jiān)聽狀態(tài), 并且最多允許有backlog個(gè)客戶端處于連接等待狀態(tài), 如果接收到更多 的連接請求就忽略, 這里設(shè)置不會太大(一般是5), 具體細(xì)節(jié)同學(xué)們課后深入研究;
  • listen()成功返回0,失敗返回-1;

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

accept();

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

  • 三次握手完成后, 服務(wù)器調(diào)用accept()接受連接;
  • 如果服務(wù)器調(diào)用accept()時(shí)還沒有客戶端的連接請求,就阻塞等待直到有客戶端連接上來;
  • addr,addrlen是一個(gè)輸出型參數(shù),accept()返回時(shí)傳出客戶端的地址和端口號;
  • 如果給addr 參數(shù)傳NULL,表示不關(guān)心客戶端的地址;
  • addrlen參數(shù)是一個(gè)傳入傳出參數(shù)(value-result argument), 傳入的是調(diào)用者提供的, 緩沖區(qū)addr的長度 以避免緩沖區(qū)溢出問題, 傳出的是客戶端地址結(jié)構(gòu)體的實(shí)際長度(有可能沒有占滿調(diào)用者提供的緩沖區(qū));
  • accept的返回值也是一個(gè)文件描述符
connect

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

  • 客戶端需要調(diào)用connect()連接服務(wù)器;
  • connect和bind的參數(shù)形式一致, 區(qū)別在于bind的參數(shù)是自己的地址, 而connect的參數(shù)是對方的地址;
  • connect()成功返回0,出錯(cuò)返回-1;

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

注意,云服務(wù)器的公有ip無法直接綁定,但可以綁定本地的ip

local.sin_addr.s_addr = INADDR_ANY;//將套接字綁定到所有可用的網(wǎng)絡(luò)接口,而不是綁定到特定的網(wǎng)絡(luò)接口。這樣就能綁定服務(wù)器的公有ip了

添加這個(gè)語句就能綁定服務(wù)器的公有ip了

  • 向網(wǎng)絡(luò)發(fā)送數(shù)據(jù),比如字符串等,使用的接口會自動將主機(jī)序列轉(zhuǎn)換為網(wǎng)絡(luò)序列

  • 獲取客戶端的ip以及端口號

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

  • 接收并發(fā)送消息

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

  • 添加重連功能,服務(wù)器處理客戶端中途讀時(shí)的錯(cuò)誤

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

代碼:

TcpClient.cc

#include <iostream>
//sock四件套
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>



void Usage(const std::string &proc)
{
    std::cout << "\n\rUsage: " << proc << " serverip serverport\n"
              << std::endl;
}

// ./tcpclient serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport);
    inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr));

    while (true)
    {
        int cnt = 5;
        int isreconnect = false;
        int sockfd = 0;
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
        {
            std::cerr << "socket error" << std::endl;
            return 1;
        }
        do
        {
            //tcp客戶端要不要綁定,tcp客戶端要綁定,只是不用顯示地寫出來(由操作系統(tǒng)根據(jù)需求進(jìn)行隨機(jī)選擇)
            //UDP在首次發(fā)送數(shù)據(jù)時(shí)確定端口號

            //tcp是面向鏈接的,客戶端發(fā)起connect的時(shí)候進(jìn)行自動隨機(jī)bind
            int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
            if (n < 0)
            {
                isreconnect = true;
                cnt--;
                std::cerr << "connect error..., reconnect: " << cnt << std::endl;
                sleep(2);
            }
            else
            {
                break;
            }
        } while (cnt && isreconnect);

        if (cnt == 0)
        {
            std::cerr << "user offline..." << std::endl;
            break;
        }

         while (true)
         {
            std::string message;
            std::cout << "Please Enter# ";
            std::getline(std::cin, message);

            int n = write(sockfd, message.c_str(), message.size());
            if (n < 0)
            {
                std::cerr << "write error..." << std::endl;
                // break;
            }

            char inbuffer[4096];
            n = read(sockfd, inbuffer, sizeof(inbuffer));
            if (n > 0)
            {
                inbuffer[n] = 0;
                std::cout << inbuffer << std::endl;
            }
            else{
                break;
            }
        }
        close(sockfd);
    }

    return 0;
}

TcpServer.cc

#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstdlib>
#include <cstring>
#include <arpa/inet.h> //sockaddr_in類型頭文件
#include <netinet/in.h>
#include "Log.hpp"
#include <sys/wait.h>
#include <signal.h>

const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 10; // 一般不要設(shè)置太大

Log lg;

enum
{
    UsageError = 1,
    SocketError,
    BindError,
    ListenError
};


class TcpServer
{
public:
    TcpServer(const uint16_t &port, const std::string &ip = defaultip) : listensock_(defaultfd), port_(port), ip_(ip)
    {
    }
    void InitServer() // 盡量不要將有風(fēng)險(xiǎn)的事情放進(jìn)構(gòu)造函數(shù)中
    {
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock_ < 0)
        {
            lg(Fatal, "create socket,errno: %d, errstring: %s", errno, strerror(errno));
            exit(SocketError);
        }
        lg(Info, "create socket success, sockfd: %d", listensock_);
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &(local.sin_addr));
        local.sin_addr.s_addr = INADDR_ANY; // 將套接字綁定到所有可用的網(wǎng)絡(luò)接口,而不是綁定到特定的網(wǎng)絡(luò)接口。這樣就能綁定服務(wù)器的ip了

        if (bind(listensock_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(BindError);
        }
        lg(Info, "Bind socket success, sockfd: %d", listensock_);

        if (listen(listensock_, backlog) < 0)
        {
            lg(Fatal, "bind error: %d, errstring: %s", errno, strerror(errno));
            exit(ListenError);
        }
        lg(Info, "Listen socket success, sockfd: %d", listensock_);
    }


    void Start()
    {
        // signal(SIGCHLD,SIG_IGN);
        lg(Info, "tcpServer is running...");
        for (;;)
        {
            // 獲取新鏈接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listensock_, (struct sockaddr *)&client, &len); // 為什么這里又多了一個(gè)fd,為什么不用之前的初始化服務(wù)器的sockfd_
            // 這里的listensock_只是將底層的鏈接獲取上來,但是真正提供服務(wù)的是accept返回的sockfd
            // 一般listensock_為3,sockfd為4
            if (sockfd < 0)
            {
                lg(Warning, "accept error: %d,errstring: %s", errno, strerror(errno));
                // 獲取一個(gè)鏈接失敗了不一定要退出,獲取下一個(gè)鏈接即可
                continue;
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            // 根據(jù)新鏈接來進(jìn)行通信
            lg(Info, "get a new link...,sockfd: %d,client ip: %s,client port: %d", sockfd, clientip, clientport);

            Service(sockfd, clientip, clientport);
            close(sockfd);


           
        }
    }
    void Service(int sockfd, const std::string &clientip, const uint16_t &clientport)
    {
        while (true)
        {
            // 讀消息直接使用read函數(shù)即可
            char buffer[4096];

            ssize_t n = read(sockfd, buffer, sizeof(buffer));
            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << "client say# " << buffer << std::endl;
                std::string echo_string = "tcpserver echo# ";
                echo_string += buffer;

                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if (n == 0)
            {
                lg(Info, "%s:%d quit,server close sockfd: %d", clientip.c_str(), clientport, sockfd);
                break;
            }
            else
            {
                lg(Warning, "read error, sockfd: %d, client ip: %s,client port: %d", sockfd, clientip.c_str(), clientport);
                break;
            }
        }
    }
    ~TcpServer() {}

private:
    int listensock_;
    uint16_t port_;
    std::string ip_;
};

但是這種單進(jìn)程服務(wù)器只能處理一個(gè)鏈接,明顯無法滿足我們的需求,所以我們進(jìn)行改進(jìn):

  • 多線程版的服務(wù)器

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

父進(jìn)程的sockfd關(guān)閉了不會對子進(jìn)程產(chǎn)生影響,因?yàn)樽舆M(jìn)程和父進(jìn)程各自有獨(dú)立的文件描述符指針(文件描述符fd中也存在引用計(jì)數(shù)

優(yōu)雅的讓父進(jìn)程不會阻塞等待(子進(jìn)程已經(jīng)退出了),同時(shí)讓系統(tǒng)領(lǐng)養(yǎng)進(jìn)程并自動回收

if(fork() > 0) exit(0);

還可以使用信號,讓父進(jìn)程不用等待

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

  • 還可以使用多線程(參考):

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

注意要將Routine變?yōu)閟tatic函數(shù),再使用pthread_detach(pthread_self())進(jìn)行線程分離

線程池:

【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器),Linux,網(wǎng)絡(luò),服務(wù)器,linux

完整的TCP服務(wù)器代碼(線程池版)

TcpClient.cc

#include <iostream>
//sock四件套
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>



void Usage(const std::string &proc)
{
    std::cout << "\n\rUsage: " << proc << " serverip serverport\n"
              << std::endl;
}

// ./tcpclient serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport);
    inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr));

    while (true)
    {
        int cnt = 5;
        int isreconnect = false;
        int sockfd = 0;
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
        {
            std::cerr << "socket error" << std::endl;
            return 1;
        }
        do
        {
            //tcp客戶端要不要綁定,tcp客戶端要綁定,只是不用顯示地寫出來(由操作系統(tǒng)根據(jù)需求進(jìn)行隨機(jī)選擇)
            //UDP在首次發(fā)送數(shù)據(jù)時(shí)確定端口號

            //tcp是面向鏈接的,客戶端發(fā)起connect的時(shí)候進(jìn)行自動隨機(jī)bind
            int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
            if (n < 0)
            {
                isreconnect = true;
                cnt--;
                std::cerr << "connect error..., reconnect: " << cnt << std::endl;
                sleep(2);
            }
            else
            {
                break;
            }
        } while (cnt && isreconnect);

        if (cnt == 0)
        {
            std::cerr << "user offline..." << std::endl;
            break;
        }

         while (true)
         {
            std::string message;
            std::cout << "Please Enter# ";
            std::getline(std::cin, message);

            int n = write(sockfd, message.c_str(), message.size());
            if (n < 0)
            {
                std::cerr << "write error..." << std::endl;
                // break;
            }

            char inbuffer[4096];
            n = read(sockfd, inbuffer, sizeof(inbuffer));
            if (n > 0)
            {
                inbuffer[n] = 0;
                std::cout << inbuffer << std::endl;
            }
            else{
                break;
            }
        }
        close(sockfd);
    }

    return 0;
}

TcpServer.cc

#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstdlib>
#include <cstring>
#include <arpa/inet.h> //sockaddr_in類型頭文件
#include <netinet/in.h>
#include "Log.hpp"
#include <sys/wait.h>
#include <signal.h>
#include "Task.hpp"
#include "ThreadPool.hpp"
const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 10; // 一般不要設(shè)置太大

Log lg;

enum
{
    UsageError = 1,
    SocketError,
    BindError,
    ListenError
};
class TcpServer;

class ThreadData
{
public:
    ThreadData(int fd, const std::string &ip, const uint16_t &p, TcpServer *t) : sockfd(fd), clientip(ip), clientport(p), tsvr(t)
    {}

public:
    int sockfd;
    std::string clientip;
    uint16_t clientport;
    TcpServer *tsvr;
};

class TcpServer
{
public:
    TcpServer(const uint16_t &port, const std::string &ip = defaultip) : listensock_(defaultfd), port_(port), ip_(ip)
    {
    }
    void InitServer() // 盡量不要將有風(fēng)險(xiǎn)的事情放進(jìn)構(gòu)造函數(shù)中
    {
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock_ < 0)
        {
            lg(Fatal, "create socket,errno: %d, errstring: %s", errno, strerror(errno));
            exit(SocketError);
        }
        lg(Info, "create socket success, sockfd: %d", listensock_);
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &(local.sin_addr));
        local.sin_addr.s_addr = INADDR_ANY; // 將套接字綁定到所有可用的網(wǎng)絡(luò)接口,而不是綁定到特定的網(wǎng)絡(luò)接口。這樣就能綁定服務(wù)器的ip了

        if (bind(listensock_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(BindError);
        }
        lg(Info, "Bind socket success, sockfd: %d", listensock_);

        if (listen(listensock_, backlog) < 0)
        {
            lg(Fatal, "bind error: %d, errstring: %s", errno, strerror(errno));
            exit(ListenError);
        }
        lg(Info, "Listen socket success, sockfd: %d", listensock_);
    }

    static void *Routine(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        td->tsvr->Service(td->sockfd, td->clientip, td->clientport); //???
        delete td;
        return nullptr;
    }

    void Start()
    {
        ThreadPool<Task>::GetInstance()->Start();
        // signal(SIGCHLD,SIG_IGN);
        lg(Info, "tcpServer is running...");
        for (;;)
        {
            // 獲取新鏈接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listensock_, (struct sockaddr *)&client, &len); // 為什么這里又多了一個(gè)fd,為什么不用之前的初始化服務(wù)器的sockfd_
            // 這里的listensock_只是將底層的鏈接獲取上來,但是真正提供服務(wù)的是accept返回的sockfd
            // 一般listensock_為3,sockfd為4
            if (sockfd < 0)
            {
                lg(Warning, "accept error: %d,errstring: %s", errno, strerror(errno));
                // 獲取一個(gè)鏈接失敗了不一定要退出,獲取下一個(gè)鏈接即可
                continue;
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            // 根據(jù)新鏈接來進(jìn)行通信
            lg(Info, "get a new link...,sockfd: %d,client ip: %s,client port: %d", sockfd, clientip, clientport);

            // Service(sockfd, clientip, clientport);
            // close(sockfd);


            //多進(jìn)程版
            // pid_t id = fork();
            // if (id == 0)
            // {
            //     // child
            //     close(listensock_); // 由于子進(jìn)程不需要父進(jìn)程的listensock_,所以我們需要將它關(guān)閉
            //     if (fork() > 0)
            //         exit(0);
            //     Service(sockfd, clientip, clientport); // 孫子進(jìn)程, system 領(lǐng)養(yǎng)
            //     close(sockfd);
            //     exit(0);
            // }
            // close(sockfd);
            // // father
            // pid_t rid = waitpid(id, nullptr, 0);
            // (void)rid;


            //多線程版本
            // ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);
            
            
            //線程池版本
            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }
    void Service(int sockfd, const std::string &clientip, const uint16_t &clientport)
    {
        while (true)
        {
            // 讀消息直接使用read函數(shù)即可
            char buffer[4096];

            ssize_t n = read(sockfd, buffer, sizeof(buffer));
            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << "client say# " << buffer << std::endl;
                std::string echo_string = "tcpserver echo# ";
                echo_string += buffer;

                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if (n == 0)
            {
                lg(Info, "%s:%d quit,server close sockfd: %d", clientip.c_str(), clientport, sockfd);
                break;
            }
            else
            {
                lg(Warning, "read error, sockfd: %d, client ip: %s,client port: %d", sockfd, clientip.c_str(), clientport);
                break;
            }
        }
    }
    ~TcpServer() {}

private:
    int listensock_;
    uint16_t port_;
    std::string ip_;
};

Task.hpp

#pragma once
#include <iostream>
#include <string>
#include "Log.hpp"
#include "Init.hpp"

extern Log lg;
Init init;

class Task
{
public:
    Task(int sockfd, const std::string &clientip, const uint16_t &clientport)
        : sockfd_(sockfd), clientip_(clientip), clientport_(clientport)
    {
    }
    Task()
    {
    }
    void run()
    {
        // 測試代碼
        char buffer[4096];
        // Tcp是面向字節(jié)流的,你怎么保證,你讀取上來的數(shù)據(jù),是"一個(gè)" "完整" 的報(bào)文呢?
        ssize_t n = read(sockfd_, buffer, sizeof(buffer)); // BUG?
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "client key# " << buffer << std::endl;
            std::string echo_string = init.translation(buffer);

            // sleep(5);
            // // close(sockfd_);
            // lg(Warning, "close sockfd %d done", sockfd_);

            // sleep(2);
            n = write(sockfd_, echo_string.c_str(), echo_string.size()); // 100 fd 不存在
            if(n < 0)
            {
                lg(Warning, "write error, errno : %d, errstring: %s", errno, strerror(errno));
            }
        }
        else if (n == 0)
        {
            lg(Info, "%s:%d quit, server close sockfd: %d", clientip_.c_str(), clientport_, sockfd_);
        }
        else
        {
            lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd_, clientip_.c_str(), clientport_);
        }
        close(sockfd_);
    }
    void operator()()
    {
        run();
    }
    ~Task()
    {
    }

private:
    int sockfd_;
    std::string clientip_;
    uint16_t clientport_;
};

Init.hpp

#pragma once

#include <iostream>
#include <string>
#include <fstream>
#include <unordered_map>
#include "Log.hpp"

const std::string dictname = "./dict.txt";
const std::string sep = ":";

extern Log lg;

//yellow:黃色...
static bool Split(std::string &s, std::string *part1, std::string *part2)
{
    auto pos = s.find(sep);
    if(pos == std::string::npos) return false;
    *part1 = s.substr(0, pos);
    *part2 = s.substr(pos+1);
    return true;
}

class Init
{
public:
    Init()
    {
        std::ifstream in(dictname);
        if(!in.is_open())
        {
            lg(Fatal, "ifstream open %s error", dictname.c_str());
            exit(1);
        }
        std::string line;
        while(std::getline(in, line))
        {
            std::string part1, part2;
            Split(line, &part1, &part2);
            dict.insert({part1, part2});
        }
        in.close();
    }
    std::string translation(const std::string &key)
    {
        auto iter = dict.find(key);
        if(iter == dict.end()) return "Unknow";
        else return iter->second;
    }
private:
    std::unordered_map<std::string, std::string> dict;
};

Log.hpp

#pragma once

#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

#define SIZE 1024

#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4

#define Screen 1
#define Onefile 2
#define Classfile 3

#define LogFile "log.txt"

class Log
{
public:
    Log()
    {
        printMethod = Screen;
        path = "./log/";
    }
    void Enable(int method)
    {
        printMethod = method;
    }
    std::string levelToString(int level)
    {
        switch (level)
        {
        case Info:
            return "Info";
        case Debug:
            return "Debug";
        case Warning:
            return "Warning";
        case Error:
            return "Error";
        case Fatal:
            return "Fatal";
        default:
            return "None";
        }
    }

    
    void printLog(int level, const std::string &logtxt)
    {
        switch (printMethod)
        {
        case Screen:
            std::cout << logtxt << std::endl;
            break;
        case Onefile:
            printOneFile(LogFile, logtxt);
            break;
        case Classfile:
            printClassFile(level, logtxt);
            break;
        default:
            break;
        }
    }
    void printOneFile(const std::string &logname, const std::string &logtxt)
    {
        std::string _logname = path + logname;
        int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"
        if (fd < 0)
            return;
        write(fd, logtxt.c_str(), logtxt.size());
        close(fd);
    }
    void printClassFile(int level, const std::string &logtxt)
    {
        std::string filename = LogFile;
        filename += ".";
        filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"
        printOneFile(filename, logtxt);
    }

    ~Log()
    {
    }
    void operator()(int level, const char *format, ...)
    {
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&t);
        char leftbuffer[SIZE];
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
                 ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
                 ctime->tm_hour, ctime->tm_min, ctime->tm_sec);

        va_list s;
        va_start(s, format);
        char rightbuffer[SIZE];
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
        va_end(s);

        // 格式:默認(rèn)部分+自定義部分
        char logtxt[SIZE * 2];
        snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);

        // printf("%s", logtxt); // 暫時(shí)打印
        printLog(level, logtxt);
    }

private:
    int printMethod;
    std::string path;
};

Main.cc

#include "TcpServer.hpp"
#include <iostream>
#include <memory>

void Usage(std::string proc)
{
    std::cout<<"\n\rUsage: "<<proc <<" port[1024+]\n" <<std::endl;
}


int main(int argc,char *argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(UsageError);
    }

    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<TcpServer> tcp_svr(new TcpServer(port));
    tcp_svr->InitServer();
    tcp_svr->Start();
    
    return 0;
}

dict.txt

apple:蘋果...
banana:香蕉...
red:紅色...
yellow:黃色...
the: 這
be: 是
to: 朝向/給/對
and: 和
I: 我
in: 在...里
that: 那個(gè)
have: 有
will: 將
for: 為了
but: 但是
as: 像...一樣
what: 什么
so: 因此
he: 他
her: 她
his: 他的
they: 他們
we: 我們
their: 他們的
his: 它的
with: 和...一起
she: 她
he: 他(賓格)
it: 它

ThreadPool.hpp

#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>

struct ThreadInfo
{
    pthread_t tid;
    std::string name;
};

static const int defalutnum = 10;

template <class T>
class ThreadPool
{
public:
    void Lock()
    {
        pthread_mutex_lock(&mutex_);
    }
    void Unlock()
    {
        pthread_mutex_unlock(&mutex_);
    }
    void Wakeup()
    {
        pthread_cond_signal(&cond_);
    }
    void ThreadSleep()
    {
        pthread_cond_wait(&cond_, &mutex_);
    }
    bool IsQueueEmpty()
    {
        return tasks_.empty();
    }
    std::string GetThreadName(pthread_t tid)
    {
        for (const auto &ti : threads_)
        {
            if (ti.tid == tid)
                return ti.name;
        }
        return "None";
    }

public:
    static void *HandlerTask(void *args)
    {
        ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
        std::string name = tp->GetThreadName(pthread_self());
        while (true)
        {
            tp->Lock();

            while (tp->IsQueueEmpty())
            {
                tp->ThreadSleep();
            }
            T t = tp->Pop();
            tp->Unlock();

            t();
        }
    }
    void Start()
    {
        int num = threads_.size();
        for (int i = 0; i < num; i++)
        {
            threads_[i].name = "thread-" + std::to_string(i + 1);
            pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this);
        }
    }
    T Pop()
    {
        T t = tasks_.front();
        tasks_.pop();
        return t;
    }
    void Push(const T &t)
    {
        Lock();
        tasks_.push(t);
        Wakeup();
        Unlock();
    }
    static ThreadPool<T> *GetInstance()
    {
        if (nullptr == tp_) // ???
        {
            pthread_mutex_lock(&lock_);
            if (nullptr == tp_)
            {
                std::cout << "log: singleton create done first!" << std::endl;
                tp_ = new ThreadPool<T>();
            }
            pthread_mutex_unlock(&lock_);
        }

        return tp_;
    }

private:
    ThreadPool(int num = defalutnum) : threads_(num)
    {
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cond_, nullptr);
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }
    ThreadPool(const ThreadPool<T> &) = delete;
    const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // a=b=c
private:
    std::vector<ThreadInfo> threads_;
    std::queue<T> tasks_;

    pthread_mutex_t mutex_;
    pthread_cond_t cond_;

    static ThreadPool<T> *tp_;
    static pthread_mutex_t lock_;
};

template <class T>
ThreadPool<T> *ThreadPool<T>::tp_ = nullptr;

template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;

我們更推薦多線程版的服務(wù)器(創(chuàng)建進(jìn)程的成本比較高)

注意線程池版本的服務(wù)器不適合長服務(wù),因?yàn)榫€程的個(gè)數(shù)是確定的

服務(wù)器的結(jié)構(gòu):

while(1)
{
  cliaddr_len = sizeof(cliaddr);
  connfd = accept(listenfd,(struct sockaddr *)&cliaddr_len);
  n = read(connfd,buf,MAXLINE);
  ...
  close(connfd);
}

【附】:雙工在物理層面上是指通信線路上可以同時(shí)發(fā)送和接收數(shù)據(jù),然而在數(shù)據(jù)鏈路層的傳輸采用載波監(jiān)聽/碰撞檢測的技術(shù)。指的就是在有線傳輸上只能存在一種電信號,所以也就不會存在兩種電信號同時(shí)存在的場景了,即有線傳輸物理層面上沒有雙工一說,要么發(fā)送數(shù)據(jù),要么接收數(shù)據(jù)。 (ps:無線傳輸中的碼分復(fù)用可以同時(shí)存在)我們現(xiàn)在認(rèn)為的雙工是指邏輯連接層面上,而 UDP 是不需要進(jìn)行邏輯連接的,只是單向發(fā)送,但雙方隨時(shí)都可以發(fā)送,如果你認(rèn)為這是雙工那也合理。而TCP的全雙工也是邏輯層面上,通信兩端隨時(shí)都可以發(fā)送和接收數(shù)據(jù),不需要像 HTTP/1.1 那樣采用請求應(yīng)答模式。文章來源地址http://www.zghlxwxcb.cn/news/detail-851395.html

到了這里,關(guān)于【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(TCP服務(wù)器)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

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

相關(guān)文章

  • 【Linux】TCP網(wǎng)絡(luò)套接字編程+協(xié)議定制+序列化和反序列化

    【Linux】TCP網(wǎng)絡(luò)套接字編程+協(xié)議定制+序列化和反序列化

    悟已往之不諫,知來者之可追。抓不住的就放手,屬于你的都在路上…… 1. 為了讓我們的代碼更規(guī)范化,所以搞出了日志等級分類,常見的日志輸出等級有DEBUG NORMAL WARNING ERROR FATAL等,再配合上程序運(yùn)行的時(shí)間,輸出的內(nèi)容等,公司中就是使用日志分類的方式來記錄程序的輸

    2024年02月08日
    瀏覽(25)
  • [Linux] 網(wǎng)絡(luò)編程 - 初見TCP套接字編程: 實(shí)現(xiàn)簡單的單進(jìn)程、多進(jìn)程、多線程、線程池tcp服務(wù)器

    [Linux] 網(wǎng)絡(luò)編程 - 初見TCP套接字編程: 實(shí)現(xiàn)簡單的單進(jìn)程、多進(jìn)程、多線程、線程池tcp服務(wù)器

    網(wǎng)絡(luò)的上一篇文章, 我們介紹了網(wǎng)絡(luò)變成的一些重要的概念, 以及 UDP套接字的編程演示. 還實(shí)現(xiàn)了一個(gè)簡單更簡陋的UDP公共聊天室. [Linux] 網(wǎng)絡(luò)編程 - 初見UDP套接字編程: 網(wǎng)絡(luò)編程部分相關(guān)概念、TCP、UDP協(xié)議基本特點(diǎn)、網(wǎng)絡(luò)字節(jié)序、socket接口使用、簡單的UDP網(wǎng)絡(luò)及聊天室實(shí)現(xiàn)…

    2024年02月16日
    瀏覽(32)
  • 網(wǎng)絡(luò)編程【TCP流套接字編程】

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

    目錄 TCP流套接字編程 1.ServerSocket API 2.Socket API 3.TCP中的長短連接 4.回顯程序(短連接) 5.服務(wù)器和客戶端它們的交互過程 6.運(yùn)行結(jié)果及修改代碼 ? ??兩個(gè)核心: ServerSocket? ? ?Socket 1.ServerSocket API ? ServerSocket 是創(chuàng)建?TCP服務(wù)端Socket的API ServerSocket 構(gòu)造方法: ServerSocket 方法 :

    2023年04月12日
    瀏覽(573)
  • 網(wǎng)絡(luò)編程套接字( TCP )

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

    目錄 1、實(shí)現(xiàn)一個(gè)TCP網(wǎng)絡(luò)程序(單進(jìn)程版) ????????1.1、服務(wù)端serverTcp.cc文件 ?????????????????服務(wù)端創(chuàng)建套接字 ?????????????????服務(wù)端綁定 ?????????????????服務(wù)端監(jiān)聽 ?????????????????服務(wù)端獲取連接 ?????????????????服務(wù)

    2024年01月17日
    瀏覽(1815)
  • 【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)
  • 【JavaEE】網(wǎng)絡(luò)編程之TCP套接字、UDP套接字

    【JavaEE】網(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月20日
    瀏覽(118)
  • 【網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(三)TCP網(wǎng)絡(luò)程序

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

    與前邊的UDP網(wǎng)絡(luò)程序相同,創(chuàng)建套接字的接口都是socket,下邊對socket接口進(jìn)行介紹: 協(xié)議家族選擇AF_INET,因?yàn)槲覀円M(jìn)行網(wǎng)絡(luò)通信。 而第二個(gè)參數(shù),為服務(wù)類型,傳入SOCK_STREAM,我們編寫TCP程序,所以要選擇流式的服務(wù)。 第三個(gè)參數(shù)默認(rèn)傳入0,由前兩個(gè)參數(shù)就可以推出這是

    2024年02月16日
    瀏覽(91)
  • 網(wǎng)絡(luò)編程套接字應(yīng)用分享【Linux &C/C++ 】【UDP應(yīng)用 | TCP應(yīng)用 | TCP&線程池小項(xiàng)目】

    網(wǎng)絡(luò)編程套接字應(yīng)用分享【Linux &C/C++ 】【UDP應(yīng)用 | TCP應(yīng)用 | TCP&線程池小項(xiàng)目】

    目錄 前提知識 1. 理解源ip,目的ip和Macip 2. 端口號 3. 初識TCP,UDP協(xié)議 4.?網(wǎng)絡(luò)字節(jié)序 5. socket 編程 sockaddr類型? 一,基于udp協(xié)議編程? 1. socket——?jiǎng)?chuàng)建套接字 2. bind——將套接字強(qiáng)綁定? 3. recvfrom——接受數(shù)據(jù) 4. sendto——發(fā)出信息 ?遇到的問題 (1. 云服務(wù)器中以及無法分配I

    2024年04月08日
    瀏覽(26)
  • 網(wǎng)絡(luò)編程套接字之三【TCP】

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

    目錄 1. ServerSocket API(給服務(wù)器端使用的類) 2. Socket API(既給服務(wù)器使用,也給客戶端使用) 3. 寫TCP回顯—服務(wù)器 4. 使用線程池后的TCP服務(wù)器代碼(最終) 5. 寫回顯-客戶端 6. TCP回顯—客戶端代碼 7. 運(yùn)行回顯服務(wù)器和客戶端 TCP流套接字編程 ?ServerSocket 是創(chuàng)建TCP服務(wù)端Socket的

    2024年01月19日
    瀏覽(91)
  • 「網(wǎng)絡(luò)編程」第二講:socket套接字(四 - 完結(jié))_ Linux任務(wù)管理與守護(hù)進(jìn)程 | TCP協(xié)議通訊流程

    「網(wǎng)絡(luò)編程」第二講:socket套接字(四 - 完結(jié))_ Linux任務(wù)管理與守護(hù)進(jìn)程 | TCP協(xié)議通訊流程

    「前言」文章是關(guān)于網(wǎng)絡(luò)編程的socket套接字方面的,上一篇是網(wǎng)絡(luò)編程socket套接字(三),這篇續(xù)上篇文章的內(nèi)容,下面開始講解!? 「歸屬專欄」網(wǎng)絡(luò)編程 「主頁鏈接」個(gè)人主頁 「筆者」楓葉先生(fy) 「楓葉先生有點(diǎn)文青病」「句子分享」 Time?goes?on?and?on,?never?to?an?

    2024年02月10日
    瀏覽(46)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包