建立基于TCP的客戶端和服務端

函數介紹:?

1.socket()

作用:創建套接字

domain:

  • AF_INET:IPv4 Internet 協議族。
  • AF_INET6:IPv6 Internet 協議族。
  • AF_UNIX:Unix 域協議族,用于在同一臺主機上的進程間通信。

type:

  • SOCK_STREAM:提供序列化的、可靠的、雙向連接的字節流服務,通常用于TCP連接。
  • SOCK_DGRAM:提供無連接的、不可靠的數據報服務,通常用于UDP通信。

protocol:

????????這個參數指定了使用的特定協議。對于很多常見的套接字類,如?SOCK_STREAM?和?SOCK_DGRAM,這個參數可以設置為0,系統會自動選擇一個默認協議。

return value:

socket() 函數調用成功時,返回一個非負整數,即新創建的套接字的文件描述符(socket descriptor)。如果調用失敗,函數返回-1,并且 errno 被設置為描述錯誤的值。

服務端:

2.bind()

作用:將套接字(fd)和ip和port綁定起來

sockfd:

  • 要綁定的文件描述符,也就是socket的返回值

addr:

?指向?sockaddr?結構體的指針,該結構體包含了套接字的地址信息。

  • sin_family:協議家族
  • sin_port:要綁定的端口。
  • sin_addr.s_addr:要綁定的ip

?介紹以下里面的函數:

h表示host,n表示network

htons?通常用于端口號,htonl?通常用于IP地址

作用:將IPv4地址的點分十進制字符串表示轉換為網絡字節序的二進制值

INADDR_ANY:用于指定一個特殊的IPv4地址0.0.0.0,表示“接受任何可用的網絡接口”,通常用在服務器監聽上。

addrlen:?大小

return value:

成功返回0,失敗返回-1并設置錯誤碼。

3.listen()

作用:將套接字設置為監聽狀態可以監聽鏈接請求

sockfd:socket()的返回值

backlog:一個大于0的整數(后續補充)

return value:成功返回0失敗返回-1并設置錯誤碼

4.accept()

?作用:接受鏈接請求

sockfd:socket()的返回值

addr和addrlen:輸出型參數,用于存儲連接客戶端的地址信息。

return value:

成功返回一個新的文件描述符用于與連接的客戶端進行通信。之后的read和write傳入的都是accept的返回值。

失敗返回-1,設置錯誤碼。

?客戶端:

2.connect()

?

作用:向服務端發起鏈接

sockfd:socket()的返回值

addr:要連接的服務端的地址信息

addrlen:addr的大小

return value:

成功返回0,失敗返回-1并設置錯誤碼。

代碼:?

tcpServer.hpp

