最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Linux基于TCP實現(xiàn)服務端客戶端通信的步驟詳解

 更新時間:2026年03月16日 08:45:51   作者:蝦..  
文章詳細介紹了Linux系統(tǒng)中的前臺進程和后臺進程的概念、創(chuàng)建、切換和操作方法,以及進程關(guān)系、守護進程、TCP服務器與客戶端通信等方面的內(nèi)容,需要的朋友可以參考下

1.前臺進程和后臺進程

當用戶通過Xshell等工具登錄云服務器時,Linux系統(tǒng)會為用戶創(chuàng)建一個會話(session)。每個會話會默認創(chuàng)建一個bash命令行解釋器進程,它初始是前臺進程,負責接收鍵盤輸入、執(zhí)行命令并返回結(jié)果。

這個bash進程現(xiàn)在也就是前臺進程,前臺進程只能有一個,后臺進程可以有很多個。

前臺進程與后臺進程的區(qū)別

先看代碼:創(chuàng)建代碼,每隔一秒打印hello world字符串。

#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
    while(true)
    {
        cout<<"hello world"<<endl;
        sleep(1);
    }
    return 0;
}

運行結(jié)果:

可以發(fā)現(xiàn)當我們運行進程后,輸入ls,pwd這些指令bash命令行進程都不會執(zhí)行了,因為這個時候著進程就是一個前臺進程了,bash進程已經(jīng)被切換到后臺進程了,這個時候是進程在獲取鍵盤輸入,而不是bash進程,所以bash進程也就是不能執(zhí)行對應的指令了。

前臺進程:能獲取鍵盤輸入(標準輸入),一個會話中只能有一個前臺進程。
后臺進程:無法獲取鍵盤輸入,但可以向顯示器輸出結(jié)果(標準輸出/標準錯誤),一個會話中可以同時存在多個后臺進程。

注意:前臺進程擁有鍵盤輸入權(quán)限,后臺進程沒有,但兩者都能向顯示器輸出內(nèi)容。

進程切換邏輯

當運行一個程序(如 ./process )時,該程序會成為前臺進程,原本的bash會被切換到后臺。此時bash無法接收鍵盤輸入,也就無法執(zhí)行新命令;若用 Ctrl+C 終止前臺進程,bash會自動回到前臺。

進入后臺運行程序

在命令末尾加上 & ,例如 ./process & ,可以讓程序以后臺進程運行,此時bash仍保持為前臺進程,可繼續(xù)執(zhí)行其他命令。

使用 & 可以讓程序后臺運行,不影響bash接收新命令。

當我們將test進程變?yōu)楹笈_進程后,因為打印是向顯示器上面打印,所以會一直打印"hello world"字符串,但是輸入指令還是會被執(zhí)行,說明這個時候bash是前臺進程,test為后臺進程。

前臺進程和后臺進程數(shù)量

因為前臺進程和后臺進程都可以向顯示器打印數(shù)據(jù),所以,我們將后臺進程打印的數(shù)據(jù)重定向到了一個文件中

查看后臺進程數(shù)量:

ps ajx|head -1&&ps ajx|grep test|grep -v grep

可以看到后臺進程被創(chuàng)建了多個,一個會話中前臺進程只能有1個,后臺進程可以有多個。

后臺進程的操作

當我們運行一個后臺進程的時候,可以看到會出現(xiàn)一個關(guān)于后臺進程的信息。

第一個數(shù)字表示的是后臺進程的任務號,第二個表示的是進程的pid。

查看后臺進程的狀態(tài)

jobs

使用jobs可以查看后臺進程的狀態(tài)

第一列就是任務號,后面就是運行狀態(tài)了,running表示正在運行。

切換進程狀態(tài)

fg+任務號

fg加任務號可以切換該任務號的進程為前臺進程。

輸入fg 3就將后臺進程切換到了前臺,這個時候bash為后臺進程就不能收到我們的指令了。

暫停進程和終止進程

暫停進程

可以使用fg+任務號來時后臺進程切換到前臺進程,輸入ctrl+z來暫停進程,ctrl+z是向進程發(fā)送了20號信號。

