一.socket

1.1socket接口

它返回的是一个文件描述符。创建socket文件描述符(TCP/UDP,客户端+服务器)

• socket()打开一个网络通讯端口,如果成功的话,就像 open()一样返回一个文件描 述符;

• 应用程序可以像读写文件一样用 read/write 在网络上收发数据;

• 如果 socket()调用出错则返回-1;

• 对于 IPv4, family 参数指定为 AF_INET;

• 对于 UDP 协议,type 参数指定为 SOCK_DGRAM, 表示面向数据报的传输协议

• protocol 参数的介绍从略,指定为 0 即可。 

Socket的第一个参数:domain(域/协议族)

Socket编程中,socket()函数的第一个参数指定通信的协议族(Protocol Family),决定了 socket 的底层通信协议类型。常见的协议族包括:

  • AF_INET
    IPv4协议族,用于基于IPv4的网络通信(如互联网或本地网络)。这是最常用的选项。

    int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    

  • AF_INET6
    IPv6协议族,支持IPv6地址格式的通信。

    int socket_fd = socket(AF_INET6, SOCK_STREAM, 0);
    

  • AF_UNIX/AF_LOCAL
    本地通信协议族,用于同一台主机上的进程间通信(通过文件系统路径)。

    int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    

  • AF_PACKET
    底层数据包接口(如直接访问网络层数据包,常用于网络工具开发)。


参数意义

该参数定义了 socket 的地址类型,直接影响后续绑定(bind())或连接(connect())时使用的地址结构体。例如:

  • 使用 AF_INET 时,地址结构体为 struct sockaddr_in(包含IPv4地址和端口)。
  • 使用 AF_UNIX 时,地址结构体为 struct sockaddr_un(包含文件路径)。

注意事项

  • AF_ vs PF_
    历史上有 AF_(地址族)和 PF_(协议族)两种前缀,但在现代系统中两者通常等价(如 AF_INETPF_INET 可互换)。
  • 错误处理
    若参数无效(如不支持的协议族),socket() 会返回 -1,并设置 errnoEAFNOSUPPORT

socket 的第二个参数详解

socket 的第二个参数指定套接字的类型,决定了数据传输的方式和协议特性。以下是常见的套接字类型及其用途:

SOCK_STREAM
  • 面向连接的字节流套接字,使用 TCP 协议。
  • 提供可靠、有序、双向的数据传输。
  • 适用于需要数据完整性的场景,如 HTTP、FTP。
SOCK_DGRAM
  • 无连接的数据报套接字,使用 UDP 协议。
  • 传输速度快但不可靠,可能丢包或乱序。
  • 适用于实时性要求高的场景,如视频流、DNS 查询。
SOCK_RAW
  • 原始套接字,允许直接访问底层协议(如 IP、ICMP)。
  • 需要管理员权限,常用于网络探测或自定义协议开发。
SOCK_SEQPACKET
  • 提供有序、可靠、基于消息的传输(如 SCTP 协议)。
  • 结合了流式和数据报的特性,适用于电信领域。
SOCK_RDM
  • 可靠的数据报套接字,保证数据不丢失但可能乱序。
  • 较少使用,特定于某些协议(如 RDS)

注意事项

  • 第二个参数需与第一个参数(地址族,如 AF_INET)兼容。
  • 某些类型(如 SOCK_RAW)可能需要特殊权限。
  • 具体支持的类型取决于操作系统和协议栈。

 第三个参数

指定具体的传输协议。通常设置为0,表示根据domaintype自动选择默认协议。

1.2bind接口

 绑定端口号(TCP/UDP,服务器)

• 服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服 务器程序的地址和端口号后就可以向服务器发起连接; 服务器需要调用 bind 绑定一 个固定的网络地址和端口号;

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

• bind()的作用是将参数 sockfd 和 myaddr 绑定在一起, 使 sockfd 这个用于网络 通讯的文件描述符监听 myaddr 所描述的地址和端口号;

• struct sockaddr *是一个通用指针类型,myaddr 参数实际上可以接受 多种协议的 sockaddr 结构体,而它们的长度各不相同,所以需要第三个参数 addrlen 指定结构体的长度; 

1.3listen接口

开始监听 socket (TCP, 服务器)

• listen()声明 sockfd 处于监听状态, 并且最多允许有 backlog 个客户端处于连接 等待状态, 如果接收到更多的连接请求就忽略, 这里设置不会太大(一般是 5)

• listen()成功返回 0,失败返回-1;

1.4accept接口

接收请求 (TCP, 服务器)

 

它返回的也是一个文件描述符,跟listensocket配合着使用

• 三次握手完成后, 服务器调用 accept()接受连接;

• 如果服务器调用 accept()时还没有客户端的连接请求,就阻塞等待直到有客户端 连接上来;

