時間就是這麼的快,不知不覺就到了日報(七),感覺像是到了項目的後半場了,但是我還有一整個模塊(文件管理與FTP傳輸)還沒弄,現有模塊也還沒進行梳理,都是各個模塊各自爲政,感覺時間不太夠用了。
看一下上一次的日報情況,看看團隊其他成員是否有同樣感慨:
日報
1號 | 日報6(epoll模型與責任鏈模式的有機結合) |
---|---|
3號 | 日報6 |
4號 | 日報5 |
5號 | 日報6(不定長包) |
日報5(線程及線程池) | |
日報4(Socket連接) | |
9號 | 日報4 |
任務實現
將前後端和中控服務器都安排好了,前置服務器使用的是 ”epoll+責任鏈“ 的組合,後置服務器還是採用傳統模式,“中央集權”。具體看圖:
前置服務器設計
中控服務器設計
後置服務器設計
前置服務器部分代碼
前置服務器的代碼已經經過初步測試,這裏先放出來,還需要待進一步的壓力測試。
在日報六裏面放過的代碼就不再放了,放沒放過的:
//EtoC.h
#pragma once
#include "Abstract_Front.h"
#include "Epoll.h"
#include "PacketCommand2.h"
#define SERV_PORT 8888 //中控端口號
class EtoC :
public Abstract_Front
{
private:
int connect_fd;
Epoll* ep;
PacketBase* packetbase;
public:
EtoC(Epoll* epp);
void Start();
void send_name();
int run(int ffdd);
};
//EtoC.cpp
#include "EtoC.h"
EtoC::EtoC(Epoll* epp) {
this->ep = epp;
packetbase = new PacketBase();
connect_fd = socket(PF_INET, SOCK_STREAM, 0);
set_fd(connect_fd);
ep->Epoll_add(connect_fd);
}
void EtoC::Start() {
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = PF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
bind(connect_fd, (struct sockaddr*) & servaddr, sizeof(servaddr));
connect(connect_fd, (struct sockaddr*) & servaddr, sizeof(servaddr));
}
void EtoC::send_name() {
PacketCommand2* Pack2 = new PacketCommand2();
char* name = new char[6];
strcpy(name,"Front");
Pack2->Send_name(name);
send(connect_fd,Pack2->getData(),Pack2->getSize(),0);
}
//´ÓÖпطþÎñÆ÷¶Áµ½Êý¾Ý
int EtoC::run(int ffdd) {
int n = read(connect_fd, packetbase->getData(), 1024);
packetbase->setSize(n);
packetbase->unpack();
send(packetbase->getHead()->fd, packetbase->getData(), packetbase->getSize(),0);
}
//Recv_Data.h
#pragma once
#include "Abstract_Front.h"
#include "PacketBase.h"
#include "Epoll.h"
//處理從客戶端收到的消息
class RecvData :
public Abstract_Front
{
private:
PacketBase* packetbase;
Epoll* ep;
public:
RecvData(Epoll* ep);
int run(int ffdd);
};
//Recv_Date.cpp
#include "RecvData.h"
RecvData::RecvData(Epoll* ep) {
this->ep = ep;
}
//場景類實地操作時先將fd配置爲接入中控的
int RecvData::run(int ffdd)
{
packetbase = new PacketBase();
int n = read(ffdd, packetbase->getData(), 1024);
if (n <= 0) {
ep->Epoll_del(ffdd);
close(ffdd);
cout << "A Client Close!" << endl;
}
packetbase->setSize(n); //我看這一步可以併到解包裏面去
packetbase->unpack();
packetbase->getHead()->fd = ffdd;
packetbase->pack();
send(this->get_fd(), packetbase->getData(), packetbase->getSize(),0);
}
//Packet_Command2.h
#pragma once
#include "PacketBase.h"
#include "Packet2.h"
//用於邊緣服務器向中控服務器自報家門
class PacketCommand2 :
public PacketBase
{
public:
bool Send_name(char* name);
};
//Packet2.h
#pragma once
#include<stdio.h>
typedef struct edge {
char name[6]; //連入服務器名稱
}Edge_t;
//Packet_Command2.cpp
#include "PacketCommand2.h"
bool PacketCommand2::Send_name(char* name) {
int sz = sizeof(Edge_t);
this->setBodySize(sz);
this->Body = new char[sz];
Head.funcId = 0x30;
Head.optid = 0x01;
Head.usrlenth = sz;
Head.syn = 0x04;
Edge_t* body = (Edge_t*)Body;
strcpy(body->name, name);
Tail.pack_tail = 0x05;
return this->pack();
}
//場景類
//前置服務器場景類
#include"Abstract_Front.h"
#include"Epoll.h"
#include"EtoC.h"
#include"RecvData.h"
#include"Socket.h"
int main()
{
Epoll* ep = new Epoll;
EtoC* ec = new EtoC(ep);
ec->Start(); //連向中控服務器
ec->send_name();//自保家門
Socket* sc = new Socket(ep);
RecvData* rc = new RecvData(ep);
rc->set_fd(ec->get_fd()); //將fd與連入中控的fd進行掛載
//組鏈:面向客戶端連接、面向中控連接、面向客戶端數據處理
ep->setFrontHead(sc);
sc->setNextHandle(ec);
ec->setNextHandle(rc);
while (1) {
int nevent = ep->Epoll_Wait();
ep->Epoll_run(nevent);
}
}
從這個Packet_Command2和Packet2的添加,就可以看出設計模式的優勢。有興趣可以去看我的設計模式專欄。
這裏就不放鏈接了,文章挺多,自取。
中控服務器部分代碼
中控服務器中有些地方採用默認的形式,如果後續要拓展,可以在默認的地方改爲動態,不礙事,比較懶就沒弄成動態。
部分代碼和前置相同,就不再放
//Socket.h
#pragma once
#include "Abstract_Front.h"
#include "Epoll.h"
class Socket :
public Abstract_Front
{
public:
Socket(Epoll* epp);
int run(int ffdd);
private:
Epoll* ep;
int listen_fd;
};
//Socket.cpp
#include "Socket.h"
Socket::Socket(Epoll* epp) {
this->ep = epp;
struct sockaddr_in servaddr;//客戶端地址及服務器地址
listen_fd = socket(AF_INET, SOCK_STREAM, 0);//1.創建文件描述符(用於監聽)
//成功返回文件描述符,失敗返回-1,並設置errno
set_fd(listen_fd);
ep->Epoll_add(listen_fd); //凡是在外面使用ep的,都要上鎖,這裏等着被鎖吧
bzero(&servaddr, sizeof(servaddr));//清零客戶端地址結構體
servaddr.sin_family = AF_INET;//IPv4類型地址
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//主機本地任意IP地址
servaddr.sin_port = htons(8888);//綁定端口號
bind(listen_fd, (struct sockaddr*) & servaddr, sizeof(servaddr));//將監聽文件描述與IP地址綁定
listen(listen_fd, 20);//監聽廣播(查看是否有客戶端發出連接請求)
}
int Socket::run(int ffdd) {
struct sockaddr_in cliaddr;
socklen_t clilen = sizeof(cliaddr);
int connfd = accept(listen_fd, (struct sockaddr*) & cliaddr, &clilen);
ep->Epoll_add(connfd); //同樣,鎖上
}
//RecvDate.h
#pragma once
#include "Abstract_Front.h"
#include "PacketBase.h"
#include "Epoll.h"
//處理從客戶端收到的消息
class RecvData :
public Abstract_Front
{
private:
PacketBase* packetbase;
Epoll* ep;
public:
RecvData(Epoll* ep);
int run(int ffdd);
};
//RecvDate.cpp
#include "RecvData.h"
RecvData::RecvData(Epoll* ep) {
this->ep = ep;
this->set_fd(-1);
}
//場景類實地操作時先將fd配置爲接入中控的
int RecvData::run(int ffdd)
{
packetbase = new PacketBase();
int n = read(ffdd, packetbase->getData(), 1024);
if (n <= 0) {
ep->Epoll_del(ffdd);
close(ffdd);
cout << "A Client Close!" << endl;
}
packetbase->setSize(n); //我看這一步可以併到解包裏面去
packetbase->unpack();
if (packetbase->getHead()->funcId == 0x30) { //收到登記包
string temp = packetbase->getBody();
fds[temp] = ffdd;
}
else if(packetbase->getHead()->funcId == 0x31){ //如果是中轉包(這裏默認中轉給DB服務器或Front服務器)
int to_fd = fds["DB"]; //形式還是要走一下的,通過容器表來定位fd
send(to_fd, packetbase->getData(), packetbase->getSize(),0);
}
else if (packetbase->getHead()->funcId == 0x32) {
int to_fd = fds["FTP"]; //形式還是要走一下的,通過容器表來定位fd
send(to_fd, packetbase->getData(), packetbase->getSize(),0);
}
else {
cout << "無人認領包" << endl;
}
}
//場景類
#include"Epoll.h"
#include"Socket.h"
#include"RecvData.h"
int main() {
Epoll* ep = new Epoll();
Socket* sc = new Socket(ep);
RecvData* rc = new RecvData(ep);
ep->setFrontHead(sc);
sc->setNextHandle(rc);
while (1) {
int nevent = ep->Epoll_Wait();
ep->Epoll_run(nevent);
}
}
後置服務器前奏
後置服務器用了很多動態庫,不然代碼量會有點大,所以還沒測好。