使用ctrl+z暫停進程后,在查看進程狀態(tài),就可以看到后臺進程被暫停了,bash也就自動切換回到前臺進程了。

終止進程

和暫停進程的方法一樣的,通過fg切換到前臺進程,使用ctrl+c來終止進程

恢復暫停的后臺進程

bg+任務號//恢復后臺進程

bg+任務號恢復暫停的后臺進程

2.Linux的進程關(guān)系

向創(chuàng)建一個test后臺進程,然后創(chuàng)建3個sleep后臺進程

sleep 1000|sleep 2000|sleep 3000

那么三個sleep之間是采用兩個管道連接起來的,本質(zhì)上還是創(chuàng)建了3個sleep進程,然后第一個sleep進程休眠結(jié)束后,結(jié)果是什么都沒有,然后將這個結(jié)果通過管道交給第二個sleep進程,然后第二個sleep進程開始休眠,同理結(jié)束后通過管道交給第三個sleep進程,所以這三個sleep進程合起來在完成同一個任務,process這個后臺進程自己在完成一個任務

所以我們使用如下指令查找一行運行的進程中,帶有sleep或者process的進程,grep -E ‘內(nèi)容|內(nèi)容’ 是進行正則匹配對應的內(nèi)容

ps ajx|head -1&&ps ajx|grep -E 'test|sleep'|grep -v grep

先解釋各字段的意思:

PPID    父進程 PID。這些進程都由 Shell(bash)創(chuàng)建,所以 PPID 就是 bash 的 PID。

PID       進程自身的唯一 ID。
PGID    進程組 ID。- 3 個  屬于同一進程組,PGID 等于第一個  的 PID(它是組長)。-  單獨成組,PGID 就是它自己的 PID。
SID       會話 ID,等于當前登錄 Shell(bash)的 PID。同一終端下的所有進程共享同一個 SID。
TTY       關(guān)聯(lián)的終端設(shè)備。
STAT      進程狀態(tài)(如  休眠、 運行等)。

進程組和任務的關(guān)系

一個進程組也就是完成的一個任務,當我們啟動了兩次test進程,也就是代表著我們啟動了兩個不同的任務,當我們通過管道創(chuàng)建的3個sleep進程也就是屬于一個任務組,所以3個sleep的PGID是系統(tǒng)的,也就意味著他們屬于同一個任務組。

同時屬于同一個進程組的進程,他們的PGID進程組ID會等于進程組中的第一個進程的PID,上圖中第一個sleep是第一個啟動的進程,所以他也就相當于這個進程組的隊長,進程組ID就保持和隊長的PID一樣。

session會話ID對于bash的PID

當我們打開xshell去連接云服務器時,登錄成功后會fork出一個bash進程,bash進程會使用

setsid()來創(chuàng)建一個會話,那么bash進程也就是會話的首進程。

內(nèi)核規(guī)定:

調(diào)用 setsid() 的進程 = 會話首進程

會話首進程的 PID = 整個會話的 SID

所以bash的PID會對于會話ID

3.守護進程

后臺進程會受到用戶的登錄和退出的影響的,當我們關(guān)閉xshell,在打開時,后臺進程就被清理了。

如果我們想要一個后臺進程不受用戶退出和登錄的影響,該怎么辦?

守護進程

當張三登錄 Linux,李四也登錄 Linux,系統(tǒng)會給每個人單獨開一個房間:張三的房間 = 張三的 Session,李四的房間 = 李四的 Sessio,這里的房間也就是一個會話,兩個會話互不打擾,張三 退出,就會關(guān)閉這個會話,將這個會話的所有進程終止。

但是李四的會話是不受影響的。我們把一個進程自成一個會話叫做守護進程,給一個進程單獨開一個會話,是它不會受其他會話的影響。

linux操作系統(tǒng)中的可能有很多個用戶在使用,所以操作系統(tǒng)就會為這些用戶每一個用戶創(chuàng)建一個對應的session會話,所以系統(tǒng)中就會同時存在多個session會話,那么linux操作系統(tǒng)要不要將這些session會話管理起來?要,那么如何進行管理呢?

