C/C++封装socket通信类

2023-05-16

C/C++封装socket通信类

  • 一、读取、写入数据
    • 1、recvn函数
    • 2、sendn函数
    • 3、TcpRecv函数
    • 4、TcpSend函数
  • 二、C的封装方法
    • 1、客户端
    • 2、服务端
  • 三、C++的封装方法
    • 1、客户端
    • 2、服务端

不管是socket通信程序的客户端还是服务端,准备工作的代码又长又难看占地方,影响了主程序的结构,必须分离出来。

一、读取、写入数据

1、recvn函数

//从已经准备好的socket中读取数据。
//sockfd:已经准备好的socket连接。
//buffer:接收数据缓冲区的地址。
//n:本次接收数据的字节数。
//返回值:成功接收到n字节的数据后返回true,socket连接不可用返回false。
bool recvn(const int sockfd, char *buffer, const size_t n) {
	int remain, len, idx;
	remain = n;
	idx = 0;
	while (remain > 0) {
		if ((len = recv(sockfd, buffer + idx, remain,0)) <= 0)
			return false;
		idx += len;
		remain -= len;
	}
	return true;
}

2、sendn函数

//向已经准备好的socket中写入数据。
//sockfd:已经准备好的socket连接。
//buffer:待发送数据缓冲区的地址。
//n:待发送数据的字节数,缺省值是0时默认为buffer的大小。
//返回值:成功发送完n字节的数据后返回true,socket连接不可用返回false。
bool sendn(const int sockfd, const char *buffer, const size_t n = 0) {
	int remain, len, idx;
	remain = (n == 0) ? strlen(buffer) : n;
	idx = 0;
	while (remain > 0) {
		if ((len = send(sockfd,buffer + idx, remain,0)) <= 0)
			return false;
		idx += len;
		remain -= len;
	}
	return true;
}

3、TcpRecv函数

//接收socket的对端发送过来的数据。
//sockfd:可用的socket连接。
//buffer:接收数据缓冲区的地址。
//ibuflen:本次成功接收数据的字节数。解决TCP网络传输「粘包」
//itimeout:接收等待超时的时间,单位:秒,缺省值是0-无限等待。
//返回值:true-成功;false-失败,失败有两种情况:1)等待超时;2)socket连接已不可用。
bool TcpRecv(const int sockfd,char *buffer,int *ibuflen,const int itimeout = 0) {
	if (sockfd == -1) return false;
	if (itimeout > 0) {  //延时操作 
		fd_set tmpfd;

		FD_ZERO(&tmpfd);
		FD_SET(sockfd,&tmpfd);

		struct timeval timeout;
		timeout.tv_sec = itimeout;
		timeout.tv_usec = 0;

		//如果在itimeout时间内没有可用资源的文件描述符的话就退出 
		if (select(sockfd+1,&tmpfd,0,0,&timeout) <= 0) return false;
	}

	//数据包 = 数据大小 + 数据
	(*ibuflen) = 0;

	if(!recvn(sockfd,(char*)ibuflen,4)) return false;

	(*ibuflen)=ntohl(*ibuflen);  //把网络字节序转换为主机字节序。
	
	return recvn(sockfd, buffer, *ibuflen);
}

4、TcpSend函数

