日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

2-4:套接字(Socket)编程之TCP通信

發(fā)布時(shí)間:2025/3/15 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 2-4:套接字(Socket)编程之TCP通信 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 一 TCP通信服務(wù)端和客戶端——和UDP區(qū)別
    • (1)服務(wù)端
    • (2)客戶端
  • 二:TCP通信-多進(jìn)程/線程
    • (1)多進(jìn)程版本
    • (2)多線程版本
    • (3)線程池版本

一 TCP通信服務(wù)端和客戶端——和UDP區(qū)別

TCP是面向字節(jié)流的,是有連接的,會(huì)在服務(wù)端和客戶端之間建立一條連接,而UDP顯得就比較簡單,只負(fù)責(zé)傳遞。在2-3:套接字(Socket)編程之UDP通信這一節(jié)詳細(xì)敘述了UDP通信及套接字相關(guān)內(nèi)容,本節(jié)TCP通信將會(huì)在上節(jié)的基礎(chǔ)上,對(duì)TCP和UDP中代碼的不同部分做以補(bǔ)充。

(1)服務(wù)端

tcpServer.h

#include <iostream> #include <string> #include <unistd.h> #include <cstdio> #include <cstring> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> using namespace std;#define BACKLOG 5class tcpServer { private:int _port;int listen_sock; public:tcpServer(int port=8080):_port(port){}void initServer(){listen_sock=socket(AF_INET,SOCK_STREAM,0);//區(qū)別UDP,TCP采用流式套接字if(listen_sock < 0){cout<<"套接字創(chuàng)建失敗"<<endl;exit(2);}struct sockaddr_in local;local .sin_family=AF_INET;local.sin_port=htons(_port);local.sin_addr.s_addr=htonl(INADDR_ANY);if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0){cout<<"綁定失敗"<<endl;exit(3);}//開啟監(jiān)聽if(listen(listen_sock,BACKLOG) < 0){cout<<"綁定失敗"<<endl;exit(4);} }//通信服務(wù)void Service(int sock){char buffer[1024];while(1){size_t s=recv(sock,buffer,sizeof(buffer)-1,0);//區(qū)別UDPif(s > 0){buffer[s]='\0';cout<<"服務(wù)端收到客戶端的消息:"<<buffer<<endl;send(sock,buffer,strlen(buffer),0);//區(qū)別UDP}if(s==0)//客戶端下線,服務(wù)端收到0{cout<<"客戶端已經(jīng)下線"<<endl;close(sock);//注意關(guān)閉套接字,資源是有限的break;}}}void startServer(){sockaddr_in endpoint;while(1){//TCP-acceptsocklen_t len=sizeof(endpoint);int _sock=accept(listen_sock,(struct sockaddr*)&endpoint,&len);if(_sock < 0){cout<<"accept失敗"<<endl;continue;}cout<<"一臺(tái)新的客戶端已經(jīng)連接"<<endl;//拿到套接字進(jìn)行通信Service(_sock);}}~tcpServer(){close(listen_sock);} };

1:創(chuàng)建套接字

相較于UDP,TCP通信時(shí)選擇的套接字是流式套接字

listen_sock=socket(AF_INET,SOCK_STREAM,0);

2:listen監(jiān)聽
在套接字綁定之前,UDP和TCP基本是一致的。在TCP通信中,有兩套套接字,其中一套用于監(jiān)聽,稱之為監(jiān)聽套接字。TCP不同于UDP,如果和客戶端之間沒有連接就不能發(fā)送數(shù)據(jù),所以要把一個(gè)套接字設(shè)置為監(jiān)聽狀態(tài),以便在任意時(shí)候客戶端請(qǐng)求服務(wù)器時(shí)都能有套接字與該客戶端建立連接。關(guān)于BACKLOG選項(xiàng)后序再網(wǎng)絡(luò)原理里面再做解釋

函數(shù)原型如下

#include <sys/socket.h>int listen(int s, int backlog);

3:accept