先描述再組織,先使用struct結(jié)構(gòu)體將session會話描述起來,然后采用一定的數(shù)據(jù)結(jié)構(gòu),例如鏈表,將這些struct結(jié)構(gòu)體實例化的session會話的描述對象組織起來,從此以后對session會話的管理就轉(zhuǎn)化成了對鏈表的增刪查改

所以系統(tǒng)中一定有對應的系統(tǒng)調(diào)用可以創(chuàng)建session會話,所以這也就使得讓服務器成為守護進程有了可能

4.TCP服務器與客戶端通信(守護進程版)

setsid創(chuàng)建一個會話。

Dame.hpp

#include <iostream>
#include <string>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
const string nullfile="/dev/null";
void Daemon(const string&cwd="")
{
    //忽略其它信號
    signal(SIGCHLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);
    //創(chuàng)建子進程,然后終止父進程,子進程會拷貝父進程資源,例如文件描述符,頁表,地址空間,代碼等,
    //然后由子進程繼續(xù)向后執(zhí)行代碼
    if(fork() > 0)
        exit(0);
    //創(chuàng)建session會話,子進程成為服務器,然后守護進程化
    setsid();
    //更改當前子進程代表的服務器進程的工作目錄
    if(!cwd.empty())
        chdir(cwd.c_str());
    //打開 /dev/null 文件
    int fd = open(nullfile.c_str(), O_WRONLY);
    //重定向 標準輸入,標準輸出,標準錯誤
    if(fd > 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
}

但是不能直接調(diào)用 setsid,因為進程組組長 不能調(diào)用 setsid 創(chuàng)建新會話,只有組員才能調(diào)用。

但是服務器一運行:自己就是一個進程組,自己就是組長,直接調(diào)用 setsid 會失敗。所以使用fork 創(chuàng)建子進程,父進程直接退出,由子進程來創(chuàng)建會話,子進程徹底脫離原來的終端,自成會話。

父進程死了,子進程還活著,子進程變成 孤兒進程被系統(tǒng) 1 號進程領(lǐng)養(yǎng)

所以守護進程本質(zhì)就是孤兒進程 + 自成會話。 

因為守護進程已經(jīng):脫離終端,不跟用戶交互,所以對于標準輸入 0是沒人輸入,標準輸出 1 是 沒人看,標準錯誤 2是沒人看,這些文件描述符留著占位置、可能出問題。
 
所以可以打開  /dev/null (黑洞文件,寫進去就消失),然后用  dup2 ,把 0、1、2 全部重定向到  /dev/null 

守護進程工作目錄最好切換到根目錄 /,避免:占用某個目錄導致無法卸載,日志、文件路徑混亂
所以可以使用chdir改變目錄

以前日志需要打印到顯示器,但是現(xiàn)在標準輸入,輸出,錯誤都被關(guān)閉了 ,但是我們又希望看到日志打印的信息

所以可以在將日志系統(tǒng)根據(jù)不同的錯誤等級往對應的里寫文件,以后看日志直接去文件中看

同時我們希望守護進程不想被隨便干擾:
用  signal  忽略  SIGPIPE (管道破裂)忽略  SIGCHLD (子進程退出)忽略  SIGTSTP (Ctrl+Z)忽略  SIGHUP (掛斷)

main.cpp

改變?nèi)罩敬蛴》绞?,根?jù)日志等級王不同的文件中打印

#include <iostream>
#include <memory>
#include "server.hpp"
void Usage(const std::string str)
{
    std::cout << "\n\tUsage: " << str << " port[1024+]\n" << std::endl; 
}
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(UsageError);
    }
    lg.Enable(Classfile);
    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<TcpServer> server(new TcpServer(port));
    server->init();
    server->StartServer();
    return 0;
}

server.hpp