//向socket的对端发送数据。
//sockfd:可用的socket连接。
//buffer:待发送数据缓冲区的地址。
//ibuflen:待发送数据的字节数,如果发送的是ascii字符串,ibuflen取0,如果是二进制流数据,ibuflen为二进制数据块的大小。
//itimeout:接收等待超时的时间,单位:秒,值是0-无限等待。
//返回值:true-成功;false-失败,如果失败,表示socket连接已不可用。
bool TcpSend(const int sockfd,const char *buffer,const int ibuflen = 0,const int itimeout = 5) {
	if (sockfd == -1 ) return false;
	if (itimeout > 0) {  //延时操作 
		fd_set tmpfd;

		FD_ZERO(&tmpfd);
		FD_SET(sockfd,&tmpfd);

		struct timeval timeout;
		timeout.tv_sec = itimeout;
		timeout.tv_usec = 0;

		//如果在itimeout时间内没有可用资源的文件描述符的话就退出 
		if (select(sockfd+1,&tmpfd,0,0,&timeout) <= 0) return false;
	}

	//如果长度为0,就采用字符串的长度
	int ilen = (ibuflen == 0) ? strlen(buffer) : ibuflen;

	int ilenn=htonl(ilen);  //转换为网络字节序

	//数据包 = 数据大小 + 数据
	char strTBuffer[ilen+4];  //前面保存长度
	memset (strTBuffer,0,sizeof(strTBuffer));
	memcpy (strTBuffer, &ilenn, 4);
	memcpy (strTBuffer+4, buffer, ilen);

	return sendn(sockfd,strTBuffer, ilen+4);
}

二、C的封装方法

C语言只能把程序代码封装成函数。

1、客户端

把客户端连接服务端的socket操作封装到connecttoserver函数中,主程序的代码更简洁。

// TCP客户端连服务端的函数,serverip-服务端ip,port通信端口
// 返回值:成功返回已连接socket,失败返回-1。
int connecttoserver(const char *serverip,const int port) {
	int sockfd = socket(AF_INET,SOCK_STREAM,0); // 创建客户端的socket

	struct hostent* h; // ip地址信息的数据结构
	if ( (h = gethostbyname(serverip)) == 0 ) {
		perror("gethostbyname");
		close(sockfd);
		return -1;
	}

	// 把服务器的地址和端口转换为数据结构
	struct sockaddr_in servaddr;
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(port);
	memcpy(&servaddr.sin_addr,h->h_addr,h->h_length);

	// 向服务器发起连接请求
	if (connect(sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr)) != 0) {
		perror("connect");
		close(sockfd);
		return -1;
	}

	return sockfd;
}

2、服务端

把服务端初始化socket操作封装到initserver函数中,主程序的代码更简洁。

// 初始化服务端的socket,port为通信端口
// 返回值:成功返回初始化的socket,失败返回-1。
int initserver(int port) {
	int listenfd = socket(AF_INET,SOCK_STREAM,0);  // 创建服务端的socket

	//设置SO_REUSEADDR选项,解决关闭程序端口还在占用,再运行socket bind不成功的问题
	int opt = 1;
	unsigned int len = sizeof(opt);
	setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,len);

	// 把服务端用于通信的地址和端口绑定到socket上
	struct sockaddr_in servaddr;    // 服务端地址信息的数据结构
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family = AF_INET;  // 协议族,在socket编程中只能是AF_INET
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  // 本主机的任意ip地址
	servaddr.sin_port = htons(port);  // 绑定通信端口
	if (bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 ) {
		perror("bind");
		close(listenfd);
		return -1;
	}

	// 把socket设置为监听模式
	if (listen(listenfd,5) != 0 ) {
		perror("listen");
		close(listenfd);
		return -1;
	}

	return listenfd;
}

三、C++的封装方法

C++语言可以封装数据和函数,采用的是类。

1、客户端

// socket通信的客户端类
class CTcpClient {
	public:
		int  m_sockfd;    // 客户端的socket.
		char m_ip[21];    // 服务端的ip地址。
		int  m_port;      // 与服务端通信的端口。
		bool m_btimeout;  // 调用Recv和Send方法时,失败的原因是否是超时:true-超时,false-未超时。
		int  m_buflen;    // 调用Recv方法后,接收到的报文的大小,单位:字节。

		CTcpClient();  // 构造函数。

		// 向服务端发起连接请求。
		// ip:服务端的ip地址。
		// port:服务端监听的端口。
		// 返回值:true-成功;false-失败。
		bool ConnectToServer(const char *ip,const int port);

		// 接收服务端发送过来的数据。
		// buffer:接收数据缓冲区的地址,数据的长度存放在m_buflen成员变量中。
		// itimeout:等待数据的超时时间,单位:秒,缺省值是0-无限等待。
		// 返回值:true-成功;false-失败,失败有两种情况:1)等待超时,成员变量m_btimeout的值被设置为true;2)socket连接已不可用。
		bool Recv(char *buffer,const int itimeout=0);