accept表示服務(wù)端接受一個(gè)連接,每當(dāng)一個(gè)客戶端連接成功時(shí),就會(huì)建立一條連接。最為關(guān)鍵的是該接口的返回值,其返回值也是一個(gè)套接字,前面說過TCP通信中存在兩套套接字,一個(gè)就是剛才說到的過的監(jiān)聽套接字,它的職責(zé)就是不斷從網(wǎng)絡(luò)中獲取連接,因?yàn)榭赡軙?huì)用很多客戶端想要和服務(wù)端通信,當(dāng)它把連接拿上來之后,調(diào)用接口accept,其返回值所產(chǎn)生的套接字就是專門用來處理這條連接,用于進(jìn)行通信的。這樣做的話整個(gè)服務(wù)端只需要一個(gè)監(jiān)聽套接字,同時(shí)連接只要成功,只需調(diào)用接口產(chǎn)生新的套接字再用于通信即可。

int _sock=accept(listen_sock,(struct sockaddr*)&endpoint,&len);

當(dāng)accept失敗時(shí),該接口返回值也會(huì)小于0,需要注意的是此時(shí)只是一個(gè)連接失敗了,你不能因?yàn)檫@么一個(gè)連接失敗了,而把整個(gè)服務(wù)器給退了,所以要繼續(xù)continue。
當(dāng)accept成功時(shí),我們建立一個(gè)新的函數(shù),該函數(shù)就是用來專門去處理通信問題的

如下在accept成功之后,加入這樣一句代碼,表示連接上了一臺(tái)新的客戶端

然后使用telnet命令,如果你的Centos沒有這個(gè)命令,需要進(jìn)行一定配置,鏈接如下,親測(cè)有效

CentOS 7.4安裝telnet服務(wù)端

然后使用本地環(huán)回測(cè)試,使用telnet進(jìn)行連接,可以發(fā)現(xiàn)當(dāng)一臺(tái)主機(jī)連接成功時(shí),服務(wù)端提示出了相應(yīng)的訊息

4:recv和send
不同于UDP中的recvfrom和sendto,在TCP通信中,我們盡可能采用的是recv和send來接受和發(fā)送

#include <sys/types.h> #include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t len, int flags); int send(int s, const void *msg, size_t len, int flags);

(2)客戶端

tcpClient.h

#include <iostream> #include <string> #include <cstring> #include <unistd.h> #include <cstdio> #include <cstdlib> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <netinet/in.h> using namespace std;class tcpClient { private:string _ip;//服務(wù)端IP和端口號(hào)int _port;int _sock; public:tcpClient(string ip="127.0.0.1",int port=8080):_ip(ip),_port(port){}void initClient(){_sock=socket(AF_INET,SOCK_STREAM,0);//區(qū)別UDPif(_sock < 0){cout<<"套接字創(chuàng)建失敗"<<endl;exit(2);}struct sockaddr_in sev;sev.sin_family=AF_INET;sev.sin_port=htons(_port);sev.sin_addr.s_addr=inet_addr(_ip.c_str());//TCP-connectif(connect(_sock,(struct sockaddr*)&sev,sizeof(sev))!=0){cout<<"connect失敗"<<endl;exit(3);}}void startClient(){char mssage[64];while(1){ size_t s=read(0,mssage,sizeof(mssage)-1);//從標(biāo)準(zhǔn)輸入讀入if(s > 0){mssage[s-1]='\0';//剔除換行符send(_sock,mssage,strlen(mssage),0);ssize_t ret=recv(_sock,mssage,sizeof(mssage)-1,0);if(ret > 0){cout<<"客戶端得到服務(wù)端消息"<<mssage<<endl;}}}}~tcpClient(){close(_sock);}};

1:connect

TCP是面向連接,因此對(duì)于客戶端來說,它就要調(diào)用connect接口連接服務(wù)端,如果返回值為0表示連接成功

connect(_sock,(struct sockaddr*)&sev,sizeof(sev))

2:客戶端退出

當(dāng)客戶端退出時(shí),我們要讓服務(wù)器知道客戶端退出,并且關(guān)閉已經(jīng)打開的套接字
那么服務(wù)端如何知道客戶端退出了呢,這其實(shí)和recv接口的返回值有關(guān)

These calls return the number of bytes received, or -1 if an error occurred. In the event of an error, errno is set to indicate theerror. The return value will be 0 when the peer has performed an orderly shutdown.

它的意思就說如果客戶端下線,那么服務(wù)端將會(huì)接受到0 因此,服務(wù)端會(huì)有下面這樣代碼

if(s==0) {cout<<"客戶端已經(jīng)下線"<<endl;close(sock);break; }

二:TCP通信-多進(jìn)程/線程

