目录
一:面向对象设计思想
在前面的几篇文章介绍了服务器搭建socket通信的实现,只是在程序主入口main函数中做了简单测试,但在正常业务中,为了使得业务流程更加清晰明了,就需要采用封装思想,面向对象设计封装主要功能函数;
在本节文章中,主要目标就是把socket通信连接改写面向对象程序设计,需要思考搭建哪几个类以及每个类需要实现什么样的功能(用一个类的封装设计来搭建一整个模块的功能),具体的封装实现是要看数据传输的方式和业务的大致结构,因此封装不是固定的,适合的才是最好的;
常见服务器搭建类的设计包括有:地址类 IO类 socket基类 TCP服务器类 TCP客户端类 UDP类
二:网络事件
TCP 网络编程最本质的是处理三个半事件:
1.连接的建立,包括服务端接受 (accept) 新连接和客户端成功发起 (connect) 连接
2.连接的断开,包括主动断开 (close 或 shutdown) 和被动断开 (read 返回 0)
3.消息到达,文件描述符可读;
这是最为重要的一个事件,对它的处理方式决定了网络编程的风格(阻塞还是非阻塞,如何处理分包,应用层的缓冲如何设计等等)
4.消息发送完毕,这算半个;
对于低流量的服务,可以不必关心这个事件;另外,这里“发送完毕”是指将数据写入操作系统的缓冲区,将由 TCP 协议栈负责数据的发送与重传,不代表对方已经收到数据
三:socket通信 类的设计
地址类的设计
#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
class CHostAddress
{
public:
CHostAddress(unsigned short port);
~CHostAddress();
unsigned short getPort();
void setPort(unsigned short port);
struct sockaddr_in getAddr_in();
struct sockaddr* getAddr();
int getLength();
private:
unsigned short port;
struct sockaddr_in s_addr;
int length;
};
#include "CHostAddress.h"
CHostAddress::CHostAddress(unsigned short port)
{
this->port = port;
this->s_addr.sin_family = AF_INET;//IPV4版本选择
this->s_addr.sin_addr.s_addr = INADDR_ANY;
this->s_addr.sin_port = this->port;
this->length = sizeof(this->s_addr);
}
CHostAddress::~CHostAddress()
{
}
unsigned short CHostAddress::getPort()
{
return this->port;
}
void CHostAddress::setPort(unsigned short port)
{
this->port = port;
}
sockaddr_in CHostAddress::getAddr_in()
{
return this->s_addr;
}
sockaddr* CHostAddress::getAddr()
{
return (struct sockaddr*)&(this->s_addr);
}
int CHostAddress::getLength()
{
return this->length;
}
socket通信 基类
#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
class CBaseSocket
{
public:
CBaseSocket(unsigned short port);
virtual ~CBaseSocket();
int getSocketfd();
void start();//运行网络连接
virtual void stop() = 0;
virtual void run() = 0;
protected:
int socketfd;
};
#include "CBaseSocket.h"
CBaseSocket::CBaseSocket(unsigned short port)
{
this->socketfd = 0;
}
CBaseSocket::~CBaseSocket()
{
}
int CBaseSocket::getSocketfd()
{
return this->socketfd;
}
void CBaseSocket::start()
{
this->socketfd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
if (this->socketfd < 0)
{
perror("socket error");
}
else
{
this->run();
}
}
TCP服务器类的设计
#pragma once
#include "CBaseSocket.h"
#include"CHostAddress.h"
#include <sys/types.h>
#include <sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<unistd.h>
#include<iostream>
#define LISTEN_MAX_NUM 10
using namespace std;
class CTcpServer :
public CBaseSocket
{
public:
CTcpServer(unsigned short port);
~CTcpServer();
void run();
void stop();
CHostAddress* getAddress();
void setAddress(CHostAddress* address);
private:
CHostAddress* address;
};
#include "CTcpServer.h"
CTcpServer::CTcpServer(unsigned short port):CBaseSocket(port)
{
this->address = new CHostAddress(port);
}
CTcpServer::~CTcpServer()
{
delete this->address;
}
void CTcpServer::run()
{
int opt_val = 1;
if (setsockopt(this->socketfd, SOL_SOCKET, SO_REUSEADDR, (const void*)&opt_val, sizeof(opt_val))==-1)
{
perror("setsockopt error");
}
if (bind(this->socketfd, this->address->getAddr(), this->address->getLength()) == -1)
{
perror("bind error");
}
if (listen(this->socketfd, LISTEN_MAX_NUM) == -1)
{
perror("listen error");
}
cout << "服务器启动成功" << endl;
}
void CTcpServer::stop()
{
if (this->socketfd != 0)
{
close(this->socketfd);
this->socketfd = 0;
}
}
CHostAddress* CTcpServer::getAddress()
{
return this->address;
}
void CTcpServer::setAddress(CHostAddress* address)
{
this->address = address;
}
main.cpp 程序主入口测试
#include<iostream>
#include"CTcpServer.h"
using namespace std;
int main()
{
CTcpServer* tcp = new CTcpServer(10000);
tcp->start();
return 0;
}
可以做个简单测试,测试下服务器是否能够成功启动
服务器搭建,类的封装设计还待完善,在后面文章中会继续补充