		// 向服务端发送数据。
		// buffer:待发送数据缓冲区的地址。
		// ibuflen:待发送数据的大小,单位:字节,缺省值为0,如果发送的是ascii字符串,ibuflen取0,如果是二进制流数据,ibuflen为二进制数据块的大小。
		//itimeout:接收等待超时的时间,单位:秒,值是0-无限等待。
		// 返回值:true-成功;false-失败,如果失败,表示socket连接已不可用。
		bool Send(const char *buffer,const int ibuflen=0,const int itimeout=5);

		// 断开与服务端的连接
		void Close();

		~CTcpClient();  // 析构函数自动关闭socket,释放资源。
};

CTcpClient::CTcpClient() {
	m_sockfd=-1;
	memset(m_ip,0,sizeof(m_ip));
	m_port=0;
	m_btimeout=false;
}

bool CTcpClient::ConnectToServer(const char *ip,const int port) {
	if (m_sockfd != -1) {
		close(m_sockfd);
		m_sockfd = -1;
	}

	strcpy(m_ip,ip);
	m_port=port;

	struct hostent* h;
	struct sockaddr_in servaddr;

	if ( (m_sockfd = socket(AF_INET,SOCK_STREAM,0) ) < 0) return false;

	if ( !(h = gethostbyname(m_ip)) ) {
		close(m_sockfd);
		m_sockfd = -1;
		return false;
	}

	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(m_port);  // 指定服务端的通讯端口
	memcpy(&servaddr.sin_addr,h->h_addr,h->h_length);

	if (connect(m_sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr)) != 0) {
		close(m_sockfd);
		m_sockfd = -1;
		return false;
	}

	return true;
}

bool CTcpClient::Recv(char *buffer,const int itimeout) {
	if (m_sockfd == -1) return false;

	if (itimeout>0) {
		fd_set tmpfd;

		FD_ZERO(&tmpfd);
		FD_SET(m_sockfd,&tmpfd);

		struct timeval timeout;
		timeout.tv_sec = itimeout;
		timeout.tv_usec = 0;

		m_btimeout = false;

		int i;
		if ( (i = select(m_sockfd+1,&tmpfd,0,0,&timeout)) <= 0 ) {
			if (i==0) m_btimeout = true;
			return false;
		}
	}

	m_buflen = 0;
	return (TcpRecv(m_sockfd,buffer,&m_buflen));
}

bool CTcpClient::Send(const char *buffer,const int ibuflen,const int itimeout) {
	if (m_sockfd == -1) return false;

	if (itimeout>0) {
		fd_set tmpfd;

		FD_ZERO(&tmpfd);
		FD_SET(m_sockfd,&tmpfd);

		struct timeval timeout;
		timeout.tv_sec = itimeout;
		timeout.tv_usec = 0;

		m_btimeout = false;

		int i;
		if ( (i = select(m_sockfd+1,&tmpfd,0,0,&timeout)) <= 0 ) {
			if (i==0) m_btimeout = true;
			return false;
		}
	}

	int ilen=ibuflen;

	if (ibuflen==0) ilen=strlen(buffer);

	return(TcpSend(m_sockfd,buffer,ilen));
}

void CTcpClient::Close() {
	if (m_sockfd > 0) close(m_sockfd);

	m_sockfd=-1;
	memset(m_ip,0,sizeof(m_ip));
	m_port=0;
	m_btimeout=false;
}

CTcpClient::~CTcpClient() {
	Close();
}

2、服务端

// socket通信的服务端类
class CTcpServer {
	private:
		int m_socklen;                    // 结构体struct sockaddr_in的大小。
		struct sockaddr_in m_clientaddr;  // 客户端的地址信息。
		struct sockaddr_in m_servaddr;    // 服务端的地址信息。
	public:
		int  m_listenfd;   // 服务端用于监听的socket。
		int  m_connfd;     // 客户端连接上来的socket。
		bool m_btimeout;   // 调用Recv和Send方法时,失败的原因是否是超时:true-超时,false-未超时。
		int  m_buflen;     // 调用Recv方法后,接收到的报文的大小,单位:字节。