使用上面的代碼,利用telnet進(jìn)行測(cè)試,xshell中有多個(gè)窗口,代表多個(gè)客戶端,第一個(gè)客戶端連接后,服務(wù)端的確打印出了相關(guān)訊息,但是第二個(gè)和第三個(gè)在連接時(shí)卻沒有打印出信息

問題的原因就是咋們編寫的服務(wù)器目前是一個(gè)單進(jìn)程版的服務(wù)器,當(dāng)?shù)谝粋€(gè)主機(jī)連接時(shí),由于沒有發(fā)送數(shù)據(jù),因此進(jìn)程會(huì)被阻塞在service函數(shù)中,到時(shí)后面的客戶端連接不上

(1)多進(jìn)程版本

tcpServer.h

#include <iostream> #include <signal.h> #include <string> #include <unistd.h> #include <cstdio> #include <cstring> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> using namespace std;#define BACKLOG 5class tcpServer { private:int _port;int listen_sock; public:tcpServer(int port=8080):_port(port){}void initServer(){signal(SIGCHLD,SIG_IGN);//讓子進(jìn)程自己銷毀listen_sock=socket(AF_INET,SOCK_STREAM,0);//區(qū)別UDP,TCP采用流式套接字if(listen_sock < 0){cout<<"套接字創(chuàng)建失敗"<<endl;exit(2);}struct sockaddr_in local;local .sin_family=AF_INET;local.sin_port=htons(_port);local.sin_addr.s_addr=htonl(INADDR_ANY);if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0){cout<<"綁定失敗"<<endl;exit(3);}//開啟監(jiān)聽if(listen(listen_sock,BACKLOG) < 0){cout<<"綁定失敗"<<endl;exit(4);}}void Service(int sock){char buffer[1024];while(1){size_t s=recv(sock,buffer,sizeof(buffer)-1,0);if(s > 0){buffer[s]='\0';cout<<"服務(wù)端收到客戶端的消息:"<<buffer<<endl;send(sock,buffer,strlen(buffer),0);}if(s==0){cout<<"客戶端已經(jīng)下線"<<endl;close(sock);break;}}}void startServer(){sockaddr_in endpoint;while(1){//acceptsocklen_t len=sizeof(endpoint);int _sock=accept(listen_sock,(struct sockaddr*)&endpoint,&len);if(_sock < 0){cout<<"accept失敗"<<endl;continue;}string client_info=inet_ntoa(endpoint.sin_addr);client_info+=":";client_info+=to_string(ntohs(endpoint.sin_port));cout<<"一臺(tái)新的客戶端已經(jīng)連接:"<<client_info<<endl;pid_t id=fork();if(id==0)//子進(jìn)程{close(listen_sock); //子進(jìn)程會(huì)以父進(jìn)程為模板,復(fù)制父進(jìn)程PCB,所以對(duì)于子進(jìn)程來說,可以關(guān)閉的它的listen_sock,盡量節(jié)省資源Service(_sock);exit(0);//子進(jìn)程處理完畢}//對(duì)于父進(jìn)程它只關(guān)心監(jiān)聽套接字,所以可以把父進(jìn)程的sock關(guān)閉,而且是必須關(guān)掉,因?yàn)檫@個(gè)sock對(duì)它沒用了close(_sock);}}~tcpServer(){close(listen_sock);} };

以上代碼中關(guān)于父子進(jìn)程之間的關(guān)系,以及進(jìn)程等待這里就不細(xì)談了,詳見

Linux系統(tǒng)編程

再次強(qiáng)調(diào),對(duì)于子進(jìn)程,它可以關(guān)閉監(jiān)聽套接字,因?yàn)樽舆M(jìn)程是用來通信的,它只關(guān)心sock,對(duì)于父進(jìn)程則必須要關(guān)閉sock,只保留監(jiān)聽套接字,否則客戶端連接的越多,系統(tǒng)資源將會(huì)被耗盡

再次測(cè)試,可以發(fā)現(xiàn)多個(gè)客戶端可以同時(shí)連接服務(wù)器

(2)多線程版本

在Linux系統(tǒng)編程那一部分我們?cè)敿?xì)說過多線程和多進(jìn)程的優(yōu)缺點(diǎn),創(chuàng)建進(jìn)程的代價(jià)遠(yuǎn)遠(yuǎn)高于創(chuàng)建線程,因此多線程版本如下

