socket编程包括服务端和客户端,TCP或UDP,单线程、多线程、select模式等多种实现方式,现对代码进行重构。
详细实现代码:https://download.csdn.net/download/k117470154/10520499
一、类图
类图说明:
1. CSktReq:socket地址基类,封装socktet、ip、端口
2. CSktReq:socket请求基类,每个socket请求创建一个实例,保存本端和对端地址
3. CSktApp:socket应用基类,实现不同类型的socket应用,比如tcpserver、tcpclient等
二、代码重构后,单线程TcpServer的实现代码
1. socket_base.h
#ifndef _SOCKET_BASE_H
#define _SOCKET_BASE_H
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <assert.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <list>
#include <map>
using namespace std;
/* 重定义一些数据类型 */
typedef void VOID;
typedef char CHAR;
typedef unsigned char BYTE;
typedef unsigned int BOOL;
typedef unsigned short WORD16;
typedef unsigned int WORD32;
typedef int SWORD32;
typedef unsigned long ULONG;
typedef stringstream SSTREAM;
typedef ostringstream OSTREAM;
typedef istringstream ISTREAM;
typedef std::string STRCPP;
typedef std::vector<string> STRVEC;
typedef std::map<string, string> STRMAP;
typedef std::map<string, string>::iterator STRMAPIT;
/*------------------------------------------------*/
typedef int SOCKFD;
typedef struct sockaddr_in SOCKADDRIN;
typedef struct sockaddr SOCKADDR;
typedef struct epoll_event EPEVENT;
/*------------------------------------------------*/
typedef ofstream OFSTREAM;
typedef ifstream IFSTREAM;
typedef int FILEFD;
typedef struct stat FILEST;
typedef struct dirent DIRENT;
/*------------------------------------------------*/
typedef pthread_t THREAD;
typedef pthread_mutex_t TMUTEX;
typedef pthread_cond_t TCONDT;
/*------------------------------------------------*/
typedef pid_t PID;
typedef int PSTAT;
/*------------------------------------------------*/
typedef time_t TIMET;
typedef tm TIMETM;
typedef struct timeval TIMEVAL;
/*------------------------------------------------*/
#define TRUE (1)
#define FALSE (0)
#define FDREAD 0
#define FDWRITE 1
#define STDIN 0
#define STDOUT 1
#define INVALIDFD ((SOCKFD)-1)
/*------------------------------------------------*/
// 定义socket地址类 封装socket ip port
class CSktAddr
{
public:
CSktAddr();
STRCPP addr();
STRCPP portstr();
VOID noblock();
VOID setaddr(SOCKFD fd, STRCPP ip, WORD16 port, BOOL block);
VOID closefd();
public:
SOCKFD fd; // socket
STRCPP ip; // ip地址
WORD16 port; // 端口号
};
// 定义socket请求基类
class CSktReq
{
public:
CSktReq():m_rbuff(), m_sbuff(), m_saddr(), m_paddr(), m_block(FALSE){}
virtual ~CSktReq(){}
SOCKFD selffd() {return m_saddr.fd;}
SOCKFD peerfd() {return m_paddr.fd;}
VOID closeself();
VOID closepeer();
VOID setsaddr(SOCKFD fd);
VOID setpaddr(SOCKFD fd);
virtual VOID* clone() = 0;
virtual BOOL initialize() = 0;
virtual BOOL activate() = 0;
virtual BOOL receive() = 0;
virtual BOOL dispatch() = 0;
virtual BOOL process() = 0;
protected:
STRCPP m_rbuff; //接收数据的buffer
STRCPP m_sbuff; //发送数据的buffer
CSktAddr m_saddr; //本端地址
CSktAddr m_paddr; //对端地址
BOOL m_block;
};
typedef list<CSktReq*> REQUESTLIST;
typedef list<CSktReq*>::iterator REQUESTITER;
typedef map<SOCKFD, CSktReq*> REQUESTMAP;
// 定义socket应用基类
class CSktApp
{
public:
CSktApp(STRCPP ip, WORD16 port) : m_ip(ip), m_port(port), m_reqtype(NULL) {}
virtual ~CSktApp();
virtual VOID working() = 0;
virtual VOID initreqtype() = 0;
virtual VOID startup();
protected:
STRCPP m_ip; //
WORD16 m_port; //
CSktReq* m_reqtype; // 请求原型 (使用原型模式)
private:
};
STRCPP get_current_time();
STRCPP getgmttime(TIMET timestamp);
BOOL getline(STRCPP& buffer, STRCPP& line);
BOOL getsize(STRCPP& buffer, STRCPP& str, ULONG size);
VOID delCRLF(STRCPP& buffer);
VOID strsplit(const STRCPP& str, STRCPP sep, STRVEC& strvec);
#endif
2. socket_base.cpp
#include "socket_base.h"
//socket地址类构造函数
CSktAddr::CSktAddr() : fd(INVALIDFD), ip(), port(0)
{
}
//返回 ip地址:端口号:socket 格式的字符串
STRCPP CSktAddr::addr()
{
OSTREAM addr;
addr << ip << ":" << port << ":" << fd;
return addr.str();
}
STRCPP CSktAddr::portstr()
{
SSTREAM pstr;
pstr << port;
return pstr.str();
}
//设置socket为非阻塞
VOID CSktAddr::noblock()
{
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
}
//设置socket地址各个字段
VOID CSktAddr::setaddr(SOCKFD sfd, STRCPP sip, WORD16 sport, BOOL block)
{
fd = sfd;
ip = sip;
port = sport;
if(INVALIDFD != fd && !block)
{
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
}
}
//关闭socket
VOID CSktAddr::closefd()
{
if(INVALIDFD != fd)
{
close(fd);
fd = INVALIDFD;
}
}
VOID CSktReq::closeself()
{
m_saddr.closefd();
}
VOID CSktReq::closepeer()
{
m_paddr.closefd();
}
VOID CSktReq::setsaddr(SOCKFD fd)
{
SOCKADDRIN addr = {0};
WORD32 size = sizeof(addr);
getsockname(fd, (SOCKADDR*)&addr, &size);
m_saddr.fd = fd;
m_saddr.ip = inet_ntoa(addr.sin_addr);
m_saddr.port = ntohs(addr.sin_port);
}
VOID CSktReq::setpaddr(SOCKFD fd)
{
SOCKADDRIN addr = {0};
WORD32 size = sizeof(addr);
getpeername(fd, (SOCKADDR*)&addr, &size);
m_paddr.fd = fd;
m_paddr.ip = inet_ntoa(addr.sin_addr);
m_paddr.port = ntohs(addr.sin_port);
}
CSktApp::~CSktApp()
{
if(NULL != m_reqtype)
{
m_reqtype->closeself();
m_reqtype->closepeer();
delete m_reqtype;
m_reqtype = NULL;
}
}
VOID CSktApp::startup()
{
initreqtype();
if(NULL != m_reqtype)
{
if(m_reqtype->initialize())
{
working(); // working必须有个循环在一直执行
}
}
else
{
cout << "CSktApp startup failed." << endl;
}
}
// 获取当前时间
STRCPP get_current_time()
{
TIMET tt = time(NULL);
TIMETM* t = localtime(&tt);
CHAR str[100] = {0};
sprintf(str, "%d-%02d-%02d %02d:%02d:%02d",
t->tm_year + 1900,
t->tm_mon + 1,
t->tm_mday,
t->tm_hour,
t->tm_min,
t->tm_sec);
return str;
}
// 获取GMT时间
STRCPP getgmttime(TIMET timestamp)
{
static const CHAR* NAME_WEEKDAY[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static const CHAR* NAME_MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
CHAR str[100] = {0};
TIMETM* timegmt;
timegmt = gmtime(×tamp);
sprintf(str, "%s, %02d %3s %4d %02d:%02d:%02d GMT",
NAME_WEEKDAY[timegmt->tm_wday],
timegmt->tm_mday,
NAME_MONTH[timegmt->tm_mon],
timegmt->tm_year + 1900,
timegmt->tm_hour,
timegmt->tm_min,
timegmt->tm_sec);
return STRCPP(str);
}
/* 从buffer中读取一行 \n或\r\n 并且去掉了\n 或\r\n */
BOOL getline(STRCPP& buffer, STRCPP& line)
{
ULONG linepos = buffer.find('\n');
if(string::npos == linepos)
{
return FALSE;
}
line.clear();
if(linepos > 0 && buffer[linepos-1] == '\r')
{
line.assign(buffer, 0, linepos-1);
}
else
{
line.assign(buffer, 0, linepos);
}
buffer.erase(0, linepos+1);
return TRUE;
}
/* 从buffer中读取前size个字节 放入str 并从buffer中删除前size个字节 */
BOOL getsize(STRCPP& buffer, STRCPP& str, ULONG size)
{
str.clear();
if(buffer.size() < size)
{
return FALSE;
}
str.assign(buffer, 0 , size);
buffer.erase(0, size);
return TRUE;
}
/* 从buffer中删除末尾的 \n或\r\n */
VOID delCRLF(STRCPP& buffer)
{
ULONG size = buffer.size();
if(0 < size && '\n' == buffer[size-1])
{
size--;
if(0 < size && '\r' == buffer[size-1])
{
size--;
}
buffer.assign(buffer, 0, size);
}
}
/* 以sep分割str 放入strvec中
strvec.size() 肯定 >= 1
如果sep位于str的开始或结尾 则strvec的第一个或最后一个为空字符串 */
VOID strsplit(const STRCPP& str, STRCPP sep, STRVEC& strvec)
{
ULONG pos1 = 0;
ULONG pos2 = 0;
STRCPP strtmp;
strvec.clear();
pos2 = str.find(sep);
while(string::npos != pos2)
{
strtmp = str.substr(pos1, pos2 - pos1);
//if(0 != strtmp.size())
//{
strvec.push_back(strtmp);
//}
pos1 = pos2 + sep.size();
pos2 = str.find(sep, pos1);
}
strtmp = str.substr(pos1);
//if(0 != strtmp.size())
//{
strvec.push_back(strtmp);
//}
}
3. socket_tcpbase.h
#ifndef _SOCKET_TCP_H
#define _SOCKET_TCP_H
#include "socket_base.h"
class CTcpReq : public CSktReq
{
public:
BOOL readinfo(STRCPP& buffer);
BOOL sendinfo(const STRCPP& buffer);
protected:
private:
};
class CTcpApp : public CSktApp
{
public:
CTcpApp(STRCPP ip, WORD16 port) : CSktApp(ip, port){}
protected:
private:
};
#endif
4. socket_tcpbase.cpp
#include "socket_tcpbase.h"
BOOL CTcpReq::readinfo(STRCPP& info)
{
CHAR tmp[1] = {0}; // 接收信息的缓冲区
SWORD32 len = 0; // recv返回值 实际一次接收的长度
len = recv(m_paddr.fd, tmp, sizeof(tmp), 0);
if(0 < len)
{
info.append(tmp, len);
return TRUE;
}
if(0 == len)
{
cout << "connect closed: " << m_paddr.addr() << endl;
m_paddr.closefd();
return FALSE;
}
else
{
if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
{
return TRUE;
}
return FALSE;
}
}
BOOL CTcpReq::sendinfo(const STRCPP& info)
{
WORD32 sendLen = 0; // 已发送的长度
SWORD32 tempLen = 0; // send返回值 实际一次发送的长度
const CHAR* buffer = info.c_str(); // 要发送的信息
const WORD32 size = info.size(); // 要发送的总长度
if(NULL == buffer)
{
return FALSE;
}
while(sendLen < size)
{
tempLen = send(m_paddr.fd, buffer+sendLen, size-sendLen, MSG_NOSIGNAL);
if(-1 == tempLen)
{
if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
{
continue;
}
return FALSE;
}
else
{
sendLen += tempLen;
}
}
return TRUE;
}
5. socket_tcpserver.h
#ifndef _SOCKET_TCPSERVER_H
#define _SOCKET_TCPSERVER_H
#include "socket_tcpbase.h"
const STRCPP TCP_SERVER_IP = "0.0.0.0";
const WORD16 TCP_SERVER_PORT = 9000;
class CTcpSvrReq : public CTcpReq
{
public:
CTcpSvrReq(STRCPP ip, WORD16 port, BOOL block)
{
m_saddr.setaddr(INVALIDFD, ip, port, block);
m_block = block;
}
virtual VOID* clone();
virtual BOOL initialize();
virtual BOOL activate();
virtual BOOL receive();
virtual BOOL dispatch();
virtual BOOL process();
protected:
private:
};
class CTcpSvrApp : public CTcpApp
{
public:
CTcpSvrApp(STRCPP svrip, WORD16 svrport) : CTcpApp(svrip, svrport) {}
virtual VOID initreqtype()
{
m_reqtype = new CTcpSvrReq(m_ip, m_port, TRUE);
}
protected:
REQUESTLIST m_requests;
private:
};
/* 单线程 阻塞 */
class CTcpServer_singlethread : public CTcpSvrApp
{
public:
CTcpServer_singlethread(STRCPP svrip, WORD16 svrport) : CTcpSvrApp(svrip, svrport) {}
virtual VOID working();
protected:
};
#endif
6. socket_tcpserver.cpp
#include "socket_tcpserver.h"
VOID* CTcpSvrReq::clone()
{
return new CTcpSvrReq(*this);
}
BOOL CTcpSvrReq::initialize()
{
WORD32 opt = 1;
SOCKADDRIN addr = {0};
SOCKFD fd = INVALIDFD;
//创建socket
fd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == fd)
{
perror("socket failed");
return FALSE;
}
//设置地址重用
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (CHAR*)&opt, sizeof(opt));
addr.sin_family = AF_INET;
addr.sin_port = htons(m_saddr.port);
addr.sin_addr.s_addr = ("" == m_saddr.ip ? htonl(INADDR_ANY) : inet_addr(m_saddr.ip.c_str()));
//绑定ip地址和端口
if(-1 == bind(fd, (SOCKADDR*)&addr, sizeof(addr)))
{
perror("bind failed");
close(fd);
return FALSE;
}
//开始监听
if(-1 == listen(fd, 10))
{
perror("listen failed");
close(fd);
return FALSE;
}
m_saddr.setaddr(fd, m_saddr.ip, m_saddr.port, m_block);
return TRUE;
}
BOOL CTcpSvrReq::activate()
{
SOCKFD sockfd = -1;
SOCKADDRIN addrin = {0};
WORD32 addlen = sizeof(addrin);
if(INVALIDFD == m_saddr.fd)
{
cout << "activate failed, fd invalid" << endl;
return FALSE;
}
sockfd = accept(m_saddr.fd, (SOCKADDR*)&addrin, &addlen);
if(-1 == sockfd)
{
perror("accept failed");
return FALSE;
}
else
{
m_paddr.setaddr(sockfd, inet_ntoa(addrin.sin_addr), ntohs(addrin.sin_port), m_block);
cout << "connect accept: " << m_paddr.addr() << endl;
return TRUE;
}
}
BOOL CTcpSvrReq::receive()
{
if(readinfo(m_rbuff))
{
return TRUE;
}
return FALSE;
}
BOOL CTcpSvrReq::dispatch()
{
return TRUE;
}
BOOL CTcpSvrReq::process()
{
STRCPP line;
if(getline(m_rbuff, line))
{
cout << get_current_time() << " ";
cout << m_paddr.addr() << " ";
cout << line << endl; // getline去掉了\n 所以这里需要endl才能输出到屏幕
}
return TRUE;
}
/* 单线程单链接 阻塞 */
VOID CTcpServer_singlethread::working()
{
while(NULL != m_reqtype)
{
if(m_reqtype->activate())
{
while(INVALIDFD != m_reqtype->peerfd())
{
if(m_reqtype->receive())
{
m_reqtype->process();
}
}
}
else
{
return;
}
}
}
7. test_tcpserver.cpp
#include "socket_tcpserver.h"
class CTestTcpServer : public CTcpServer_singlethread
{
public:
CTestTcpServer(STRCPP ip, WORD16 port) : CTcpServer_singlethread (ip, port) {}
};
int main()
{
CTestTcpServer tcpserver("127.0.0.1", 9000);
tcpserver.startup();
return 0;
}