		CTcpServer();  // 构造函数。

		// 服务端初始化。
		// port:指定服务端用于监听的端口。
		// 返回值:true-成功;false-失败,一般情况下,只要port设置正确,没有被占用,初始化都会成功。
		bool InitServer(const unsigned int port);

		// 阻塞等待客户端的连接请求。
		// 返回值:true-有新的客户端已连接上来,false-失败,Accept被中断,如果Accept失败,可以重新Accept。
		bool Accept();

		// 获取客户端的ip地址。
		// 返回值:客户端的ip地址,如"192.168.1.100"。
		char *GetIP();

		// 接收客户端发送过来的数据。
		// buffer:接收数据缓冲区的地址,数据的长度存放在m_buflen成员变量中。
		// itimeout:等待数据的超时时间,单位:秒,缺省值是0-无限等待。
		// 返回值:true-成功;false-失败,失败有两种情况:1)等待超时,成员变量m_btimeout的值被设置为true;2)socket连接已不可用。
		bool Recv(char *buffer,const int itimeout=0);

		// 向客户端发送数据。
		// buffer:待发送数据缓冲区的地址。
		// ibuflen:待发送数据的大小,单位:字节,缺省值为0,如果发送的是ascii字符串,ibuflen取0,如果是二进制流数据,ibuflen为二进制数据块的大小。
		//itimeout:接收等待超时的时间,单位:秒,值是0-无限等待。
		// 返回值:true-成功;false-失败,如果失败,表示socket连接已不可用。
		bool Send(const char *buffer,const int ibuflen=0,const int itimeout=5);

		// 关闭监听的socket,即m_listenfd,常用于多进程服务程序的子进程代码中。
		void CloseListen();

		// 关闭客户端的socket,即m_connfd,常用于多进程服务程序的父进程代码中。
		void CloseClient();

		~CTcpServer();  // 析构函数自动关闭socket,释放资源。
};

CTcpServer::CTcpServer() {
	m_listenfd=-1;
	m_connfd=-1;
	m_socklen=0;
	m_btimeout=false;
}

bool CTcpServer::InitServer(const unsigned int port) {
	CloseListen();

	if ( (m_listenfd = socket(AF_INET,SOCK_STREAM,0))<=0) return false;

	//设置SO_REUSEADDR选项,解决关闭程序端口还在占用,再运行socket bind不成功的问题
	// WINDOWS平台如下
	//char b_opt='1';
	//setsockopt(m_listenfd,SOL_SOCKET,SO_REUSEADDR,&b_opt,sizeof(b_opt));

	// Linux如下
	int opt = 1;
	unsigned int len = sizeof(opt);
	setsockopt(m_listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,len);

	memset(&m_servaddr,0,sizeof(m_servaddr));
	m_servaddr.sin_family = AF_INET;
	m_servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	m_servaddr.sin_port = htons(port);
	if (bind(m_listenfd,(struct sockaddr *)&m_servaddr,sizeof(m_servaddr)) != 0 ) {
		CloseListen();
		return false;
	}

	if (listen(m_listenfd,5) != 0 ) {
		CloseListen();
		return false;
	}

	m_socklen = sizeof(struct sockaddr_in);

	return true;
}

bool CTcpServer::Accept() {
	if (m_listenfd == -1) return false;

	if ((m_connfd=accept(m_listenfd,(struct sockaddr *)&m_clientaddr,(socklen_t*)&m_socklen)) < 0)
		return false;

	return true;
}

char *CTcpServer::GetIP() {
	return(inet_ntoa(m_clientaddr.sin_addr));
}