#include <iostream> #include <pthread.h> #include <signal.h> #include <string> #include <unistd.h> #include <cstdio> #include <cstring> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> using namespace std;#define BACKLOG 5class tcpServer { private:int _port;int listen_sock; public:tcpServer(int port=8080):_port(port){}void initServer(){listen_sock=socket(AF_INET,SOCK_STREAM,0);//區(qū)別UDP,TCP采用流式套接字if(listen_sock < 0){cout<<"套接字創(chuàng)建失敗"<<endl;exit(2);}struct sockaddr_in local;local .sin_family=AF_INET;local.sin_port=htons(_port);local.sin_addr.s_addr=htonl(INADDR_ANY);if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0){cout<<"綁定失敗"<<endl;exit(3);}//開啟監(jiān)聽if(listen(listen_sock,BACKLOG) < 0){cout<<"綁定失敗"<<endl;exit(4);}}static void Service(int sock){char buffer[1024];while(1){size_t s=recv(sock,buffer,sizeof(buffer)-1,0);if(s > 0){buffer[s]='\0';cout<<"服務(wù)端收到客戶端的消息:"<<buffer<<endl;send(sock,buffer,strlen(buffer),0);}if(s==0){cout<<"客戶端已經(jīng)下線"<<endl;close(sock);break;}}}static void* Route(void* args)//線程路線{pthread_detach(pthread_self());int* p=(int*)args;int sock=*p;Service(sock);return NULL;}void startServer(){sockaddr_in endpoint;while(1){//acceptsocklen_t len=sizeof(endpoint);int _sock=accept(listen_sock,(struct sockaddr*)&endpoint,&len);if(_sock < 0){cout<<"accept失敗"<<endl;continue;}string client_info=inet_ntoa(endpoint.sin_addr);client_info+=":";client_info+=to_string(ntohs(endpoint.sin_port));cout<<"一臺(tái)新的客戶端已經(jīng)連接:"<<client_info<<endl;pthread_t tid;int* p=new int(sock);//在堆上開辟,p是棧私有的,防止出現(xiàn)bugpthread_create(&tid,nullptr,Route,(void*)&_sock);}}~tcpServer(){close(listen_sock);} };

效果如下

(3)線程池版本

關(guān)于線程部分,可以查看

Linux系統(tǒng)編程41:多線程之線程池的概念及實(shí)現(xiàn)

