Giter VIP home page Giter VIP logo

30daymakecppserver's Introduction

👋Hi there, I'm Yuesong Feng, a database kernel developer

  • 🎓 Working in Dameng since 2023
  • ⌨️ Worked on 2022 OceanBase Database Competition
  • 💻 Worked in enmotech (Intern) 2022
  • 🔭 Worked in Alibaba (Intern) 2021
  • 🌱 Worked in Tencent (Intern) 2020
  • ✨ Master of Computer Science, The University of Hong Kong, 2021 - 2023
  • 👯 Bachelor of Computer Science and Technology, Sichuan University, 2017 - 2021
  • ⚡ Fun fact: Enjoying playing the piano and classical guitar

yuesong-feng's GitHub stats

30daymakecppserver's People

Contributors

allinsds avatar hjhj111 avatar llthomas avatar retroactively avatar young-flash avatar yuesong-feng avatar yukunj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

30daymakecppserver's Issues

Socket析构close(fd)返回-1

我尝试打印出socket析构时候close(fd)的结果,发现它总是返回-1。
然后我在connection 读取缓冲区完毕后close(fd),这里就返回0正常关闭了套接字。

然后有时我还会遇到epoll add error: bad file descriptor的问题,这也许也和之前没有close有关吗?

关于 day15

image

image

你好,请问我已经选择了linux 系统,但是编译还是会报错,似乎Vhannel 类没有被定义?

请问如何解决 Day12 中的 exit code 139?

你在代码中也写了这个注释,如果连接已断开调用 close 是正常的,但是如果 delete 连接会导致 segment fault,可能原因大概率是 threadpool 任务未执行完去访问一个已被释放了的无效内存地址
我想的是等待任务执行完之后再 delete 但是不知道怎么修改好

还可以添加分发器、拦截器

在收到client请求后,还可以添加分发器Dispatcher,可以根据对client不同的code进行业务分发。而对每个分发器后提交给具体的business处理时,还可以加上拦截器,添加一个拦截器链条。

day02的小小问题

在客户端进行ctrl+c之后,在服务端显示了两条信息,一条是读取字节数等于0的情况,一条则是读取字节数为-1的情况,有哪位大佬知道嘛
image

What is the point of ThreadPool?

On Day 10 when the thread pool was first introduced, it made sense as we were pushing individual handler functions into the task queue (so thread pool achieves dynamic allocation of tasks). However, from Day 11 on, only EventLoop::loop() would be pushed into the queue, and EventLoop::loop() takes care of a (sub/main)-reactor and never returns in normal case (fixed therefore once allocated). As the number of threads is the same as the number of EventLoop objects, the effect is basically a one-to-one allocation of EventLoops to threads. With such being the case, why not just assign an EventLoop per thread?

Day2 问题求助

server的char buf[1024]改成char buf[2048]后server返回的消息会间隔一次,求教这是为什么

kyk@ubuntu:~/web_server/d2$ ./client 
9
message from server: 9
8
message from server: 
7
message from server: 8
6
message from server: 
5
message from server: 7
4
message from server: 
3
message from server: 6
2
message from server: 
1
message from server: 5

server.cpp

#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>