啟動進程守護

 void StartServer()
    {
        Daemon();
        ThreadPool<Task>::GetInstance()->Start();
        lg(Info,"TcpServer is running");
        for(;;)
        {
            struct sockaddr_in client;
            socklen_t len=sizeof(client);
            //阻塞等待
            int sockfd=accept(listensock_,(struct sockaddr*)(&client),&len);
            if(sockfd<0)
            {
                lg(Warning,"accept error,errno:%d,errstring:%s",errno,strerror(errno));
                continue;
            }
            uint16_t clientport=ntohs(client.sin_port);
            string clientip=inet_ntoa(client.sin_addr);
            lg(Info,"get a new link...,clientip:%s,clientport:%d",clientip.c_str(),clientport);
            //多進程
            // pid_t id=fork();
            // if(id==0)
            // {
            //     //子進程
            //     close(listensock_);
            //     //子進程創(chuàng)建孫進程后直接退出
            //     if(fork()>0) exit(0);
            //     //孫進程
            //     Service(sockfd,clientip,clientport);
            //     close(sockfd);
            // }
            // close(sockfd);
            // pid_t rid=waitpid(id,nullptr,0);
            //多線程
            // ThreadData*td=new ThreadData(sockfd,clientip,clientport,this);
            // pthread_t id;
            // pthread_create(&id,nullptr,Routine,td);
            //線程池
            Task t(sockfd,clientip,clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }

刪除守護進程,查看進程pid,kill指令刪除即可。

測試:

 bash還是可以接收到指令。

客戶端也可以正常運行。

源代碼

client.cpp

#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;
void Usage(const string& str)
{
    cout << "\n\tUsage: " << str << " serverip serverport" << endl; 
}
// 客戶端啟動格式:./tcpclient serverip serverport
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        return 0;
    }
    string serverip = argv[1];
    uint16_t serverport = stoi(argv[2]);
    struct sockaddr_in server;
    server.sin_family=AF_INET;
    server.sin_port=htons(serverport);
    server.sin_addr.s_addr=inet_addr(serverip.c_str());
    socklen_t len=sizeof(server);
    while(true)
    {
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(sockfd < 0)
        {
            cerr << "socket create err" << endl;
            return 1;
        }
        bool isconnect=false;
        int cnt=5;
        do
        {
            int n=connect(sockfd,(struct sockaddr*)(&server),len);
            if(n<0)
            {
                //連接失敗
                isconnect=true;
                cnt--;
                sleep(2);
                cerr<<"connect error......,reconnect: "<<cnt<<endl;
            }
            //連接成功,退出循環(huán)
            else
            {
                break;
            }
        }while(cnt&&isconnect);
        if(cnt==0)
        {
            cerr<<"user offline..."<<endl;
            return 2;
        }
        //客戶端發(fā)起connect請求時,操作系統(tǒng)會自動進行端口號的隨機bind綁定
        string message;
        char inbuffer[4096];
        cout << "Please Enter# ";
        getline(cin, message);
        int n = write(sockfd, message.c_str(), message.size());
        if(n < 0)
        {
            cerr << "write err" << endl;
            break;
        }
        n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);
        if(n > 0)
        {
            inbuffer[n] = 0;
            cout << inbuffer << endl;
        }
        //當讀取返回值為0時,不要直接break退出
        // else
        // {
        //     break;
        // }
        close(sockfd);
    }
    return 0;
}

Daemon.hpp

