c++ Linux本地套接字实践

文章介绍了本地套接字的实现逻辑,包括CSocketParam和CLocalSocket类的使用,展示了如何封装和初始化本地套接字,以及服务器端和客户端的链接操作,特别提到了非阻塞模式的应用。

本地套接字的实现逻辑非常简单,为了提高复用性,主要是对这个过程进行封装,抽象成基类这样后续无论使用本地套接字还是网络套接字都可以(不动业务层)

如图是一个基于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&param) {
		ip = param.ip;
		port = param.port;
		attr = param.attr;
		memcpy(&addr_in, &param.addr_in, sizeof(addr_in));
		memcpy(&addr_un, &param.addr_un, sizeof(addr_un));
	}
public:
	CSocketParam& operator=(const CSocketParam& param) {
		if (this != &param) {
			ip = param.ip;
			port = param.port;
			attr = param.attr;
			memcpy(&addr_in, &param.addr_in, sizeof(addr_in));
			memcpy(&addr_un, &param.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&param) = 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();
	}
};

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值