• addr 是一个传出参数,accept()返回时传出客户端的地址和端口号;

• 如果给 addr 参数传 NULL,表示不关心客户端的地址;

• addrlen 参数是一个传入传出参数(value-result argument), 传入的是调用者提 供的, 缓冲区 addr 的长度以避免缓冲区溢出问题, 传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区); 

1.5connect接口

建立连接 (TCP, 客户端)

• 客户端需要调用 connect()连接服务器;

• connect 和 bind 的参数形式一致, 区别在于 bind 的参数是自己的地址, 而 connect 的参数是对方的地址;

• connect()成功返回 0,出错返回-1;

 1.6sockeaddr结构

socketAPI 是一层抽象的网络编程接口 , 适用于各种底层网络协议 , IPv4 IPv6, 以及后面的UNIXDomainSocket. 然而 , 各种网络协议的地址格式并不相同 .

• IPv4 和 IPv6 的地址格式定义在 netinet/in.h 中,IPv4 地址用 sockaddr_in 结构 体表示,包括16 地址类型, 16 位端口号和 32 位 IP 地址.

• IPv4、IPv6 地址类型分别定义为常数 AF_INET AF_INET6. 这样,只要取得某 种 sockaddr 结构体的首地址,不需要知道具体是哪种类型的 sockaddr 结构体,就可 以根据地址类型字段确定结构体中的内容.

• socket API 可以都用 struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收 IPv4, IPv6, 以及 UNIX Domain Socket 各种类型的 sockaddr 结构体指针做为参数; 

 

虽然 socket api 的接口是 sockaddr, 但是我们真正在基于 IPv4 编程时, 使用的数据结 构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型, 端口号, IP 地址. 

 

in_addr 用来表示一个 IPv4 的 IP 地址. 其实就是一个 32 位的整数; 

通常这样初始化

网络地址为 INADDR_ANY, 这个宏表示本地的任意 IP 地址,因为服务器可能有 多个网卡,每个网卡也可能绑定多个 IP 地址, 这样设置可以在所有的 IP 地址上监听, 直到与某个客户端建立了连接时才确定下来到底用哪个 IP 地址 

 二.UDP

2.1简单的接口代码(demo)

InetAddr.hpp

#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>class InetAddr
{
public:InetAddr(struct sockaddr_in &addr):_addr(addr){_port = ntohs(addr.sin_port);_ip = inet_ntoa(addr.sin_addr);}std::string GetIp(){return _ip;}uint16_t GetPort(){return _port;}
private:std::string _ip;//点分十进制uint16_t _port;struct sockaddr_in _addr;
};

NoCopy.hpp

#pragma once#include<iostream>class NoCopy 
{
public:NoCopy(){}NoCopy(const NoCopy&) = delete;const NoCopy& operator=(const NoCopy&) = delete;~NoCopy(){}
};

Common.hpp

#pragma once enum ExitCode
{OK = 0,SOCKET_ERR,BIND_ERR
};

UdpServer.hpp

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#include <cstdlib>
#include <memory>
#include "nocopy.hpp"
#include "InetAddr.hpp"
#include "Common.hpp"
#include "Log.hpp"
using namespace LogModule;
const static int defaultport = 8888;
const static int defaultsockfd = -1;
const static int defaultsize = 1024;// 禁止拷贝和赋值
class UdpServer : public NoCopy
{
public:UdpServer(int port = defaultport) : _port(port), _sockfd(defaultsockfd){}void Init(){// 1.创建socket_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "socket error...";exit(SOCKET_ERR);}LOG(LogLevel::DEBUG) << "sockfd success: " << _sockfd;// 2.初始化结构体struct sockaddr_in local;bzero(&local,sizeof(local));//memset(&local,0,sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;//注意这里不要写特定的ip值local.sin_port = htons(_port);// 3.bind,结构体填完,还要设置到内核中int n = ::bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n!=0){LOG(LogLevel::FATAL) << "bind error...";exit(BIND_ERR);}LOG(LogLevel::DEBUG) << "bind success...";}void Start(){while(true){char buffer[defaultsize];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = recvfrom(_sockfd,buffer,sizeof(buffer) - 1,0,(struct sockaddr*)&peer,&len);if(n > 0){InetAddr addr(peer);buffer[n] = 0;std::cout << "client: [" << addr.GetIp() << " "<<addr.GetPort()<<" say# ]" << buffer <<std::endl;sendto(_sockfd,buffer,sizeof(buffer) - 1,0,(struct sockaddr*)&peer,len);}}}~UdpServer(){}private://std::string _ip;uint16_t _port;int _sockfd;
};

UdpClient.cc

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <cstdlib>
#include <string.h>
#include <memory>
#include "nocopy.hpp"
#include "InetAddr.hpp"
#include "Common.hpp"
#include "Log.hpp"using namespace LogModule;int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << " server_ip server_port" << std::endl;return 1;}std::string server_ip = argv[1];uint16_t server_port = std::stoi(argv[2]);// 1.还是创建socketint sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){LOG(LogLevel::FATAL) << "socket error...";return 2;}LOG(LogLevel::DEBUG) << "sockfd success: " << sockfd;// 2.clent 也要进行bind,但是不要跟server一样显示的进行bind,因为服务器的port是众所周知的,而client的port可能会非常多// 导致端口号冲突,bind的操作OS会随机给我们分配端口号// 3.填充server信息struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_addr.s_addr = inet_addr(server_ip.c_str()); // ip地址是点分十进制转换为网络序列的32为整数server.sin_family = AF_INET;server.sin_port = htons(server_port);while (true){std::string in;std::cout << "please enter# ";std::getline(std::cin, in);// std::cin>>in;//  std::cout<< 1 <<std::endl;int n = sendto(sockfd, in.c_str(), in.size(), 0, (struct sockaddr *)&server, sizeof(server));if (n < 0){LOG(LogLevel::ERROR) << "sendto failed";continue;}char buffer[1024];struct sockaddr_in peer;socklen_t peer_len = sizeof(peer);int m = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &peer_len);if (m > 0){buffer[m] = 0;std::cout << buffer << std::endl;}}return 0;
}