void errif(bool condition, const char *errmsg)
{
    if (condition)
    {
        perror(errmsg);
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char const *argv[])
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    errif(sockfd == -1, "sock creat error");

    struct sockaddr_in serv_addr;
    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serv_addr.sin_port = htons(8888);

    errif(bind(sockfd, (sockaddr *)&serv_addr, sizeof(serv_addr)) == -1, "sock bind error");

    errif(listen(sockfd, SOMAXCONN) == -1, "sock listen error");

    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_len = sizeof(clnt_addr);
    bzero(&clnt_addr, sizeof(clnt_addr));

    int clnt_sockfd = accept(sockfd, (sockaddr *)&clnt_addr, &clnt_addr_len);
    errif(clnt_sockfd == -1, "sock accpt error");
    printf("new client fd %d! IP: %s Port: %d\n", clnt_sockfd, inet_ntoa(clnt_addr.sin_addr), ntohs(clnt_addr.sin_port));

    while (true)
    {
        char buf[2048];
        bzero(&buf, sizeof(buf));
        ssize_t read_size = read(clnt_sockfd, buf, sizeof(buf));
        if (read_size > 0)
        {
            printf("message from client fd %d: %s\n", clnt_sockfd, buf);
            write(clnt_sockfd, buf, sizeof(buf));
        }
        else if (read_size == 0)
        {
            printf("client fd %d disconnected\n", clnt_sockfd);
            close(clnt_sockfd);
            break;
        }
        else if (read_size == -1)
        {
            close(clnt_sockfd);
            errif(true, "socket read error");
        }
    }
    close(sockfd);
    return 0;
}

client.cpp

#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>

void errif(bool condition, const char *errmsg)
{
    if (condition)
    {
        perror(errmsg);
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char const *argv[])
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    errif(sockfd == -1, "sock creat error");

    struct sockaddr_in serv_addr;
    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serv_addr.sin_port = htons(8888);

    errif(connect(sockfd, (sockaddr *)&serv_addr, sizeof(serv_addr)) == -1, "connect error");

    while (true)
    {
        char buf[1024];
        bzero(&buf, sizeof(buf));                              //清空缓冲区
        scanf("%s", buf);                                      //从键盘输入要传到服务器的数据
        ssize_t write_bytes = write(sockfd, buf, sizeof(buf)); //发送缓冲区中的数据到服务器socket,返回已发送数据大小
        if (write_bytes == -1)
        { // write返回-1,表示发生错误
            printf("socket already disconnected, can't write any more!\n");
            break;
        }
        bzero(&buf, sizeof(buf));                            //清空缓冲区
        ssize_t read_bytes = read(sockfd, buf, sizeof(buf)); //从服务器socket读到缓冲区,返回已读数据大小
        if (read_bytes > 0)
        {
            printf("message from server: %s\n", buf);
        }
        else if (read_bytes == 0)
        { // read返回0,表示EOF,通常是服务器断开链接,等会儿进行测试
            printf("server socket disconnected!\n");
            break;
        }
        else if (read_bytes == -1)
        { // read返回-1,表示发生错误,按照上文方法进行错误处理
            close(sockfd);
            errif(true, "socket read error");
        }
    }

    return 0;
}

Day 10 的问题

客户端连接的时候 服务端报了这个错误 epoll add error: File exists