bool CTcpServer::Recv(char *buffer,const int itimeout) {
	if (m_connfd == -1) return false;

	if (itimeout>0) {
		fd_set tmpfd;

		FD_ZERO(&tmpfd);
		FD_SET(m_connfd,&tmpfd);

		struct timeval timeout;
		timeout.tv_sec = itimeout;
		timeout.tv_usec = 0;

		m_btimeout = false;

		int i;
		if ( (i = select(m_connfd+1,&tmpfd,0,0,&timeout)) <= 0 ) {
			if (i==0) m_btimeout = true;
			return false;
		}
	}

	m_buflen = 0;
	return(TcpRecv(m_connfd,buffer,&m_buflen));
}

bool CTcpServer::Send(const char *buffer,const int ibuflen,const int itimeout) {
	if (m_connfd == -1) return false;

	if (itimeout>0) {
		fd_set tmpfd;

		FD_ZERO(&tmpfd);
		FD_SET(m_connfd,&tmpfd);

		struct timeval timeout;
		timeout.tv_sec = itimeout;
		timeout.tv_usec = 0;

		m_btimeout = false;

		int i;
		if ( (i = select(m_connfd+1,&tmpfd,0,0,&timeout)) <= 0 ) {
			if (i==0) m_btimeout = true;
			return false;
		}
	}

	int ilen = ibuflen;
	if (ilen==0) ilen=strlen(buffer);

	return(TcpSend(m_connfd,buffer,ilen));
}

void CTcpServer::CloseListen() {
	if (m_listenfd > 0) {
		close(m_listenfd);
		m_listenfd=-1;
	}
}

void CTcpServer::CloseClient() {
	if (m_connfd > 0) {
		close(m_connfd);
		m_connfd=-1;
	}
}