UdpServer.cc

#include"UdpServer.hpp"int main()
{Enable_Console_Log_Strtegy();std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>();usvr->Init();usvr->Start();return 0;
} 

运行结果:

服务器:

客户端: 

2.2代码小升级,网络字典查询

其实也就是在UdpServer端加上了一个上层调用的函数,让我们能够实现单词之间的转化

UdpServer.hpp

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#include <cstdlib>
#include <memory>
#include <functional>
#include "nocopy.hpp"
#include "InetAddr.hpp"
#include "Common.hpp"
#include "Log.hpp"
using namespace LogModule;
const static int defaultport = 8888;
const static int defaultsockfd = -1;
const static int defaultsize = 1024;using func_t = std::function<std::string(const std::string&,InetAddr& cli)>;// 禁止拷贝和赋值
class UdpServer : public NoCopy
{
public:UdpServer(func_t func,int port = defaultport) : _port(port), _sockfd(defaultsockfd),_func(func){}void Init(){// 1.创建socket_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "socket error...";exit(SOCKET_ERR);}LOG(LogLevel::DEBUG) << "sockfd success: " << _sockfd;// 2.初始化结构体struct sockaddr_in local;bzero(&local,sizeof(local));//memset(&local,0,sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;//注意这里不要写特定的ip值local.sin_port = htons(_port);// 3.bind,结构体填完,还要设置到内核中int n = ::bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n!=0){LOG(LogLevel::FATAL) << "bind error...";exit(BIND_ERR);}LOG(LogLevel::DEBUG) << "bind success...";}void Start(){while(true){char buffer[defaultsize];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = recvfrom(_sockfd,buffer,sizeof(buffer) - 1,0,(struct sockaddr*)&peer,&len);if(n > 0){InetAddr addr(peer);buffer[n] = 0;std::string result = _func(buffer,addr);std::cout << "client: [" << addr.GetIp() << " "<<addr.GetPort()<<" say# ]" << buffer <<std::endl;sendto(_sockfd,result.c_str(),result.size(),0,(struct sockaddr*)&peer,len);}}}~UdpServer(){}private://std::string _ip;uint16_t _port;int _sockfd;func_t _func;
};