为什么认为EAGAIN为非阻塞IO的结束条件?

} else if(bytes_read == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))){//非阻塞IO,这个条件表示数据全部读取完毕

我在测试大文件(84M)的传输时,发现使用这个条件会造成数据丢失。
推测OS将大文件分成了小包,而这些小包之间不一定是连续就绪的,中间会存在errno==EAGAIN的情况

查询man recv也没有相关的说明。所以希望您能详细说明一下其中的问题,谢谢!

催更

快更新呀快更新呀

Epoll 类构造函数中 events 初始化问题

Epoll::Epoll() : epfd(-1), events(nullptr){
epfd = epoll_create1(0);
errif(epfd == -1, "epoll create error");
events = new epoll_event[MAX_EVENTS];
bzero(events, sizeof(events) * MAX_EVENTS);
}

第 12 行中初始化 epoll_event 数组时 sizeof 预算符的参数出错了, events 是指针类型,应该改成 epoll_event 或者 *events;[day04, day11) 中均有此问题

代码存在内存泄露的问题

raii是c++最重要的资源管理方案咋不用呢,里面全是裸指针。
从第四课封装类开始,到后面第15课都存在内存泄露的问题。最明显的就是new的InetAddress和Socket到关闭了客户端连接还没有释放。

有没有可能未来增加 GUI的支持?

首先非常感谢楼主的更新. So far very solid.

想问问之后可不可能在Client端增加GUI的支持, 比如QT


同时想请问这个Repo的进度是和你另一个库Pine 一致的吗? 即这里的Day Max即是现在Pine的Head?

day 05 的文档中一处描述有误

epoll中的data其实是一个枚举类型 :data 应该是联合类型(union)吧,详细说明在《C++ Primer(第五版)》第十九章第六节

day09 的 client.cpp 文件

day09client.cpp 代码中,读取服务器端返回的数据时,为什么将判断 read 返回值是否为 -1 的情况删除了?

在day 13中,是如何工程化的?

由于没有学习过这些插件,从day12 到day13 这之间已经不知道发生了什么。
cmake,clang这两者是需要去学习嘛?cmakelists.txt 是手写出来的嘛?
以及format,cpplint,clang-tidy 这些插件是怎么添加和运作的?

关于day08中Connection对象的删除问题

我看到的代码逻辑是: Connection对象里调用Server里的删除Connection对象的方法, 这样不就是在Connection对象的方法里删除其自身吗? 这样不会有问题吗?

还是我理解错了?

day01无法显示

day01中我用FinalShell连接云服务器的Ubuntu系统,在运行server文件后看不到服务器的响应,新建终端运行client文件也无响应。请问是需要更改端口号和地址吗?

关于day12中的Server类的构造函数

Server::Server(EventLoop _loop) : mainReactor(_loop), acceptor(nullptr){
acceptor = new Acceptor(mainReactor);
std::function<void(Socket
)> cb = std::bind(&Server::newConnection, this, std::placeholders::_1);
acceptor->setNewConnectionCallback(cb);

int size = std::thread::hardware_concurrency();
thpool = new ThreadPool(size);
for(int i = 0; i < size; ++i){
    subReactors.push_back(new EventLoop());//这里不明白,每次new EventLoop对象都会构建一个新的红黑树,
}

for(int i = 0; i < size; ++i){
    std::function<void()> sub_loop = std::bind(&EventLoop::loop, subReactors[i]);//每个subReactors都需要执行loop函数,即真正的业务逻辑
    thpool->add(sub_loop);
}

}

for(int i = 0; i < size; ++i){
subReactors.push_back(new EventLoop());//这里不明白,每次new EventLoop对象都会构建一个新的红黑树,
}

请问这个部分每次创建新的EventLoop都会创建新的epoll红黑树,请问为什么需要创建多个红黑树,多线程多Reactor模型中sub-Reactor也需要自己的epoll红黑树吗?

day08 Server类中newConnection函数缺失逻辑

InetAddress *clnt_addr = new InetAddress();
Socket *clnt_sock = new Socket(serv_sock->accept(clnt_addr));
clnt_sock->setnonblocking();
Connection connect = new Connection(eventLoop, clnt_sock);
printf("Connect fd= %d \n", clnt_sock->getFd());
std::function<void (Socket
)> cb = std::bind(&Server::deleteConection, this, std::placeholders::_1);
connect->setDeleteConnectionCallback(cb);
connections.emplace(clnt_sock->getFd(), connect);
需要accept获取客户端地址

day2有点问题

我的运行环境是vmware运行的centos7,使用make编译时显示“printf在此作用域中尚未声明”与“scanf在此作用域中尚未声明”,后在client.cpp与server.cpp中加入#include 后问题解决。

催更

博主为什么不发新的了?

day1 请问各位大佬使用 day2 的错误提示添加到 day1 的 server 中是否有遇到 bind error:Invalid argument ?bind 是在 listen 前调用啊,在腾讯云 ubuntu20.04 上写的,有大佬知道怎么解决 ?

#include <cstdio>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <cstdlib>

using namespace std;

void errif(bool condition, const char *errmsg){
    if(condition){
        perror(errmsg);
        exit(EXIT_FAILURE);
    }
}

int main() {
	// 创建 socket
	int sockfd = socket(AF_INET, SOCK_STREAM, 0); 

	// 声明专用sockaddr_in socket 地址
	struct sockaddr_in serv_addr;
	errif(sockfd == -1, "sockfd create error");
	// 初始化地址信息为 0
	bzero(&serv_addr, sizeof(serv_addr));
	// socket地址中 family 表示 IP 地址类型
	serv_addr.sin_family = AF_INET;
	// socket 地址中, sin_addr表示 IPv4 地址结构体,用网络字节序表示
	// inet_addr将用于十进制字符串表示转化为网络字节序整数表示
	serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	// socket 地址中, sin_port表示端口号,要用网络字节序表示
	// htons: host to network short
	serv_addr.sin_port = htons(8888);

	// 将 server 地址和文件描述符绑定
	bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr));
	errif(bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) == -1, "bind failed");
	// 监听 socket 端口
	listen(sockfd, SOMAXCONN);
	errif(listen(sockfd, SOMAXCONN) == -1, "listen failed");
	// 声明客户端地址
	struct sockaddr_in clnt_addr;
	// 计算客户端地址所占大小
	socklen_t clnt_addr_len = sizeof(clnt_addr);
	// 初始化客户端地址
	bzero(&clnt_addr, sizeof(clnt_addr));

	// 客户端文件描述符为接收
	int clnt_sockfd = accept(sockfd, (sockaddr*)&clnt_addr, &clnt_addr_len);
	
	printf("new client fd%d! IP:%s Port: %d\n", clnt_sockfd, inet_ntoa(clnt_addr.sin_addr), ntohs(clnt_addr.sin_port));
	return 0;
}