服務(wù)器大致邏輯為:

  • 初始化服務(wù)器,服務(wù)器初始化時(shí)創(chuàng)建線程池
  • 啟動(dòng)服務(wù)器,創(chuàng)建任務(wù),將任務(wù)放進(jìn)線程池的任務(wù)隊(duì)列中
  • 由于任務(wù)的加入,喚醒了線程池中的線程,線程拿到任務(wù),調(diào)用任務(wù)的工作接口,進(jìn)行工作
  • 工作完畢調(diào)用析構(gòu)函數(shù),釋放套接字
  • 下面的代碼簡單的實(shí)現(xiàn)了客戶端發(fā)送英文單詞,服務(wù)端進(jìn)行翻譯的過程

    thread_pool.h

    #pragma once #include <iostream> #include <pthread.h> #include <signal.h> #include <string> #include <unistd.h> #include <cstdio> #include <cstring> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <queue> #include <map> using namespace std;struct Task//任務(wù) { private:int _sock;//套接字static map<string,string> dict;//字典 public:Task(){}~Task(){close(_sock);}Task(int sock):_sock(sock){dict.insert(pair<string,string>("apple","蘋果"));dict.insert(pair<string,string>("melon","西瓜"));dict.insert(pair<string,string>("orange","橘子"));}void work()//獲得任務(wù)后在這里進(jìn)行判斷{char buffer[1024];size_t s=recv(_sock,buffer,sizeof(buffer)-1,0);if(s > 0){buffer[s]='\0';string key=buffer;//拿到用戶的鍵值cout<<"服務(wù)端收到客戶端的消息:"<<buffer<<endl;send(_sock,dict[key].c_str(),dict[key].size(),0);//給用戶返回翻譯結(jié)果}if(s==0){cout<<"客戶端已經(jīng)下線"<<endl;}} };map<string,string>Task:: dict;class ThreadPool//線程池 { private:bool _thread_quit_flag;//線程退出標(biāo)志,如果主線程發(fā)送了信號(hào),就將其置為trueint _thread_num;//線程池線程數(shù)量queue<Task*> q;//任務(wù)隊(duì)列,存放指針pthread_mutex_t lock;//保護(hù)任務(wù)隊(duì)列的鎖pthread_cond_t cond;//條件變量,當(dāng)沒有任務(wù)時(shí)線程池的線程掛起,當(dāng)有任務(wù)時(shí)喚醒線程池線程public:void ThreadLock(){//鎖pthread_mutex_lock(&lock);}void ThreadUnlock(){//解鎖pthread_mutex_unlock(&lock);}bool IsEmpty(){//判斷任務(wù)隊(duì)列為空return q.size()==0;}void Threadwait(){//沒有任務(wù)線程池的線程掛起pthread_cond_wait(&cond,&lock);}void Threadwakeup(){//當(dāng)主線程添加了任務(wù)就喚醒線程池線程pthread_cond_signal(&cond);}void ThreadWakeAll(){//最后發(fā)送退出信號(hào)時(shí),將所有線程喚醒,然后讓其退出pthread_cond_broadcast(&cond);//喚醒所有線程}void ThreadQuit(){//線程退出函數(shù)pthread_exit(nullptr);}public:ThreadPool(int thread_num=5):_thread_quit_flag(false),_thread_num(thread_num)//flag默認(rèn)設(shè)置為flase,不退出{pthread_mutex_init(&lock,nullptr);pthread_cond_init(&cond,nullptr);}~ThreadPool(){pthread_mutex_destroy(&lock);pthread_cond_destroy(&cond);}static void* Route(void* arg)//所有的線程都要執(zhí)行這樣的流程:先判斷是否有任務(wù),有就執(zhí)行,沒有就掛起{pthread_detach(pthread_self());//線程 ThreadPool* this_p=(ThreadPool*)arg;//某個(gè)線程的this指針while(1){this_p->ThreadLock();//鎖定任務(wù)隊(duì)列while(this_p->_thread_quit_flag==false && this_p->IsEmpty())//如果沒有發(fā)出退出信號(hào)并且隊(duì)列為空,那么就讓線程掛起{this_p->Threadwait();//當(dāng)線程蘇醒時(shí)(可能接收到了廣播信號(hào)或者位于線程隊(duì)列它被下一個(gè)喚醒了),就會(huì)從掛起的地方醒來,繼續(xù)向下執(zhí)行}if((this_p->_thread_quit_flag==false && !this_p->IsEmpty()) || (this_p->_thread_quit_flag==true && !this_p->IsEmpty()))//醒來的原因不管是否是因?yàn)榻邮盏搅送顺鲂盘?hào),反正只要有任務(wù)就得先執(zhí)行完{Task* t;this_p->Get(&t);this_p->ThreadUnlock();//注意不要在臨界資源內(nèi)做任務(wù),效率很低t->work();//獲得任務(wù)后進(jìn)行計(jì)算delete t;//任務(wù)結(jié)束關(guān)閉套接字,調(diào)用任務(wù)析構(gòu)函數(shù)關(guān)閉套接字}//如果接受到信號(hào)且隊(duì)列已經(jīng)Wie空了,那么就退出線程else if(this_p->_thread_quit_flag==true && this_p->IsEmpty())//如果醒來的原因是因?yàn)榻邮艿搅送顺鲂盘?hào),而且任務(wù)隊(duì)列中已經(jīng)沒有任務(wù)了,那么就退出線程{this_p->ThreadUnlock();this_p->ThreadQuit();//退出時(shí)不要忘記解鎖,否則將來只有一個(gè)線程能推出,其余線程無法退出}} }void ThreadPoolInit()//風(fēng)險(xiǎn)操作不要在構(gòu)造函數(shù)中寫,該函數(shù)用于創(chuàng)建線程池線程,并讓線程執(zhí)行Route流程{pthread_t t;for(int i=0;i < _thread_num;i++){pthread_create(&t,nullptr,Route,this);//這里最后一個(gè)參數(shù)傳入了this指針,相應(yīng)的Route函數(shù)也是靜態(tài)成員函數(shù)//如果不傳入this指針,那么當(dāng)這個(gè)參數(shù)就會(huì)和非靜態(tài)成員函數(shù)搶this指針的位置,因此會(huì)造成參數(shù)過多的問題//所以在這里傳入this,然后在形參中使用this調(diào)用自己的成員函數(shù)即可}}void ThreadPoolQuit()//線程池退出{_thread_quit_flag=true;//將結(jié)束標(biāo)志置為trueThreadWakeAll();//喚醒所有線程}void Get(Task** t)//線程獲取任務(wù){Task* out=q.front();//注意是指針q.pop();*t=out;//這是解引用}void Put(Task& t)//主線程放任務(wù){ThreadLock();q.push(&t);//注意這是取地址ThreadUnlock();Threadwakeup();//當(dāng)放了一個(gè)任務(wù)后立馬喚醒線程,這里一般情況下不要喚醒所有線程,會(huì)產(chǎn)生掠群效應(yīng),影響穩(wěn)定性} };

    tcp_server.h

    #include "thread_pool.h" #define BACKLOG 5class tcpServer { private:int _port;int listen_sock;ThreadPool* _th; public:tcpServer(int port=8080):_port(port),_th(nullptr){}void initServer(){listen_sock=socket(AF_INET,SOCK_STREAM,0);//區(qū)別UDP,TCP采用流式套接字if(listen_sock < 0){cout<<"套接字創(chuàng)建失敗"<<endl;exit(2);}struct sockaddr_in local;local .sin_family=AF_INET;local.sin_port=htons(_port);local.sin_addr.s_addr=htonl(INADDR_ANY);if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0){cout<<"綁定失敗"<<endl;exit(3);}//開啟監(jiān)聽if(listen(listen_sock,BACKLOG) < 0){cout<<"綁定失敗"<<endl;exit(4);}_th=new ThreadPool();_th->ThreadPoolInit();//初始化線程池}static void Service(int sock){char buffer[1024];while(1){size_t s=recv(sock,buffer,sizeof(buffer)-1,0);if(s > 0){buffer[s]='\0';cout<<"服務(wù)端收到客戶端的消息:"<<buffer<<endl;send(sock,buffer,strlen(buffer),0);}if(s==0){cout<<"客戶端已經(jīng)下線"<<endl;close(sock);break;}}}static void* Route(void* args){pthread_detach(pthread_self());int* p=(int*)args;int sock=*p;Service(sock);return NULL;}void startServer(){sockaddr_in endpoint;while(1){//acceptsocklen_t len=sizeof(endpoint);int _sock=accept(listen_sock,(struct sockaddr*)&endpoint,&len);if(_sock < 0){cout<<"accept失敗"<<endl;continue;}string client_info=inet_ntoa(endpoint.sin_addr);client_info+=":";client_info+=to_string(ntohs(endpoint.sin_port));cout<<"一臺(tái)新的客戶端已經(jīng)連接:"<<client_info<<endl;//構(gòu)建任務(wù),放進(jìn)線程池Task* t=new Task(_sock);_th->Put(*t);}}~tcpServer(){close(listen_sock);}};

    tcp_client.h

    #include <iostream> #include <string> #include <cstring> #include <unistd.h> #include <cstdio> #include <cstdlib> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <netinet/in.h> using namespace std;class tcpClient { private:string _ip;int _port;int _sock; public:tcpClient(string ip="127.0.0.1",int port=8080):_ip(ip),_port(port){}void initClient(){_sock=socket(AF_INET,SOCK_STREAM,0);if(_sock < 0){cout<<"套接字創(chuàng)建失敗"<<endl;exit(2);}struct sockaddr_in sev;sev.sin_family=AF_INET;sev.sin_port=htons(_port);sev.sin_addr.s_addr=inet_addr(_ip.c_str());if(connect(_sock,(struct sockaddr*)&sev,sizeof(sev))!=0){cout<<"connect失敗"<<endl;exit(3);}}void startClient(){char mssage[64];while(1){ cout<<"請(qǐng)輸入信息**:";fflush(stdout);size_t s=read(0,mssage,sizeof(mssage)-1);//從標(biāo)準(zhǔn)輸入讀入if(s > 0){mssage[s-1]='\0';//剔除換行符send(_sock,mssage,strlen(mssage),0);ssize_t ret=recv(_sock,mssage,sizeof(mssage)-1,0);if(ret > 0){cout<<"客戶端得到服務(wù)端消息"<<mssage<<endl;}else if(ret ==0){break;}}}}~tcpClient(){close(_sock);} };

    效果如下

    新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎(jiǎng)!定制產(chǎn)品紅包拿不停!

    總結(jié)

    以上是生活随笔為你收集整理的2-4:套接字(Socket)编程之TCP通信的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。