CTcpServer::~CTcpServer() {
	CloseListen();
	CloseClient();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C/C++封装socket通信类 的相关文章

  • socket通讯相互发送读取xml实例

    首先了解下socket通讯传输数据的特点 数据在网络传输时使用的都是字节流或字符流 Socket也不例外 所以我们发送数据的时候需要转换为字节发送 读取的时候也是以字节为单位读取 那么问题就在于socket通讯时 接收方并不知道此次数据有多
  • 在浏览器输入URL,按下回车之后的流程?

    1 在浏览器中输入一个URL 2 查找本地配置文件 如果之前有访问过 浏览器会进行缓存 如果没有的话会在本机域名解析文件hosts文件中寻找是否存在该URL的域名映射 如Windows的配置文件 C Windows System32 dri
  • MFC实现socket网络通信--主机与服务器之间传送数据

    MFC实现socket网络通信 模拟主机与服务器之间传送数据 MFC实现socket网络通信 1 新建MFC应用程序 2 创建服务端窗口界面 3 写服务器代码 4 创建客户端窗口界面 5 客户端代码部分 6 开始调试 7 小结 MFC实现s
  • 关于非同一局域网下两台设备之间的网络通信(服务器的作用)

    看过很多关于局域网下的两台设备之间的通信方式 最多的就是通过socket进行tcp ip通信 建立一个服务端 再建立一个客户端 客户端向服务端发起请求连接 然后再进行两端的通信 但发现其实这却存在着很多的问题与不足 如果是不在同一局域网下的
  • JDK8 网络Net包研究(一)

    网络基础 1 国际标准化组织的OSI 开放式系统互联模型 七层模型 2 TCP IP协议 组 四层模型 3 TCP IP协议组 一组包括TCP协议和IP协议 UDP协议 ICMP协议和其他一些协议的协议组 网络层 IP协议 gt 网络互连协
  • Socket -- udp

    接收者 完成System out println UDPProvider Started 作为接收者 指定一个端口用于数据接收 DatagramSocket ds new DatagramSocket 20000 构建接收实体 final
  • Socket编程之聊天程序 - 模拟Fins/ModBus协议通信过程

    设备控制软件编程涉及到的基本通信方式主要有TCP IP与串口 用到的数据通信协议有Fins与ModBus 更高级别的通信如 net中的Remoting与WCF在进行C S架构软件开发时会采用 本篇文章结合Fins ModBus协议的指令帧结
  • Websocket(一)——原理及基本属性和方法

    初次接触 WebSocket 的人 都会问同样的问题 我们已经有了 HTTP 协议 为什么还需要另一个协议 它能带来什么好处 答案很简单 因为 HTTP 协议有一个缺陷 通信只能由客户端发起 举例来说 我们想了解今天的天气 只能是客户端向服
  • c# 使用udp协议接收消息

    两个例子 例一 引用命名空间 using System Net using System Net Sockets 定义 private UdpClient Reveive IPAddress localIP IPAddress Parse
  • linux进程间通信---本地socket套接字(二)---多进程实现一个server对应多个client

    先给自己打个广告 本人的微信公众号正式上线了 搜索 张笑生的地盘 主要关注嵌入式软件开发 股票基金定投 足球等等 希望大家多多关注 有问题可以直接留言给我 一定尽心尽力回答大家的问题 想要获取完整源码的 关注公众号后回复 socket2 即
  • IOS 网络初探(一) - NSURLConnection

    在IOS中 除了最基本的socket外 苹果提供了NSURLConnection类来实现网络通信 请求服务器数据 GET方式 请求服务器数据分成异步和同步两种方式 先来看看异步 非阻塞 NSURL url NSURL URLWithStri
  • Python2.7网络通信socket和串口通信serial多线程同时实现

    Python2 7下多线程网络通信socket和串口通信serial同时进行 最近在写网络通信TCP IP读取数据和串口通信读取发送数据 之前写了单线程的然后这次尝试多线程实现 当然我是写的网络通信的服务端 话不多说贴上代码 coding
  • 从0实现基于Linux socket聊天室-实现聊天室的公聊、私聊功能-4

    前面文章链接如下 从0实现基于Linux socket聊天室 多线程服务器模型 1 从0实现基于Linux socket聊天室 多线程服务器一个很隐晦的错误 2 从0实现基于Linux socket聊天室 实现聊天室的登录 注册功能 3 上
  • Unity3d之Socket UDP协议

    原文地址 http blog csdn net dingkun520wy article details 49201245 一 Socket 套接字 UDP协议的特点 1 是基于无连接的协议 没有生成连接的延迟所以速度比TCP快 2 支持一
  • websocket详解

    之前利用websocket以及jQuery做了一个聊天通讯应用 最近在总结整个过程中的一些问题 也借此机会聊聊websocket协议 webSocket本身不存在跨域问题 所以可以利用webSocket来进行非同源之间的通信 webSock
  • C语言实现TCP连接

    开发环境 TCP服务端 TCP UDP测试工具 开发环境 Linux 编程语言 C语言 TCP UDP测试工具工具的使用请自行百度 我们用这款软件模拟TCP服务端 效果展示 代码编写 include
  • JAVA socket编程实例

    转载文章 原作者无从考证 感谢作者的无私奉献 事实上网络编程简单的理解就是两台计算机相互通讯数据而已 对于程序员而言 去掌握一种编程接口并使用一种编程模型相对就会显得简单的多了 Java SDK提供一些相对简单的Api来完成这些工作 Soc
  • socket编程之服务器端与客户端(代码实例)

    在我们学习的过程中 对TCP IP UDP Socket编程这些词应该有所了解了 随着网络技术的发展 这些词充斥着我们的耳朵 那么我想介绍一下 什么是TCP IP UDP socket在哪里呢 socket通信是什么呢 socket接口函数
  • socket,socket.io,mongodb

    Socket 网络上的程序实现双向的数据链接 这个链接的一端成为socket 1 Socket是一个持久链接 2 Socket是双向通信的 Socket VS ajax轮询 ajax轮询 是利用客户端来发送请求 每隔几秒发送一个http请求
  • SocketOutputStream和SocketChannel write方法的区别和底层实现

    Java直接内存原理提到了SocketChannel write的实现原理 通过IOUtil write将java堆内存拷贝到了直接内存 然后再把地址传给了I O函数 那么 BIO 是怎么实现往socket里面写数据的呢 BIO Socke

随机推荐