#pragma once
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <cstdlib>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include "log.hpp"
#include "threadpool.hpp"namespace server
{enum{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR,LISTEN_ERR,OPEN_ERR};static const uint16_t gport = 8080;static const int gbacklog = 5; // 底層鏈接隊列的長度class TcpServer;class ThreadData{public:ThreadData(TcpServer *self, int sock) : _self(self), _sock(sock){}public:TcpServer *_self;int _sock;};class TcpServer{public:TcpServer(const uint16_t &port = gport) : _listensock(-1), _port(port){}void initServer(){// 1.創建socket文件套接字對象_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){logMessage(FATAL, "create socket error");exit(SOCKET_ERR);}logMessage(NORMAL, "create socket success:%d", _listensock);// 2.bind綁定自己的網絡信息struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0){logMessage(FATAL, "bind socket error");exit(BIND_ERR);}logMessage(NORMAL, "bind socket success");// 3.設置socket為監聽狀態,獲取新的客戶端鏈接if (listen(_listensock, gbacklog) < 0){logMessage(FATAL, "listen socket error");exit(LISTEN_ERR);}logMessage(NORMAL, "listen socket success");}void start(){// 4.線程池初始化ThreadPool<Task>::getInstance()->run();logMessage(NORMAL, "Thread init success");// signal(SIGCHLD,SIG_IGN);//忽略這個信號for (;;){// 4.server獲取新鏈接// sock,和client進行通信的fdstruct sockaddr_in peer;socklen_t len = sizeof(peer);int sock = accept(_listensock, (struct sockaddr *)&peer, &len);if (sock < 0){logMessage(ERROR, "accept error");continue;}logMessage(NORMAL, "accept a new link success,get a new sock:%d", sock);serviceIO(sock);close(sock);//對一個已經使用完畢的sock,我們必須要關閉這個sock,要不然會導致文件描述符泄漏}}// static void *threadRoutine(void *args)// {//     pthread_detach(pthread_self());//     ThreadData *td = static_cast<ThreadData *>(args);//     td->_self->serviceIO(td->_sock);//     delete td;//     close(td->_sock);//     return nullptr;// }~TcpServer() {}private:int _listensock;uint16_t _port;};
}
// 5.這里就是一個sock,未來通信我們就用這個sock,面向字節流的,后續全部都是文件操作!
//  serviceIO(sock);
//  close(sock);//對一個已經使用完畢的sock,我們必須要關閉這個sock,要不然會導致文件描述符泄漏// version2,多進程版,多線程版,線程池版
// version1,多進程版
//  pid_t id = fork();
//  if(id == 0)//child
//  {
//      close(_listensock);
//      if(fork()>0) exit(0);//關閉child,創建了一個孫子。因為child退了,孫子進程成了孤兒進程,由操作系統回收
//      serviceIO(sock);
//      close(sock);//對一個已經使用完畢的sock,我們必須要關閉這個sock,要不然會導致文件描述符泄漏
//      exit(0);
//  }
// father
//  pid_t ret = waitpid(id,nullptr,0);
//  if(ret>0)
//  {
//      std::cout<<"wait success:"<<ret<<std::endl;
//  }// version2,多進程版
//  pid_t id = fork();
//  if(id == 0)//child
//  {
//      close(_listensock);
//      serviceIO(sock);
//      close(sock);//對一個已經使用完畢的sock,我們必須要關閉這個sock,要不然會導致文件描述符泄漏
//      exit(0);
//  }
//  close(sock);// version3:多線程版
//  pthread_t tid;
//  ThreadData* td = new ThreadData(this,sock);
//  pthread_create(&tid,nullptr,threadRoutine,td);// version4:線程池
//ThreadPool<Task>::getInstance()->push(Task(sock, serviceIO));

?tcpServer.cc

#include"tcpServer.hpp"
#include<memory>
using namespace server;
using namespace std;
static void Usage(string proc)
{cout<<".\nUsage:\n\t"<<proc<<"local_port\n\n";
}
//tcp服務器,啟動上和udp Server一模一樣
//./tcpServer local_port
int main(int argc,char* argv[])
{if(argc!=2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);unique_ptr<TcpServer> tsvr(new TcpServer(port));tsvr->initServer();tsvr->start();return 0;}

tcpClient.hpp?