#include <iostream>
#include <string>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
const string nullfile="/dev/null";
void Daemon(const string&cwd="")
{
    //忽略其它信號
    signal(SIGCHLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);
    //創(chuàng)建子進程,然后終止父進程,子進程會拷貝父進程資源,例如文件描述符,頁表,地址空間,代碼等,
    //然后由子進程繼續(xù)向后執(zhí)行代碼
    if(fork() > 0)
        exit(0);
    //創(chuàng)建session會話,子進程成為服務器,然后守護進程化
    setsid();
    //更改當前子進程代表的服務器進程的工作目錄
    if(!cwd.empty())
        chdir(cwd.c_str());
    //打開 /dev/null 文件
    int fd = open(nullfile.c_str(), O_WRONLY);
    //重定向 標準輸入,標準輸出,標準錯誤
    if(fd > 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
}

dict.txt

apple:蘋果
banana:香蕉
book:書
cat:貓
dog:狗
egg:雞蛋
fish:魚
girl:女孩
house:房子
ice:冰
juice:果汁
king:國王
lion:獅子
milk:牛奶
nest:鳥巢
orange:橙子
pen:鋼筆
queen:女王
rice:米飯
sun:太陽
tree:樹
umbrella:雨傘
van:貨車
water:水
x-ray:X光
yellow:黃色
zoo:動物園
baby:嬰兒
cake:蛋糕
door:門
ear:耳朵
face:臉
grass:草
hand:手
ice cream:冰淇淋
jacket:夾克
kite:風箏
lamp:燈
moon:月亮
nose:鼻子
one:一
pig:豬
queen bee:蜂王
red:紅色
school:學校
toy:玩具
uncle:叔叔
vest:背心
window:窗戶
box:盒子
car:汽車
dad:爸爸
eye:眼睛
fan:風扇
game:游戲
hat:帽子
iceberg:冰山
jeep:吉普車
key:鑰匙
lion cub:幼獅
map:地圖
night:夜晚
octopus:章魚
pear:梨
queen size:大號
rain:雨
star:星星
table:桌子
umbrella stand:傘架
violin:小提琴
wolf:狼
apple pie:蘋果派
ball:球
chair:椅子
doll:玩偶
elephant:大象
flower:花
goat:山羊
hen:母雞
insect:昆蟲
jar:罐子
kangaroo:袋鼠
leaf:葉子
monkey:猴子
nest egg:儲備金
orange juice:橙汁
pencil:鉛筆
queen ant:蟻后
rabbit:兔子
ship:船
tiger:老虎
umbrella hat:傘帽
van driver:貨車司機
watch:手表
xylophone:木琴
yacht:游艇
zebra:斑馬
air:空氣
big:大的
cup:杯子

Init.hpp

#pragma once
#include<iostream>
#include<string>
#include<fstream>
#include<unordered_map>
#include"log.hpp"
using namespace std;
const string dictname="./dict.txt";
const string sep=":";
bool Solit(string &s,string *part1,string*part2)
{
    auto pos=s.find(sep);
    if(pos==string::npos) return false;//npos就是無符號整型的最大值,string的findsize_t,算法頭文件下的npos返回的則是迭代器。
    *part1=s.substr(0,pos);//substr左閉右開
    *part2=s.substr(pos+1);
    return true;
}
class Init
{
public:
    Init()
    {
        //ofstream從文件寫入
        ifstream in(dictname);//從指定文件中讀取
        if(!in.is_open())
        {
            lg(Fatal,"ifstream open %s error",dictname.c_str());
            exit(1);
        }
        string line;
        while(getline(in,line))//不斷的從in中讀取到line中
        {
            string part1,part2;
            Solit(line,&part1,&part2);
            dict.insert({part1,part2});
        }
        in.close();
    }
    string translation(const string&key)
    {
        auto iter=dict.find(key);
        if(iter==dict.end()) return "Unknow";
        else return iter->second;
    }
private:
    unordered_map<string,string> dict;
};

main.cpp

#include <iostream>
#include <memory>
#include "server.hpp"
void Usage(const std::string str)
{
    std::cout << "\n\tUsage: " << str << " port[1024+]\n" << std::endl; 
}
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(UsageError);
    }
    lg.Enable(Classfile);
    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<TcpServer> server(new TcpServer(port));
    server->init();
    server->StartServer();
    return 0;
}

makefile

.PHONY:all
all:tcpserver client
tcpserver:main.cpp
	g++ -o $@ $^ -std=c++11 -lpthread -g
client:client.cpp
	g++ -o $@ $^ -std=c++11 -g
.PHONY:clean
clean:
	rm -f tcpserver client