 Dict.hpp

#include<unordered_map>
#include<fstream>
#include"UdpServer.hpp"const static std::string defaultdict = "./dictionary.txt";
const static std::string sep = ":";class Dict
{
public:Dict(const std::string &path = defaultdict):_dict_path(path){}bool LoadWord(){std::ifstream in(_dict_path);if(!in.is_open()){LOG(LogLevel::FATAL) << "open file error...";return false;}std::string line;while(std::getline(in,line))//注意是从文件流里去读取,一次读一行{auto pos = line.find(sep);if(pos == std::string::npos){  LOG(LogLevel::FATAL) << "解析文件失败";continue;}std::string english = line.substr(0,pos);std::string chinese = line.substr(pos + sep.size());if(english.empty()||chinese.empty()){LOG(LogLevel::FATAL) << "没有有效内容";continue;//返回继续进行解析文件}_dict[english] = chinese;LOG(LogLevel::DEBUG) << "加载" << line << "成功";}in.close();return true;}std::string Transact(std::string word,InetAddr &client){auto pos = _dict.find(word);if(pos == _dict.end()){LOG(LogLevel::DEBUG) << "进入到了翻译模块, [" << client.GetIp() << " : " << client.GetPort() << "]# " << word << "->None";return "None";}LOG(LogLevel::DEBUG) << "进入到了翻译模块, [" << client.GetIp() << " : " << client.GetPort() << "]# " << word << "->" << pos->second;return pos->second;}~Dict(){}
private:std::string _dict_path;std::unordered_map<std::string,std::string> _dict;
};

UdpServer.cc

#include"UdpServer.hpp"
#include"Dict.hpp"int main(int argc,char* argv[])
{if (argc != 2){std::cerr << "Usage: " << argv[0] << " port" << std::endl;return 1;}uint16_t port = std::stoi(argv[1]);Enable_Console_Log_Strtegy();Dict dict;dict.LoadWord();std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>([&dict](const std::string& word,InetAddr& cli)->std::string{return dict.Transact(word,cli);},port);usvr->Init();usvr->Start();return 0;
}

dictionary.txt: 

实现效果: 

 

2.3多线程下简单聊天室

Route.hpp

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include "InetAddr.hpp"
#include "Log.hpp"using namespace LogModule;class Route
{bool IsExist(InetAddr& client){for (auto &user : _online_user){if (user == client){return true;}}return false;}void AddUser(InetAddr &client){LOG(LogLevel::INFO) << "新增一个在线用户: " << client.StringAddr();_online_user.push_back(client);}void DeleteUser(InetAddr &peer){for (auto iter = _online_user.begin(); iter != _online_user.end(); iter++){if (*iter == peer){_online_user.erase(iter);LOG(LogLevel::INFO) << "删除一个在线用户:" << peer.StringAddr() << "成功";break;}}}public:Route(){}void MessageRoute(int sockfd, std::string &message, InetAddr &peer){if(!IsExist(peer)){AddUser(peer);}std::string send_message = peer.StringAddr() + "# " + message; // 127.0.0.1:8080# 你好for(auto &user : _online_user){sendto(sockfd,send_message.c_str(),send_message.size(),0,user.GetAddr(),sizeof(*user.GetAddr()));}// 这个用户一定已经在线了if (message == "QUIT"){LOG(LogLevel::INFO) << "删除一个在线用户: " << peer.StringAddr();DeleteUser(peer);}}~Route(){}private:std::vector<InetAddr> _online_user;
};

UdpServer.cc

#include"UdpServer.hpp"
#include"Route.hpp"
#include"ThreadPool.hpp"using namespace ThreadPoolModule;using task_t = std::function<void()>;int main(int argc,char* argv[])
{if (argc != 2){std::cerr << "Usage: " << argv[0] << " port" << std::endl;return 1;}uint16_t port = std::stoi(argv[1]);Enable_Console_Log_Strtegy();//1.路由服务Route r;//2.线程池auto tp = ThreadPool<task_t>::GetInstance();std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>([&r,&tp](int sockfd,const std::string& message,InetAddr& cli){task_t t = std::bind(&Route::MessageRoute,&r,sockfd,message,cli);tp->Enqueue(t);},port);usvr->Init();usvr->Start();return 0;
}

UdpServer.hpp

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#include <cstdlib>
#include <memory>
#include <functional>
#include "nocopy.hpp"
#include "InetAddr.hpp"
#include "Common.hpp"
#include "Log.hpp"
using namespace LogModule;
const static int defaultport = 8888;
const static int defaultsockfd = -1;
const static int defaultsize = 1024;using func_tt = std::function<void(int sockfd,const std::string&,InetAddr&)>;// 禁止拷贝和赋值
class UdpServer : public NoCopy
{
public:UdpServer(func_tt func,uint16_t port = defaultport) : _port(port), _sockfd(defaultsockfd),_func(func){}void Init(){// 1.创建socket_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "socket error...";exit(SOCKET_ERR);}LOG(LogLevel::DEBUG) << "sockfd success: " << _sockfd;// 2.初始化结构体struct sockaddr_in local;bzero(&local,sizeof(local));//memset(&local,0,sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;//注意这里不要写特定的ip值local.sin_port = htons(_port);// 3.bind,结构体填完,还要设置到内核中int n = ::bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n!=0){LOG(LogLevel::FATAL) << "bind error...";exit(BIND_ERR);}LOG(LogLevel::DEBUG) << "bind success...";}void Start(){while(true){char buffer[defaultsize];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = recvfrom(_sockfd,buffer,sizeof(buffer) - 1,0,(struct sockaddr*)&peer,&len);if(n > 0){InetAddr addr(peer);buffer[n] = 0;_func(_sockfd,buffer,addr);//路由功能,外部传函数}}}~UdpServer(){}private://std::string _ip;uint16_t _port;int _sockfd;func_tt _func;
};

客户端也进行多线程修改

UdpClient.cc

#include <iostream>
#include <string>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "Thread.hpp"int sockfd = 0;
std::string server_ip;
uint16_t server_port = 0;
pthread_t id;using namespace ThreadModule;void Recv()
{while (true){char buffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);int m = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);if (m > 0){buffer[m] = 0;std::cerr << buffer << std::endl; // 2}}
}
void Send()
{struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(server_port);server.sin_addr.s_addr = inet_addr(server_ip.c_str());const std::string online = "inline";sendto(sockfd, online.c_str(), online.size(), 0, (struct sockaddr *)&server, sizeof(server));while (true){std::string input;//std::cout << "Please Enter# "; // 1std::getline(std::cin, input); // 0int n = sendto(sockfd, input.c_str(), input.size(), 0, (struct sockaddr *)&server, sizeof(server));(void)n;if (input == "QUIT"){pthread_cancel(id);break;}}
}// client 我们也要做多线程改造
// ./udpclient server_ip server_port
int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << " server_ip server_port" << std::endl;return 1;}server_ip = argv[1];server_port = std::stoi(argv[2]);// 1. 创建socketsockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){std::cerr << "socket error" << std::endl;return 2;}// 2. 创建线程Thread recver(Recv);Thread sender(Send);recver.Start();sender.Start();recver.Join();sender.Join();// 2. 本地的ip和端口是什么?要不要和上面的“文件”关联呢?// 问题:client要不要bind?需要bind.//       client要不要显式的bind?不要!!首次发送消息,OS会自动给client进行bind,OS知道IP,端口号采用随机端口号的方式//   为什么?一个端口号,只能被一个进程bind,为了避免client端口冲突//   client端的端口号是几,不重要,只要是唯一的就行!// 填写服务器信息return 0;
}

 三.地址转换函数

sockaddr_in 中的成员 struct in_addr sin_addr 表示 32 位 的 IP 地址

但是我们通常用点分十进制的字符串表示 IP 地址,以下函数可以在字符串表示 和 in_addr 表示之间转换;

字符串转 in_addr 的函数:

in_addr 转字符串的函数:

其中 inet_pton 和 inet_ntop 不仅可以转换 IPv4 的 in_addr,还可以转换 IPv6 的 in6_addr,因此函数接口是 void *addrptr。 

inet_ntoa 

inet_ntoa 这个函数返回了一个 char*, 很显然是这个函数自己在内部为我们申请了一块 内存来保存 ip 的结果. 那么是否需要调用者手动释放呢? 

man 手册上说, inet_ntoa 函数, 是把这个返回结果放到了静态存储区. 这个时候不需要 我们手动进行释放. 那么问题来了, 如果我们调用多次这个函数, 会有什么样的效果呢? 参见如下代码:

 

因为 inet_ntoa 把结果放到自己内部的一个静态存储区, 这样第二次调用时的结果会覆 盖掉上一次的结果.

在多线程环境下, 推荐使用 inet_ntop, 这个函数由调用者提供一个缓冲区保存 结果, 可以规避线程安全问题; 

 四.TCP

多线程版本,多进程版本,线程池版本

TcpServer.hpp

#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#include <cstdlib>
#include <memory>
#include <functional>
#include <sys/types.h>
#include <sys/wait.h>
#include "nocopy.hpp"
#include "InetAddr.hpp"
#include "Common.hpp"
#include "Log.hpp"
#include "Command.hpp"
#include "ThreadPool.hpp"
using namespace LogModule;
using namespace ThreadPoolModule;using func_tt = std::function<void()>;class TcpServer : public NoCopy
{
public:TcpServer(uint16_t port) : _port(port), _isruning(false){}void Init(){// 1.创建listensocket_listensock = ::socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){LOG(LogLevel::FATAL) << "socket error...";exit(SOCKET_ERR);}LOG(LogLevel::DEBUG) << "socket success: " << _listensock;// 2.bind// struct sockaddr_in server;// bzero(&server, sizeof(server));// server.sin_addr.s_addr = INADDR_ANY;// server.sin_family = AF_INET;// server.sin_port = htons(_port);InetAddr server(_port);int n = ::bind(_listensock, CONV(&server.NetAddr()), server.GetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind error...";exit(BIND_ERR);}LOG(LogLevel::DEBUG) << "bind success";// 3.设置监听状态,listenif (::listen(_listensock, 5) < 0){LOG(LogLevel::FATAL) << "listen error...";exit(LISTEN_ERR);}LOG(LogLevel::DEBUG) << "listen success";}void Start(){_isruning = true;while (_isruning){// 4.获取链接,acceptstruct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = ::accept(_listensock, CONV(&peer), &len);if (sockfd < 0){LOG(LogLevel::FATAL) << "accept error...";continue;}InetAddr client(peer);LOG(LogLevel::DEBUG) << "accept success ,name: " << client.StringAddr();// 5.提供服务// Service(sockfd);// close(sockfd);// ProcessConnection(sockfd,peer);//ThreadConnection(sockfd, peer);ThreadPoolConnection(sockfd,client);}}void Service(int sockfd,InetAddr& peer){char buffer[1024];while (true){ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;std::cout << "client say# " << buffer << std::endl;std::string echo_string = "server echo# ";echo_string += buffer;write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0) // read 如果返回值是 0,表示读到了文件结尾(对端关闭了连接!){LOG(LogLevel::FATAL) << peer.StringAddr() << "client quit";break;}else{LOG(LogLevel::FATAL)<< peer.StringAddr() << "read error...";break;}}}// void Service(int sockfd,InetAddr& peer)// {//     Command com;//     char buffer[1024];//     while(true)//     {//         // 1. 先读取数据//         // a. n>0: 读取成功//         // b. n<0: 读取失败//         // c. n==0: 对端把链接关闭了,读到了文件的结尾 --- pipe//         ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);//         if (n > 0)//         {//             // buffer是一个英文单词 or 是一个命令字符串//             buffer[n] = 0; // 设置为C风格字符串, n<= sizeof(buffer)-1//             std::string echo_string = com.Execute(buffer, peer);//             write(sockfd, echo_string.c_str(), echo_string.size());//         }//         else if (n == 0)//         {//             LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";//             close(sockfd);//             break;//         }//         else//         {//             LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";//             close(sockfd);//             break;//         }//     }// }class ThreadData{public:ThreadData(int sockfd, InetAddr &ie, TcpServer *t) : _sockfd(sockfd), _client(ie), _tsv(t){}~ThreadData(){}int _sockfd;InetAddr _client;TcpServer *_tsv;};//线程池版本void ThreadPoolConnection(int sockfd,InetAddr& peer){ThreadPool<func_tt>::GetInstance()->Enqueue([this,sockfd,&peer](){this->Service(sockfd,peer);});}// 多线程服务static void *Routine(void *args){pthread_detach(pthread_self());ThreadData *tdv = static_cast<ThreadData *>(args);tdv->_tsv->Service(tdv->_sockfd,tdv->_client);close(tdv->_sockfd);delete tdv;return nullptr;}void ThreadConnection(int sockfd, struct sockaddr_in &peer){InetAddr client(peer);pthread_t tid;// std::shared_ptr<ThreadData> tdsv = std::make_shared<ThreadData>(sockfd,client,this);ThreadData *tdv = new ThreadData(sockfd, client, this);pthread_create(&tid, nullptr, Routine, (void *)tdv);}// 多进程服务void ProcessConnection(int sockfd, struct sockaddr_in &peer){pid_t id = fork();if (id < 0){close(sockfd);return;}else if (id == 0){// 子进程再去创建子进程,孙子进程// 子进程,子进程除了看到sockfd,能看到listensockfd吗??//  我们不想让子进程访问listensock!close(_listensock);if (fork() > 0){exit(0); // 大于0,是子进程}// 到这里是孙子进程,是孤儿进程,系统去回收InetAddr client(peer);LOG(LogLevel::DEBUG) << "process connection: " << client.StringAddr();Service(sockfd,client);close(sockfd);exit(0);}else{// 父进程去关闭创建的子进程close(sockfd);// 这里要等待子进程pid_t rid = waitpid(id, nullptr, 0);(void)rid;}}~TcpServer(){}private:uint16_t _port;int _listensock;bool _isruning;
};

TcpClient.cc

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#include <cstdlib>
#include <memory>
#include <functional>
#include "nocopy.hpp"
#include "InetAddr.hpp"
#include "Common.hpp"
#include "Log.hpp"using namespace LogModule;
void Usage(const std::string &process)
{std::cout << "Usage: " << process << " server_ip server_port" << std::endl;
}
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);return 1;}std::string server_ip = argv[1];uint16_t server_port = std::stoi(argv[2]);// 1.建立套接字int sockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){LOG(LogLevel::FATAL) << "socket error...";exit(SOCKET_ERR);}LOG(LogLevel::DEBUG) << "socket success: " << sockfd;// 2.不需要用户bind// 3.连接InetAddr local(server_ip, server_port);int n = ::connect(sockfd, local.GetAddr(), local.GetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "connect error...";exit(CONNECT_ERR);}LOG(LogLevel::DEBUG) << "connect success: " << sockfd;// 4.链接成功,通信while (true){std::string client_buffer;std::cout << "Please Enter# ";std::getline(std::cin, client_buffer);ssize_t n = write(sockfd, client_buffer.c_str(), client_buffer.size());char server_buffer[1024];ssize_t size = read(sockfd, server_buffer, sizeof(server_buffer)-1);if(size > 0){server_buffer[size] = 0;std::cout << server_buffer << std::endl;}}close(sockfd);return 0;
}

实现一个简单的远程命令

Command.hpp

#pragma once
#include <iostream>
#include <string>
#include <set>
#include <unistd.h>
#include <fstream>
#include "InetAddr.hpp"
#include "Log.hpp"using namespace LogModule;class Command
{
public:Command(){// 严格匹配_WhiteListCommands.insert("ls");_WhiteListCommands.insert("pwd");_WhiteListCommands.insert("ls -l");_WhiteListCommands.insert("touch haha.txt");_WhiteListCommands.insert("who");_WhiteListCommands.insert("whoami");}bool IsSafeCommand(const std::string &cmd){auto iter = _WhiteListCommands.find(cmd);return iter != _WhiteListCommands.end();}std::string Execute(const std::string &cmd, InetAddr &addr){// 1. 属于白名单命令if(!IsSafeCommand(cmd)){return std::string("坏人");}std::string who = addr.StringAddr();// 2. 执行命令// std::ifstream in;// in.open(cmd.c_str());FILE *fp = popen(cmd.c_str(), "r");if(nullptr == fp){return std::string("你要执行的命令不存在: ") + cmd;}std::string res;char line[1024];while(fgets(line, sizeof(line), fp)){res += line;}pclose(fp);std::string result = who + "execute done, result is: \n" + res;LOG(LogLevel::DEBUG) << result;return result;}~Command(){}
private:std::set<std::string> _WhiteListCommands;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/89358.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/89358.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/89358.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器人轨迹跟踪控制与动力学模型详解

1. 机器人控制的本质&#xff1a;通过关节扭矩执行轨迹 机器人控制的核心目标是让机器人关节精确跟踪期望轨迹 ( q d , q ˙ d , q d ) (q_d, \dot{q}_d, \ddot{q}_d) (qd​,q˙​d​,q​d​)。为此&#xff0c;控制器需根据当前状态 ( q , q ˙ ) (q, \dot{q}) (q,q˙​)计…

智能办公与科研革命:ChatGPT+DeepSeek大模型在论文撰写、数据分析与AI建模中的实践指南

随着人工智能技术的快速发展&#xff0c;大语言模型如ChatGPT和DeepSeek在科研领域的应用正在为科研人员提供强大的支持。这些模型通过深度学习和大规模语料库训练&#xff0c;能够帮助科研人员高效地筛选文献、生成论文内容、进行数据分析和优化机器学习模型。 ChatGPT和Deep…

运营商场景下的实时脱敏方案:PB 级日志流的分布式处理架构

在数字化浪潮中&#xff0c;运营商积累了海量数据&#xff0c;涵盖用户信息、通信记录、业务运营数据等。这些数据不仅是运营商业务运营的关键资产&#xff0c;也是创新服务、精准营销的核心驱动力。然而&#xff0c;随着数据量呈指数级增长&#xff0c;运营商每日需处理 PB 级…

docker+n8n的工作流中无法使用本地ollama服务的问题

使用docker创建n8n服务后&#xff0c;工作流中不想用大模型付费API测试&#xff0c;想用本地大模型来跑&#xff0c;刚好电脑上装了ollama&#xff0c;就试了下添加ollama节点来替代大模型付费API&#xff0c;结果就遇到了以下问题 ollama正常运行中 但是工作流会卡在这&…

通过交互式可视化探索波动方程-AI云计算数值分析和代码验证

波动方程是一个基本的数学模型&#xff0c;它描述了各种类型的波&#xff08;包括机械波、声波、电磁波和流体波&#xff09;如何通过不同的介质传播&#xff0c;这使得它对于物理学、工程学和其他科学学科中声学、光学、医学成像和电信等领域的预测和设计都至关重要。 波动方程…

10授权

目录 本节大纲 一、权限管理 1. 认证 2. 授权 二、授权核心概念 三、权限管理策略 1. 基于 URL 权限管理 权限表达式 2. 基于 方法 权限管理 EnableGlobalMethodSecurity 四、基本用法 五、原理分析 六、实战 1. 简介 2. 库表设计 3. 创建 springboot 应用 本节…

线性规划模型

线性规划算是数学建模中最基础的模型了&#xff0c;其典型特征就是线性和有限资源&#xff0c;即在一组线性约束条件下&#xff0c;求解一个线性目标函数的最大值或最小值问题&#xff1a; 其中x 是决策变量向量&#xff0c;c 是目标函数系数向量&#xff0c;a 和 b 分别是约束…

华为云Flexus+DeepSeek征文|体验华为云ModelArts快速搭建Dify-LLM应用开发平台并创建知识库大模型工作流查询数据库数据

华为云FlexusDeepSeek征文&#xff5c;体验华为云ModelArts快速搭建Dify-LLM应用开发平台并创建知识库大模型工作流查询数据库数据 什么是华为云ModelArts 华为云ModelArts ModelArts是华为云提供的全流程AI开发平台&#xff0c;覆盖从数据准备到模型部署的全生命周期管理&am…

WPF中Style和Template异同

在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;Style和Template是两个核心概念&#xff0c;用于控制UI元素的外观和行为&#xff0c;但它们的职责和使用场景有明显区别。以下是详细分析&#xff1a; 一、基本概念 1. Style&#xff08;样式&am…

针对 DVWA 中 Command Injection 模块的乱码问题及解决办法

目录 根本原因 解决办法 优化说明 适用范围 系统兼容性 在 DVWA 的 Command Injection 模块中执行系统命令时&#xff0c;返回结果出现乱码&#xff08;如图1所示&#xff09;。 根本原因 DVWA 默认使用 UTF-8 编码&#xff0c;而部分系统命令&#xff08;如 Windows 的…

Linux获取ImageNet数据集方法及小规模imagenet

一、数据集下载 ImageNet官方链接&#xff1a;ImageNet Linux命令直接下载&#xff1a; 训练集 wget https://image-net.org/data/ILSVRC/2012/ILSVRC2012_img_train.tar --no-check-certificate验证集 wget https://image-net.org/data/ILSVRC/2012/ILSVRC2012_img_val.t…

JAVA八股文:异常有哪些种类,可以举几个例子吗?Throwable类有哪些常见方法?

Throwable、Error 与 Exception 所有的异常类型都继承自 java.lang.Throwable。 其中 Error&#xff08;比如 OutOfMemoryError、StackOverflowError、类加载失败等&#xff09;表示 JVM 自身或运行环境的问题&#xff0c;不应该也通常无法由应用程序去捕获或恢复&#xff0c;…

.NetCore+Vue快速生产框架开发详细方案

文章目录 1. 项目概述 1.1 项目背景1.2 项目目标1.3 核心功能 2. 技术栈选择 2.1 后端技术栈2.2 前端技术栈2.3 开发工具 3. 系统架构设计 3.1 整体架构3.2 后端架构设计3.3 前端架构设计3.4 微服务考虑 4. 后端.NET核心设计 4.1 项目结构4.2 核心模块设计4.2.1 用户模块4.2.2 …

WPF学习笔记(18)触发器Trigger

触发器 1. 概述2. 详解2.1. Trigger 用法2.2. MultiTrigger 用法2.3. DataTrigger 用法2.4. EventTrigger 用法 总结 1. 概述 官方文档&#xff1a;https://learn.microsoft.com/zh-cn/dotnet/api/system.windows.trigger?viewnetframework-4.8 2. 详解 在Style中可以指定触…

记本好书:矩阵力量:线性代数全彩图解+微课+Python编程

书名&#xff1a;矩阵力量&#xff1a;线性代数全彩图解微课Python编程 作者&#xff1a;姜伟生 出版社&#xff1a;清华大学出版社 出版时间&#xff1a;2023-06-01 ISBN&#xff1a;9787302632511 品牌方&#xff1a;清华大学出版社有限公司 发现一本好书&#xff0c;但是一…

‌Webpack打包流程

Webpack打包流程的核心步骤包括初始化配置、解析入口文件、构建依赖图、模块转换、资源优化和输出文件‌。该流程通过递归分析模块依赖关系&#xff0c;结合加载器和插件处理各类资源&#xff0c;最终生成优化后的静态文件。 ‌核心流程概述‌ ‌初始化配置‌&#xff1a;读取…

入门pytorch-联邦学习

本文联邦学习的代码引用于https://github.com/shaoxiongji/federated-learning 本篇文章相当于带大家读一遍联邦学习的代码&#xff0c;同时加深了大家对联邦学习和Pytorch框架的理解。 这里想简单介绍一下联邦学习。 联邦学习说白了&#xff0c;就是假如有 N N N个数据拥有…

半导体和PN结

1. 什么是半导体&#xff1f; 导体&#xff0c;电阻率小&#xff0c;即电流容易通过的材料&#xff1b;Cu 绝缘体&#xff0c;导电性低&#xff0c;即电流不易通过的材料&#xff1b;塑料 半导体&#xff0c;常温下导电性能介于导体和绝缘体之间&#xff0c;是一种导电可控的…

如何分析大语言模型(LLM)的内部表征来评估文本的“诚实性”

如何分析大语言模型(LLM)的内部表征来评估文本的“诚实性” 基于这样一个假设:模型在生成诚实和不诚实回答时,其内部状态会存在系统性差异 LAT :线性人工断层扫描 我们通过一个生活化的例子来理解如何通过分析大语言模型的内部表征评估文本的“诚实性”。 场景类比:判…

【算法】动态规划 矩阵 :62. 不同路径

62. 不同路径 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&…