#pragma once
#include <iostream>
#include<string>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<cstring>
#define NUM 1024
class TcpClient
{public:TcpClient(const std::string &serverip,const uint16_t &serverport):_sock(-1),_serverip(serverip),_serverport(serverport){}void initClient(){//1.創建socket_sock = socket(AF_INET,SOCK_STREAM,0);if(_sock < 0){std::cerr<<"socket create error"<<std::endl;exit(2);}//2.客戶端不用顯示的bind//3.要不要listen?沒人連我啊//4.要不要accept?no//5.要什么呢?要發起鏈接!}void start(){struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(_serverport);server.sin_addr.s_addr = inet_addr(_serverip.c_str());if(connect(_sock,(struct sockaddr*)&server,sizeof(server)) != 0){std::cerr<<"socket connect error"<<std::endl;}else{std::string msg;while(1){std::cout<<"Enter#";std::getline(std::cin,msg);write(_sock,msg.c_str(),msg.size());char buffer[NUM];int n = read(_sock,buffer,sizeof(buffer)-1);if(n>0){//目前把讀到的數據當字符串buffer[n] = 0;std::cout<<"Server回顯#"<<buffer<<std::endl;}else{break;}}}}~TcpClient(){if(_sock >= 0)close(_sock);//文件描述符的生命周期隨進程,所以這里不寫也行}
private:int _sock;std::string  _serverip;uint16_t _serverport;};

tcpClient.cc?

#include"tcpClient.hpp"
#include<memory>
using namespace std;
static void Usage(string proc)
{cout<<".\nUsage:\n\t"<<proc<<"serverip serverport\n\n";
}
//./tcpClient serverip serverport
int main(int argc,char* argv[])
{if(argc!=3){Usage(argv[0]);exit(1);}string serverip = argv[1];uint16_t serverport = atoi(argv[2]);unique_ptr<TcpClient> tcli(new TcpClient(serverip,serverport));tcli->initClient();tcli->start();return 0;
}

Task.hpp?

#pragma once#include <iostream>
#include <string>
#include <cstdio>
#include <functional>
#include <unistd.h>
#include <string.h>
#include"log.hpp"
void serviceIO(int sock)
{char buffer[1024];while (true){ssize_t n = read(sock, buffer, sizeof(buffer) - 1);if (n > 0){// 目前我們把讀到的數據當成字符串,截止目前// 為什么UDP并不符合文件特性,文件也是字節流的,UDP是數據報的,所以要用特殊函數來讀buffer[n] = 0;std::cout << "recv messages:" << buffer << std::endl;write(sock, buffer, strlen(buffer)); // 多路轉接}else if (n == 0){// 代表client退出logMessage(NORMAL, "client quit,me too!");break;}}close(sock);
}class Task
{using func_t = std::function<void(int)>;public:Task() {}Task(int sock, func_t func): _sock(sock), _callback(func){}void operator()(){_callback(_sock);}private:int _sock;func_t _callback;
};

log.hpp?

#pragma once
#include<iostream>
#include<string>
#include<cstdarg>
#include<ctime>
#include<unistd.h>
//cat /var/log/messages系統日志
//日志等級
#define DEBUG   0
#define NORMAL  1
#define WARNING 2
#define ERROR   3
#define FATAL   4//致命錯誤const char* to_levelstr(int level)
{switch(level){case DEBUG:return "DEBUG";case NORMAL:return "NORMAL";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";}
}
// void logMessage(int level,const char* messages)
// {
//     std::cout<<messages<<std::endl;
// }
// void logMessage(DEBUG,"hello %f ,%d,%c",3.14,10,'C');
void logMessage(int level,const char* format,...)
{//[日志等級][時間戳/時間][pid][message]//暫定// va_list start;// va_start(start);// while(*p)// {//     switch(*p)//     {//         case '%'://             p++;//             if(*p == 'f') arg = va_arg(start,float);//         ...//     }// }// va_end(start);#define NUM 1024char logprefix[NUM];snprintf(logprefix,sizeof(logprefix),"[%s][%ld][pid:%d]",to_levelstr(level),(long int)time(nullptr),getpid());char logcontent[NUM];va_list arg;va_start(arg,format);vsnprintf(logcontent,sizeof(logcontent),format,arg);std::cout<<logprefix<<logcontent<<std::endl;
}

makefile?

cc=g++
.PHONY:all
all:tcpClient tcpServertcpClient:tcpClient.cc$(cc) -o $@ $^ -std=c++11 -lpthread
tcpServer:tcpServer.cc$(cc) -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:rm -f tcpClient tcpServer

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/62854.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/62854.shtml
英文地址,請注明出處:http://en.pswp.cn/web/62854.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CNCF云原生生態版圖-分類指南(三)- 運行時

CNCF云原生生態版圖-分類指南&#xff08;三&#xff09;- 運行時 CNCF云原生生態版圖-分類指南三、運行時&#xff08;Runtime&#xff09;&#xff08;一&#xff09;云原生存儲&#xff08;Cloud Native Storage&#xff09;1. 是什么&#xff1f;2. 解決什么問題&#xff1…

機器學習經典算法

機器學習經典算法學習和分享。 k近鄰算法 線性回歸 梯度下降法 PCA主成分分析法 多項式回歸 邏輯回歸 支撐向量機SVM 決策樹 隨機森林 評價分類指標

MVC基礎——市場管理系統(三)Clean Architecture

文章目錄 項目地址五、Clean Architecture5.1 user cage driven5.1.1創建CoreBusiness 5.2 創建UseCases5.2.1 創建CategoriesUseCases1. 創建VeiwCategoriesUseCase獲取所有Cagegory 5.2.2. 實現ICategoryRepository接口3. 實現獲取所有Category的方法4. 實現獲取一個Cagegory…

手機上和電腦上都能觀看的翻頁電子書是如何制作的?

想知道手機上和電腦上都能觀看的翻頁電子書是都是如何制作的&#xff1f; 想知道這樣的電子書是怎樣呈現出來的&#xff1f; 那收藏這篇文章&#xff0c;我來跟大家說說該如何實現。 操作方法 一、登錄FLBOOK 二、開始制作&#xff0c;有多種創建方式&#xff0c;分別是&…

ABAP時間戳與日期時間轉換及時區處理

一、時間戳轉換為日期時間 1. 基本轉換 CONVERT TIME STAMP <fs_back>-lastchangedatetime TIME ZONE sy-zonloINTO DATE DATA(lv_date)TIME DATA(lv_time).2. 解決8小時時差問題的方案 方案1&#xff1a;直接使用UTC時區&#xff08;推薦&#xff09; CONVERT TIME …

Java 實現給pdf文件指定位置蓋章功能

Java 實現給pdf文件指定位置蓋章功能 開發中遇到一個需求, 需要給用戶上傳的的pdf文件, 指定位置上蓋公章的功能, 經過調研和對比, 最終確定實現思路. 這里是使用pdf文件中的關鍵字進行章子的定位, 之所以這樣考慮是因為如果直接寫死坐標的話, 可能會出現因pdf大小, 縮放, 蓋章…

ASP.NET Core API + MySql

環境 數據庫&#xff1a; mysql8.0 后端&#xff1a; vs2022 ASP.NET Core API .net 8 前端&#xff1a; Hbuilderx bootstrap 5.3.0 jquery v3.7.1 bootstrap-table 1.23.5 創建項目 添加資源包 AutoMapper Microsoft.EntityFrameworkCore.Tools 8.0.0 Pomelo.EntityFramew…

RFDiffusion 計算鍵角函數get_ang解讀

get_ang 函數&#xff08;kinematics.py包中&#xff09;計算三組原子 a,b,c 所形成的平面角&#xff08;planar angle&#xff09;&#xff0c;即 b 為頂點&#xff0c; a,b,c 所確定的角度。 源代碼&#xff1a; def get_ang(a, b, c):"""calculate planar …

Bananna Pi開源社區聯合矽昌通信打造開源的低成本Wifi5路由器

香蕉派 BPI-Wifi5 路由器采用矽昌SF19A2890S2芯片方案設計。它是一款高性能無線路由器&#xff0c;適用于小微企業、家庭和其他網絡環境。Banana Pi開源社區提供整體解決方案。所有代碼開源&#xff0c;用戶可以在上面自由開發自己的應用。 Banana Pi wifi5 路由器github代碼: …

圖像融合算法筆記2024 CDTNet

目錄 ControlCom-Image-Composition CDTNet-High-Resolution-Image-Harmonization 依賴項: trilinear 推理代碼ok: ControlCom-Image-Composition diffusesion https://github.com/bcmi/ControlCom-Image-Composition CDTNet-High-Resolution-Image-Harmonization

item2 for macos

安裝Item2 brew install iterm2 查看終端類型 cat /etc/shells Mac OS X 10.15 已經將默認的shell從Bash換成了zsh&#xff0c;所以不用安裝&#xff0c;10.15以前的可以使用下面的命令進行安裝 brew install zsh 安裝Oh My ZSH # curl sh -c "$(curl -fsSL https://ra…

https證書生成、linux 生成https證書、nginx 配置https證書

1. 檢查 Certbot 是否已安裝 which certbot 2. 安裝 Certbot 2.1啟用 EPEL 倉庫&#xff08;如果尚未啟用&#xff09;&#xff1a; sudo yum install epel-release 2.2 安裝 Certbot 和 Nginx 插件&#xff1a; sudo yum install certbot python3-certbot-nginx 2.3驗證安…

Pytest-Bdd-Playwright 系列教程(14):Docstring 參數

Pytest-Bdd-Playwright 系列教程&#xff08;14&#xff09;&#xff1a;Docstring 參數 前言一、什么是docstring?二、基本語法三、主要特點四、實際例子五、注意事項六、使用建議總結 前言 在自動化測試的過程中&#xff0c;我們經常需要處理復雜的測試數據或需要輸入多行文…

手機租賃系統開發指南一站式服務流程解析

內容概要 手機租賃系統的開發是一個復雜但有趣的過程&#xff0c;像搭建樂高一樣&#xff0c;只要找到合適的模塊&#xff0c;就能打造出一個賓至如歸的租賃平臺。在這部分&#xff0c;我們將對開發流程的整體結構進行簡要概述&#xff0c;并指出每個環節的重要性。 首先&…

OpenAI 正式賦予 ChatGPT 通過視頻實時與用戶互動的能力

每周跟蹤AI熱點新聞動向和震撼發展 想要探索生成式人工智能的前沿進展嗎&#xff1f;訂閱我們的簡報&#xff0c;深入解析最新的技術突破、實際應用案例和未來的趨勢。與全球數同行一同&#xff0c;從行業內部的深度分析和實用指南中受益。不要錯過這個機會&#xff0c;成為AI領…

深入了解C++中const的用法

文章目錄 一、C中的const如何理解&#xff1f;二、C中的const與C語言中的const有何區別&#xff1f;三、const與指針、引用的結合使用 一、C中的const如何理解&#xff1f; 在C中&#xff0c;const是一個關鍵字&#xff0c;用來表示常量性&#xff0c;意在告訴編譯器某些變量或…

EasyExcel設置表頭上面的那種大標題(前端傳遞來的大標題)

1、首先得先引用easyExcel的版本依賴&#xff0c;我那 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.6</version> </dependency> 2、然后得弄直接的實體類&#xff0c;&…

純血鴻蒙崛起,原生Android挑戰?兩大操作系統巔峰對決,智能設備未來誰主沉浮?

鴻蒙HarmonyOS和原生Android系統雖然在一些方面相似&#xff0c;但在架構、設計理念、API、開發工具等方面存在一些差異。鴻蒙系統的目標是跨設備、分布式的操作系統&#xff0c;強調多設備協同和資源共享&#xff0c;而Android則主要集中在智能手機和移動設備領域。 下面將從…

計算機網絡:傳輸層、應用層、網絡安全、視頻/音頻/無線網絡、下一代因特網

目錄 &#xff08;五&#xff09;傳輸層 1&#xff0e;傳輸層尋址與端口 2&#xff0e;無連接服務與面向連接服務 3. 傳輸連接的建立與釋放 4. UDP 的優點 5. UDP 和 TCP 報文段報頭格式 6. TCP 的流量控制 7&#xff0e;TCP 的擁塞控制 8. TCP 傳送連接的管理 &#…

【前端開發】HTML+CSS網頁,可以拿來當作業(免費開源)

HTML代碼 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content_lizhongyu"widthdevice-width, initial-scale1.0"><title>小兔鮮兒-新鮮、惠民、快捷<…