server.hpp

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "log.hpp"
#include "Task.hpp"
#include "ThreadPool.hpp"
#include "Daemon.hpp"
extern Log lg;
using namespace std;
const int backlog=10;
const string ip="0.0.0.0";
const uint16_t port=8080;
enum
{
    UsageError=1,
    SocketError,
    BindError,
    ListenError
};
class TcpServer;
struct ThreadData
{
public:
    ThreadData(int fd,const string&ip,const uint16_t&p,TcpServer*t)
    :sockfd(fd)
    ,clientip(ip)
    ,clientport(p)
    ,tsvr(t)
    {}
public:
    int sockfd;
    string clientip;
    uint16_t clientport;
    TcpServer*tsvr;
};
class TcpServer
{
public:
    TcpServer(uint16_t defaultport=port)
    :ip_(ip)
    ,port_(port)
    {}
    void init()
    {
        listensock_=socket(AF_INET,SOCK_STREAM,0);
        if(listensock_<0)
        {
            lg(Fatal,"create socket error,errno:%d,errstring:%s",errno,strerror(errno));
            exit(SocketError);
        }
        lg(Info,"socked create success,listensock_:%d",listensock_);
        //防止偶發(fā)性服務器無法重啟
        int opt=1;
        setsockopt(listensock_,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&opt,sizeof(opt));
        struct sockaddr_in server;
        memset(&server,0,sizeof(server));
        server.sin_family=AF_INET;
        server.sin_port=htons(port_);
        server.sin_addr.s_addr=inet_addr(ip_.c_str());
        socklen_t len=sizeof(server);
        if(bind(listensock_,(struct sockaddr*)(&server),len)<0)
        {
            lg(Fatal,"bind socket error,errno:%d,errstring:%s",errno,strerror(errno));
            exit(BindError);
        }
        lg(Info,"socked bind success");
        if(listen(listensock_,backlog)<0)
        {
            lg(Fatal,"listen error,errno:%d,errstring:%s",errno,strerror(errno));
            exit(ListenError);
        }
        lg(Info,"listen success");
    }
    void Service(int sockfd,string clientip,uint16_t clientport)
    {
        while(true)
        {
            char buffer[4096];
            ssize_t n=read(sockfd,buffer,sizeof(buffer)-1);
            if(n>0)
            {
                buffer[n]=0;
                cout<<"client say#"<<buffer<<endl;
                string server_echo="server say#";
                server_echo+=buffer;
                write(sockfd,server_echo.c_str(),server_echo.size());
            }
            else if(n==0)
            {
                //客戶端斷開連接
                lg(Info,"%s:%d quit ,server close sockfd:%d",clientip,clientport,sockfd);
                break;
            }
            else
            {
                //異常情況
                lg(Info,"read error,%s:%d quit,server close sockfd:%d",clientip,clientport,sockfd);
                break;
            }
        }
    }
    static void*Routine(void*args)
    {
        pthread_detach(pthread_self());
        ThreadData*td=static_cast<ThreadData*>(args);
        td->tsvr->Service(td->sockfd,td->clientip,td->clientport);
        close(td->sockfd);
        delete td;
        return nullptr;
    }
    void StartServer()
    {
        Daemon();
        ThreadPool<Task>::GetInstance()->Start();
        lg(Info,"TcpServer is running");
        for(;;)
        {
            struct sockaddr_in client;
            socklen_t len=sizeof(client);
            //阻塞等待
            int sockfd=accept(listensock_,(struct sockaddr*)(&client),&len);
            if(sockfd<0)
            {
                lg(Warning,"accept error,errno:%d,errstring:%s",errno,strerror(errno));
                continue;
            }
            uint16_t clientport=ntohs(client.sin_port);
            string clientip=inet_ntoa(client.sin_addr);
            lg(Info,"get a new link...,clientip:%s,clientport:%d",clientip.c_str(),clientport);
            //多進程
            // pid_t id=fork();
            // if(id==0)
            // {
            //     //子進程
            //     close(listensock_);
            //     //子進程創(chuàng)建孫進程后直接退出
            //     if(fork()>0) exit(0);
            //     //孫進程
            //     Service(sockfd,clientip,clientport);
            //     close(sockfd);
            // }
            // close(sockfd);
            // pid_t rid=waitpid(id,nullptr,0);
            //多線程
            // ThreadData*td=new ThreadData(sockfd,clientip,clientport,this);
            // pthread_t id;
            // pthread_create(&id,nullptr,Routine,td);
            //線程池
            Task t(sockfd,clientip,clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }
private:
    int listensock_;
    string ip_;
    uint16_t port_;
};

Task.hpp

#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include "log.hpp"
#include "Init.hpp"
using namespace std;
extern Log lg;
class Task
{
public:
    Task(int sockfd, const string &clientip, const uint16_t clientport)
        : sockfd_(sockfd), clientip_(clientip), clientport_(clientport)
    {
    }
    // void run()//普通轉(zhuǎn)發(fā)
    // {
    //     char buffer[4096];
    //     int n=read(sockfd_,buffer,sizeof(buffer)-1);
    //     if(n>0)
    //     {
    //         buffer[n]=0;
    //         cout<<"client say#"<<buffer<<endl;
    //         string echo_string="server say#";
    //         echo_string+=buffer;
    //         std::cout << echo_string << std::endl;
    //         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_);
    //     }
    //     else
    //     {
    //         lg(Warning, "read error,sockfd:%d,clientip:%s,clientport:%d", sockfd_, clientip_.c_str(), clientport_);
    //     }
    //     close(sockfd_);
    // }
    void run()
    {
        char buffer[4096];
        int n=read(sockfd_,buffer,sizeof(buffer)-1);
        if(n>0)
        {
            buffer[n]=0;
            cout<<"client say#"<<buffer<<endl;
            string echo_string="server say#";
            echo_string+=it.translation(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_);
        }
        else
        {
            lg(Warning, "read error,sockfd:%d,clientip:%s,clientport:%d", sockfd_, clientip_.c_str(), clientport_);
        }
        close(sockfd_);
    }
    void operator()()
    {
        run();
    }
    ~Task()
    {
    }
private:
    int sockfd_;
    string clientip_;
    uint16_t clientport_;
    Init it;
};

ThreadPool.hpp

#include <iostream>
#include <vector>
#include <pthread.h>
#include <string>
#include <unistd.h>
#include <queue>
using namespace std;
struct ThreadInfo
{
    pthread_t tid;
    string name;
};
static const int defaultnum = 5;
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();
    }
    string GetThreadName(pthread_t tid)
    {
        for (auto &t : threads_)
        {
            if (t.tid == tid)
            {
                return t.name;
            }
        }
        return "None";
    }
public:
    static void *HandlerTask(void *args)
    {
        ThreadPool *tp = static_cast<ThreadPool<T>*>(args);
        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 len = threads_.size();
        for (int i = 0; i < len; i++)
        {
            threads_[i].name = "thread -" + to_string(i);
            pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this); // this指針指向的是調(diào)用成員函數(shù)的對象(ThreadPool)
        }
    }
    T Pop()
    {
        T t = tasks_.front();
        tasks_.pop();
        return t;
    }
    void Push(const T &in)
    {
        Lock();
        tasks_.push(in);
        WakeUp();
        UnLock();
    }
    static ThreadPool<T> *GetInstance() // 懶漢模式
    {
        if (tp_ == nullptr)//減少申請鎖和釋放鎖的次數(shù)
        {
            pthread_mutex_lock(&lock_); // 避免出現(xiàn)多個線程搶奪同一份資源
            while (tp_ == nullptr)
            {
                tp_ = new ThreadPool<T>();
            }
            pthread_mutex_unlock(&lock_);
        }
        return tp_;
    }
