datetime:2022/12/28 14:37
author:nzb
程序封装成类
_cmpublic.h
#ifndef _cmpublic_H
#define _cmpublic_H
#include <stdio.h>
#include <utime.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <time.h>
#include <math.h>
#include <stdarg.h>
#include <errno.h>
#include <signal.h>
#include <netdb.h>
#include <locale.h>
#include <dirent.h>
#include <termios.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <list>
#include <vector>
#include <deque>
#include <algorithm>
// 采用stl标准库的命名空间std
using namespace std;
#endif
Writen()函数和Readn()函数
_freecplus.h
// 从已经准备好的socket中读取数据。
// sockfd:已经准备好的socket连接。
// buffer:接收数据缓冲区的地址。
// n:本次接收数据的字节数。
// 返回值:成功接收到n字节的数据后返回true,socket连接不可用返回false。
bool Readn(const int sockfd,char *buffer,const size_t n);
// 向已经准备好的socket中写入数据。
// sockfd:已经准备好的socket连接。
// buffer:待发送数据缓冲区的地址。
// n:待发送数据的字节数。
// 返回值:成功发送完n字节的数据后返回true,socket连接不可用返回false。
bool Writen(const int sockfd,const char *buffer,const size_t n);
_freecplus.cpp
bool Readn(const int sockfd, char *buffer, const size_t n) {
int nLeft, nread, idx;
nLeft = n;
idx = 0;
while (nLeft > 0) {
if ((nread = recv(sockfd, buffer + idx, nLeft, 0)) <= 0) return false;
idx += nread;
nLeft -= nread;
}
return true;
}
bool Writen(const int sockfd, const char *buffer, const size_t n) {
int nLeft, idx, nwritten;
nLeft = n;
idx = 0;
while (nLeft > 0) {
if ((nwritten = send(sockfd, buffer + idx, nLeft, 0)) <= 0) return false;
nLeft -= nwritten;
idx += nwritten;
}
return true;
}
TcpWrite()函数和TcpRead()函数
_freecplus.h
// 接收socket的对端发送过来的数据。
// sockfd:可用的socket连接。
// buffer:接收数据缓冲区的地址。
// ibuflen:本次成功接收数据的字节数。
// itimeout:接收等待超时的时间,单位:秒,缺省值是0-无限等待。
// 返回值:true-成功;false-失败,失败有两种情况:1)等待超时;2)socket连接已不可用。
bool TcpRead(const int sockfd,char *buffer,int *ibuflen,const int itimeout=0);
// 向socket的对端发送数据。
// sockfd:可用的socket连接。
// buffer:待发送数据缓冲区的地址。
// ibuflen:待发送数据的字节数,如果发送的是ascii字符串,ibuflen取0,如果是二进制流数据,ibuflen为二进制数据块的大小。
// 返回值:true-成功;false-失败,如果失败,表示socket连接已不可用。
bool TcpWrite(const int sockfd,const char *buffer,const int ibuflen=0);
_freecplus.cpp
bool TcpRead(const int sockfd, char *buffer, int *ibuflen, const int itimeout) {
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;
int i;
if ((i = select(sockfd + 1, &tmpfd, 0, 0, &timeout)) <= 0) return false;
}
(*ibuflen) = 0;
if (Readn(sockfd, (char *) ibuflen, 4) == false) return false;
(*ibuflen) = ntohl(*ibuflen); // 把网络字节序转换为主机字节序。
if (Readn(sockfd, buffer, (*ibuflen)) == false) return false;
return true;
}
bool TcpWrite(const int sockfd, const char *buffer, const int ibuflen) {
if (sockfd == -1) return false;
fd_set tmpfd;
FD_ZERO(&tmpfd);
FD_SET(sockfd, &tmpfd);
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
if (select(sockfd + 1, 0, &tmpfd, 0, &timeout) <= 0) return false;
int ilen = 0;
// 如果长度为0,就采用字符串的长度
if (ibuflen == 0) ilen = strlen(buffer);
else ilen = ibuflen;
int ilenn = htonl(ilen); // 转换为网络字节序。
char strTBuffer[ilen + 4];
memset(strTBuffer, 0, sizeof(strTBuffer));
memcpy(strTBuffer, &ilenn, 4); // memcpy 既能处理文本数据和二进制数据,strcpy只能处理文本数据
memcpy(strTBuffer + 4, buffer, ilen);
if (Writen(sockfd, strTBuffer, ilen + 4) == false) return false;
return true;
}
服务端CTcpServer类
_freecplus.h
// 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; // 调用Read和Write方法时,失败的原因是否是超时:true-超时,false-未超时。
int m_buflen; // 调用Read方法后,接收到的报文的大小,单位:字节。
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 Read(char *buffer, const int itimeout = 0);
// 向客户端发送数据。
// buffer:待发送数据缓冲区的地址。
// ibuflen:待发送数据的大小,单位:字节,缺省值为0,如果发送的是ascii字符串,ibuflen取0,如果是二进制流数据,ibuflen为二进制数据块的大小。
// 返回值:true-成功;false-失败,如果失败,表示socket连接已不可用。
bool Write(const char *buffer, const int ibuflen = 0);
// 关闭监听的socket,即m_listenfd,常用于多进程服务程序的子进程代码中。
void CloseListen();
// 关闭客户端的socket,即m_connfd,常用于多进程服务程序的父进程代码中。
void CloseClient();
~CTcpServer(); // 析构函数自动关闭socket,释放资源。
};
_freecplus.cpp
CTcpServer::CTcpServer() {
m_listenfd = -1;
m_connfd = -1;
m_socklen = 0;
m_btimeout = false;
}
bool CTcpServer::InitServer(const unsigned int port) {
if (m_listenfd > 0) {
close(m_listenfd);
m_listenfd = -1;
}
if ((m_listenfd = socket(AF_INET, SOCK_STREAM, 0)) <= 0) return false;
// 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::Read(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 (TcpRead(m_connfd, buffer, &m_buflen));
}
bool CTcpServer::Write(const char *buffer, const int ibuflen) {
if (m_connfd == -1) return false;
fd_set tmpfd;
FD_ZERO(&tmpfd);
FD_SET(m_connfd, &tmpfd);
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
m_btimeout = false;
int i;
if ((i = select(m_connfd + 1, 0, &tmpfd, 0, &timeout)) <= 0) {
if (i == 0) m_btimeout = true;
return false;
}
int ilen = ibuflen;
if (ilen == 0) ilen = strlen(buffer);
return (TcpWrite(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();
}
客户端CTcpClient类
_freecplus.h
// socket通信的客户端类
class CTcpClient {
public:
int m_sockfd; // 客户端的socket.
char m_ip[21]; // 服务端的ip地址。
int m_port; // 与服务端通信的端口。
bool m_btimeout; // 调用Read和Write方法时,失败的原因是否是超时:true-超时,false-未超时。
int m_buflen; // 调用Read方法后,接收到的报文的大小,单位:字节。
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 Read(char *buffer, const int itimeout = 0);
// 向服务端发送数据。
// buffer:待发送数据缓冲区的地址。
// ibuflen:待发送数据的大小,单位:字节,缺省值为0,如果发送的是ascii字符串,ibuflen取0,如果是二进制流数据,ibuflen为二进制数据块的大小。
// 返回值:true-成功;false-失败,如果失败,表示socket连接已不可用。
bool Write(const char *buffer, const int ibuflen = 0);
// 断开与服务端的连接
void Close();
~CTcpClient(); // 析构函数自动关闭socket,释放资源。
};
_freecplus.cpp
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::Read(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 (TcpRead(m_sockfd, buffer, &m_buflen));
}
bool CTcpClient::Write(const char *buffer, const int ibuflen) {
if (m_sockfd == -1) return false;
fd_set tmpfd;
FD_ZERO(&tmpfd);
FD_SET(m_sockfd, &tmpfd);
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
m_btimeout = false;
int i;
if ((i = select(m_sockfd + 1, 0, &tmpfd, 0, &timeout)) <= 0) {
if (i == 0) m_btimeout = true;
return false;
}
int ilen = ibuflen;
if (ibuflen == 0) ilen = strlen(buffer);
return (TcpWrite(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();
}
简单服务端和客户端示例
_server.cpp
#include "_freecplus.h"
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Using:./demo48 port\nExample:./demo48 5005\n\n");
return -1;
}
CTcpServer TcpServer; // 创建服务端对象。
if (TcpServer.InitServer(atoi(argv[1])) == false) // 初始化TcpServer的通信端口。
{
printf("TcpServer.InitServer(%s) failed.\n", argv[1]);
return -1;
}
if (TcpServer.Accept() == false) // 等待客户端连接。
{
printf("TcpServer.Accept() failed.\n");
return -1;
}
printf("客户端(%s)已连接。\n", TcpServer.GetIP());
char strbuffer[1024]; // 存放数据的缓冲区。
while (true) {
memset(strbuffer, 0, sizeof(strbuffer));
//if (TcpServer.Read(strbuffer,300)==false) break; // 接收客户端发过来的请求报文。
if (TcpServer.Read(strbuffer, 10) == false) break; // 接收客户端发过来的请求报文。
printf("接收:%s\n", strbuffer);
strcat(strbuffer, "ok"); // 在客户端的报文后加上"ok"。
printf("发送:%s\n", strbuffer);
if (TcpServer.Write(strbuffer) == false) break; // 向客户端回应报文。
}
printf("客户端已断开。\n"); // 程序直接退出,析构函数会释放资源。
}
_client.cpp
#include "_freecplus.h"
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Using:./demo47 ip port\nExample:./demo47 172.21.0.3 5005\n\n");
return -1;
}
CTcpClient TcpClient; // 创建客户端的对象。
if (TcpClient.ConnectToServer(argv[1], atoi(argv[2])) == false) // 向服务端发起连接请求。
{
printf("TcpClient.ConnectToServer(\"%s\",%s) failed.\n", argv[1], argv[2]);
return -1;
}
char strbuffer[1024]; // 存放数据的缓冲区。
for (int ii = 0; ii < 30; ii++) // 利用循环,与服务端进行5次交互。
{
memset(strbuffer, 0, sizeof(strbuffer));
snprintf(strbuffer, 50, "%d:这是第%d个超级女生,编号%03d。", getpid(), ii + 1, ii + 1);
printf("发送:%s\n", strbuffer);
if (TcpClient.Write(strbuffer) == false) break; // 向服务端发送请求报文。
memset(strbuffer, 0, sizeof(strbuffer));
if (TcpClient.Read(strbuffer, 20) == false) break; // 接收服务端的回应报文。
printf("接收:%s\n", strbuffer);
sleep(1);
}
// 程序直接退出,析构函数会释放资源。
}