本地套接字的实现逻辑非常简单,为了提高复用性,主要是对这个过程进行封装,抽象成基类这样后续无论使用本地套接字还是网络套接字都可以(不动业务层)
如图是一个基于tcp的本地套接字
#pragma once
#include<unistd.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<netinet/in.h> //网络地址处理
#include<arpa/inet.h>
#include<string>
#include <fcntl.h>
//缓冲区
class Buffer :public std::string {
public:
Buffer() :std::string() {}
Buffer(size_t size) :std::string() { resize(size); }
operator char* () { return (char*)c_str(); } //隐式运算符重载
operator char* () const { return (char*)c_str(); }
operator const char* () const { return c_str(); }
};
enum SockAttr
{
SOCK_ISSERVER = 1,//是否为服务器
SOCK_ISNONBLOCK = 2,//是否阻塞
SOCK_ISUDP = 4, //是否是UDP
};
//初始化参数
class CSocketParam {
public:
CSocketParam(){
//清空
bzero(&addr_in, sizeof(addr_in));
bzero(&addr_in, sizeof(addr_in));
port = -1;
}
CSocketParam(Buffer&ip,short port,int attr) {
this->ip = ip;
this->port = port;
addr_in.sin_family = AF_INET;
addr_in.sin_port = port;
addr_in.sin_addr.s_addr = inet_addr(ip);
this->attr = attr;
}
CSocketParam(const Buffer&path,int attr) { //本地套接字封装
ip = path;
addr_un.sun_family = AF_UNIX;
strcpy(addr_un.sun_path, path);
this->attr = attr;
}
~CSocketParam(){}
CSocketParam(const CSocketParam¶m) {
ip = param.ip;
port = param.port;
attr = param.attr;
memcpy(&addr_in, ¶m.addr_in, sizeof(addr_in));
memcpy(&addr_un, ¶m.addr_un, sizeof(addr_un));
}
public:
CSocketParam& operator=(const CSocketParam& param) {
if (this != ¶m) {
ip = param.ip;
port = param.port;
attr = param.attr;
memcpy(&addr_in, ¶m.addr_in, sizeof(addr_in));
memcpy(&addr_un, ¶m.addr_un, sizeof(addr_un));
}
return *this;
}
sockaddr* addrin() { return (sockaddr*)&addr_in; } //最后套接字要的是这个结构的地址指针,提前转化一下
sockaddr* addrun() { return (sockaddr*)&addr_un; }
public:
sockaddr_in addr_in; //网络地址
sockaddr_un addr_un; //本地地址
//IP
Buffer ip;
short port;
//是否阻塞
int attr;//bit 0
};
class CSocketBase {
public:
CSocketBase() {
m_socket = -1;
m_status = 0;
}
virtual ~CSocketBase() {
Close();
} //虚析构顺着父类把子类都消除
public:
virtual int Init(const CSocketParam¶m) = 0; //必须实现才能声明对象
//服务器套接字创建 bind,listen,客户端只创建套接字
virtual int Link(CSocketBase** pClient=NULL) = 0;
//服务器accept 客户端connect udp忽略
virtual int Send(const Buffer&data) = 0;
//发送数据
virtual int Recv(Buffer&data) = 0;
//收数据
virtual int Close() {
m_status = 3;
if (m_socket != -1) {
int fd = m_socket;
m_socket = -1;
close(fd);
}
};
protected:
int m_socket;//套接字描述符,默认-1;
int m_status;//描述状态 0:未初始化 1:初始化了 2:连接了 3:关闭
CSocketParam m_param;
};
class CLocalSocket :public CSocketBase {\
public:
CLocalSocket() :CSocketBase(){
}
CLocalSocket(int sock) :CSocketBase() {
m_socket = sock;
}
virtual ~CLocalSocket() {
Close();
}
public:
virtual int Init(const CSocketParam& param) {
if (m_status != 0)return -1;
m_param = param;
int type = (m_param.attr & SOCK_ISUDP) ? SOCK_DGRAM : SOCK_STREAM;
if (m_socket == -1)m_socket = socket(PF_LOCAL,type,0);//创建本地套接字
if (m_socket == -1)return -2;
int ret = 0;
//下面的逻辑是,服务器端需要bind listen,客户端不需要初始化
if (m_param.attr & SOCK_ISSERVER) {
ret = bind(m_socket, m_param.addrun(), sizeof(sockaddr_un));
if (ret == -1)return -3;
ret = listen(m_socket, 32);//最多32个请求等着
if (ret == -1)return -4;
}
if (m_param.attr & SOCK_ISNONBLOCK) { //是否阻塞
int control = fcntl(m_socket, F_GETFL);
if (control == -1)return -5;
ret = fcntl(m_socket, F_SETFL, control | O_NONBLOCK);
if (ret == -1)return -6;
}
m_status = 1;
return 0;
} //必须实现才能声明对象
//服务器套接字创建 bind,listen,客户端只创建套接字
virtual int Link(CSocketBase** pClient = NULL) {
if (m_status <= 0 || m_socket == -1)return -1;
int ret = 0;
//server端accept client端connect
if (m_param.attr & SOCK_ISSERVER) {
if (pClient == NULL)return -2;
CSocketParam param;
socklen_t len = sizeof(sockaddr_un);
int fd = accept(m_socket,param.addrun(),&len); //核心accept
if (fd == -1)return -3;
*pClient = new CLocalSocket(fd);
if (pClient == NULL)return -4;
ret = (*pClient)->Init(param);
if (ret != 0) {
delete(*pClient);
*pClient = NULL;
return -5;
}
}
//else这里初始化客户端
else {
ret = connect(m_socket, m_param.addrun(), sizeof(sockaddr_un));
if (ret != 0)return -6;
}
m_status = 2;
return 0;
}
//服务器accept 客户端connect udp忽略
virtual int Send(const Buffer& data) {
if (m_status < 2 || (m_socket == -1))return -1;
ssize_t index = 0;
while (index < ((ssize_t )data.size())) {
ssize_t len = write(m_socket,(char*)data+index,data.size()-index);
if (len == 0)return -2;//表示没收到
if (len < 0)return -3; //表示错误
}
return 0;
}
//发送数据
virtual int Recv(Buffer& data) {
if (m_status < 2 || (m_socket == -1))return -1;
ssize_t len = read(m_socket, data, data.size());
if (len > 0) {
data.resize(len);
return (int)len;
}
if (len < 0) {
if (errno == EINTR || errno == EAGAIN) {
data.clear();
return 0;
}
return -2;
}
return -3;
}
//收数据
virtual int Close() {
return CSocketBase::Close();
}
};
文章介绍了本地套接字的实现逻辑,包括CSocketParam和CLocalSocket类的使用,展示了如何封装和初始化本地套接字,以及服务器端和客户端的链接操作,特别提到了非阻塞模式的应用。
4468

被折叠的 条评论
为什么被折叠?