private:
    ThreadPool(int num = defaultnum)
        : threads_(num)
    {
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cond_, nullptr);
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }
private:
    vector<ThreadInfo> threads_;
    queue<T> tasks_;
    pthread_mutex_t mutex_;
    pthread_cond_t cond_;
    static pthread_mutex_t lock_;
    static ThreadPool<T> *tp_; // 懶漢模式
};
template <class T>
ThreadPool<T> *ThreadPool<T>::tp_ = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;

以上就是Linux基于TCP實現(xiàn)服務端客戶端通信的詳細內(nèi)容,更多關(guān)于Linux TCP服務端客戶端通信的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Linux監(jiān)控重要進程的實現(xiàn)方法

    詳解Linux監(jiān)控重要進程的實現(xiàn)方法

    這篇文章主要介紹了詳解Linux監(jiān)控重要進程的實現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • Linux時間子系統(tǒng)之時間的表示示例詳解

    Linux時間子系統(tǒng)之時間的表示示例詳解

    這篇文章主要給大家介紹了關(guān)于Linux時間子系統(tǒng)之時間的表示的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用linux系統(tǒng)具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2020-05-05
  • ubuntu14.04安裝opencv3.0.0的操作方法

    ubuntu14.04安裝opencv3.0.0的操作方法

    下面小編就為大家分享一篇ubuntu14.04安裝opencv3.0.0的操作方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • 詳解在Linux系統(tǒng)中如何識別和解決端口占用問題

    詳解在Linux系統(tǒng)中如何識別和解決端口占用問題

    在日常的 Linux 系統(tǒng)管理和開發(fā)過程中,端口占用是一個常見且令人頭疼的問題,無論是部署新服務、調(diào)試應用程序,還是進行系統(tǒng)維護,遇到端口被占用都可能導致服務無法正常啟動或運行,本文將詳細介紹在 Linux 系統(tǒng)中如何識別和解決端口占用問題,需要的朋友可以參考下
    2025-01-01
  • Apache?Ignite?中的?SQL?模式(Schema)管理機制(使用建議)

    Apache?Ignite?中的?SQL?模式(Schema)管理機制(使用建議)

    Apache?Ignite支持多Schema管理,包含默認的PUBLIC和SYS系統(tǒng)Schema,允許自定義Schema通過配置或Cache名稱定義,DDL創(chuàng)建表時會自動生成對應Cache,建議合理使用Schema組織數(shù)據(jù)及權(quán)限控制,本文給大家介紹Apache?Ignite中的SQL模式(Schema)管理機制,感興趣的朋友一起看看吧
    2025-07-07
  • Linux單目錄掛載多塊磁盤的操作步驟

    Linux單目錄掛載多塊磁盤的操作步驟

    這篇文章主要介紹了Linux單目錄掛載多塊磁盤的操作步驟,Linux將多塊磁盤掛載到一個目錄,特此記錄,方便以后使用,需要的朋友可以參考下
    2024-02-02
  • Ubuntu14.04服務器環(huán)境下配置PHP7.0+Apache2+Mysql5.7的方法

    Ubuntu14.04服務器環(huán)境下配置PHP7.0+Apache2+Mysql5.7的方法

    這篇文章主要介紹了Ubuntu14.04服務器環(huán)境下配置PHP7.0+Apache2+Mysql5.7的方法,較為詳細的分析了Ubuntu14.04操作系統(tǒng)環(huán)境下配置PHP7.0+Apache2+Mysql5.7的具體步驟與相關(guān)命令使用技巧,需要的朋友可以參考下
    2018-04-04
  • linux jexus服務設(shè)置開機啟動

    linux jexus服務設(shè)置開機啟動

    這篇文章主要為大家詳細介紹了linux jexus服務設(shè)置開機啟動,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Linux系統(tǒng)查看CPU、機器型號、內(nèi)存等信息

    Linux系統(tǒng)查看CPU、機器型號、內(nèi)存等信息

    今天小編就為大家分享一篇關(guān)于Linux系統(tǒng)查看CPU、機器型號、內(nèi)存等信息,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • ubuntu環(huán)境下的php相關(guān)路徑與修改方法

    ubuntu環(huán)境下的php相關(guān)路徑與修改方法

    這篇文章主要介紹了ubuntu環(huán)境下的php相關(guān)的路徑,需要的朋友可以參考下
    2020-12-12

最新評論

湟源县| 彭水| 视频| 乌拉特中旗| 雷波县| 河东区| 景谷| 山西省| 高阳县| 英超| 宜君县| 通渭县| 紫云| 平和县| 岳西县| 抚顺市| 九寨沟县| 呼和浩特市| 长春市| 卢氏县| 和平区| 开化县| 九江市| 长沙市| 鹤山市| 合作市| 黄石市| 普安县| 泰安市| 瑞金市| 永修县| 祁连县| 溧阳市| 汝州市| 丹巴县| 旺苍县| 胶州市| 定陶县| 九龙城区| 翼城县| 同江市|