关于不同天数的文件提醒

目前在day1的代码基础上进行day2的改进时,由于没删除原来编译的client以及server,编译后的结果仍旧是day1的结果。linux编译新手,是否是编译的问题? 删除编译好的文件重新编译,或者是在新文件夹中编译就没有问题,这个可以稍微提醒一下~

关于day12中server类构造函数的

`Server::Server(EventLoop _loop) : mainReactor(_loop), acceptor(nullptr){
acceptor = new Acceptor(mainReactor);//主反应堆,用来处理连接事件
std::function<void(Socket
)> cb = std::bind(&Server::newConnection, this, std::placeholders::_1);
acceptor->setNewConnectionCallback(cb);

int size = std::thread::hardware_concurrency();
thpool = new ThreadPool(size);
for(int i = 0; i < size; ++i){
    subReactors.push_back(new EventLoop());
}

for(int i = 0; i < size; ++i){
    std::function<void()> sub_loop = std::bind(&EventLoop::loop, subReactors[i]);
    thpool->add(sub_loop);
}

}
`
std::function<void()> sub_loop = std::bind(&EventLoop::loop, subReactors[i]);请问这行代码中loop是无参函数,为什么需要绑定subReactors?

day5中返回Channel指针的poll函数一些不明白的地方

int nfds = epoll_wait(epfd, events, MAX_EVENTS, timeout);
errif(nfds == -1, "epoll wait error");
for(int i = 0; i < nfds; ++i){
Channel *ch = (Channel*)events[i].data.ptr;
ch->setRevents(events[i].events);
activeChannels.push_back(ch);
}

作者你好,请问下有关于day5中的这两句代码的意思,我在自己电脑上进行了类似的测试,有些地方搞不明白
比如说此时返回的fd = 3,我电脑上虚拟机上运行的ptr的值为0x3,47行令一个Channel *ch = ptr 就是让ch指向0x3这个地址,接着第48行对ch指向的revents进行赋值。我的理解是在0x3作为起始地址的基础上,将revents相应位置0x13进行赋值。但是这样不会产生段错误吗?这样赋值是为了在union的内存后面再腾出4B来存放revents吗?(纠结在这里,还没有看server的代码)
求解答~谢谢

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.