首页 > 编程语言 >白骑士的C++教学实战项目篇 4.3 多线程网络服务器

白骑士的C++教学实战项目篇 4.3 多线程网络服务器

时间:2024-07-18 19:56:01浏览次数:10  
标签:多线程 4.3 int C++ 服务器 接字 include 客户端

系列目录

上一篇:白骑士的C++教学实战项目篇 4.2 学生成绩管理系统

        在这一节中,我们将实现一个多线程网络服务器项目,通过该项目,我们将学习套接字编程的基础知识以及如何使用多线程处理并发连接。此外,我们还将实现一个简单的客户端来与服务器进行通信。

项目简介

        多线程网络服务器是一种可以同时处理多个客户端请求的服务器。通过多线程技术,我们可以使服务器在处理一个客户端请求时,不阻塞其他客户端的连接请求。本项目将涉及以下内容:

  • 套接字编程基础
  • 多线程处理
  • 客户端与服务器的实现

套接字编程基础

        套接字(Socket)是网络编程的基础,它提供了在网络上进行数据通信的机制。在C++中,我们可以使用POSIX标准库(如 ‘sys/socket.h‘、‘arpa/inet.h‘ 等)来进行套接字编程。下面是一个基本的服务器和客户端通信的示例。

服务器端代码

#include <iostream>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#include <thread>


const int PORT = 8080;


void handleClient(int clientSocket) {
    char buffer[1024] = {0};

    read(clientSocket, buffer, 1024);

    std::cout << "Message from client: " << buffer << std::endl;

    send(clientSocket, buffer, strlen(buffer), 0);
    close(clientSocket);
}


int main() {
    int serverFd, clientSocket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);

    // 创建服务器端套接字
    if ((serverFd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");

        exit(EXIT_FAILURE);
    }

    // 设置套接字选项
    if (setsockopt(serverFd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt failed");

        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // 绑定套接字
    if (bind(serverFd, (struct sockaddr*)&address, sizeof(address)) < 0) {
        perror("bind failed");

        exit(EXIT_FAILURE);
    }

    // 监听连接
    if (listen(serverFd, 3) < 0) {
        perror("listen failed");

        exit(EXIT_FAILURE);
    }

    while (true) {
        std::cout << "Waiting for connections...\n";

        if ((clientSocket = accept(serverFd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) {
            perror("accept failed");

            exit(EXIT_FAILURE);
        }

        std::thread clientThread(handleClient, clientSocket);

        clientThread.detach(); // 将线程分离,以便独立运行
    }

    close(serverFd);

    return 0;
}

客户端代码

#include <iostream>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>


const int PORT = 8080;


int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    char* message = "Hello from client";
    char buffer[1024] = {0};

    // 创建客户端套接字
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        std::cerr << "Socket creation error" << std::endl;

        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // 将IP地址转换为二进制格式
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        std::cerr << "Invalid address/ Address not supported" << std::endl;

        return -1;
    }

    // 连接服务器
    if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
        std::cerr << "Connection failed" << std::endl;

        return -1;
    }

    send(sock, message, strlen(message), 0);

    std::cout << "Message sent" << std::endl;

    read(sock, buffer, 1024);

    std::cout << "Message from server: " << buffer << std::endl;

    close(sock);

    return 0;
}

多线程处理

        在上述服务器代码中,我们使用了 ‘std::thread‘ 来为每个客户端连接创建一个新线程,并使用 ‘detach‘ 将其分离,使线程能够独立运行而不阻塞主线程。这种方式可以有效地处理多个客户端的并发连接。

客户端与服务器的实现

        我们已经实现了基本的客户端和服务器通信。为了使系统更健壮,可以考虑添加以下功能:

  • 异常处理:处理各种可能的网络异常,如连接失败、读写失败等。
  • 日志记录:记录客户端连接和断开时间、传输的数据等信息。
  • 并发控制:限制同时连接的客户端数量,防止服务器过载。

完整代码示例

#include <iostream>

#include <unistd.h>

#include <netinet/in.h>

#include <string.h>
#include <thread>
#include <vector>
#include <mutex>


const int PORT = 8080;


std::mutex coutMutex;


void handleClient(int clientSocket) {
    char buffer[1024] = {0};

    read(clientSocket, buffer, 1024);

    {
        std::lock_guard<std::mutex> lock(coutMutex);
        std::cout << "Message from client: " << buffer << std::endl;
    }

    send(clientSocket, buffer, strlen(buffer), 0);

    close(clientSocket);
}


int main() {
    int serverFd, clientSocket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);

    // 创建服务器端套接字
    if ((serverFd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");

        exit(EXIT_FAILURE);
    }

    // 设置套接字选项
    if (setsockopt(serverFd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt failed");

        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // 绑定套接字
    if (bind(serverFd, (struct sockaddr*)&address, sizeof(address)) < 0) {
        perror("bind failed");

        exit(EXIT_FAILURE);
    }

    // 监听连接
    if (listen(serverFd, 3) < 0) {
        perror("listen failed");

        exit(EXIT_FAILURE);
    }

    while (true) {
        std::cout << "Waiting for connections...\n";

        if ((clientSocket = accept(serverFd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) {
            perror("accept failed");

            exit(EXIT_FAILURE);
        }

        std::thread clientThread(handleClient, clientSocket);

        clientThread.detach();
    }

    close(serverFd);

    return 0;
}

        通过以上代码,我们实现了一个简单的多线程网络服务器,能够同时处理多个客户端的连接请求。这个项目不仅帮助我们掌握了套接字编程的基础知识,还了解了多线程处理的基本方法。

总结

        多线程网络服务器项目使我们掌握了网络编程和多线程处理的基本知识。通过实践套接字编程和多线程技术,我们可以开发出更高效、更稳定的网络应用程序。

下一篇:白骑士的C++教学附加篇 5.1 C++开发工具​​​​​​​

标签:多线程,4.3,int,C++,服务器,接字,include,客户端
From: https://blog.csdn.net/JeremyTC/article/details/140071375

相关文章

  • c++ primer plus 第16章string 类和标准模板库,16.2.1 使用智能指针
    c++primerplus第16章string类和标准模板库,16.2.1使用智能指针c++primerplus第16章string类和标准模板库,16.2.1使用智能指针文章目录c++primerplus第16章string类和标准模板库,16.2.1使用智能指针16.2.3uniqueptr为何优于autoptr16.2.3unique......
  • c++ primer plus 第16章string 类和标准模板库,16.2.2 有关智能指针的注意事项
    c++primerplus第16章string类和标准模板库,16.2.2有关智能指针的注意事项c++primerplus第16章string类和标准模板库,16.2.2有关智能指针的注意事项文章目录c++primerplus第16章string类和标准模板库,16.2.2有关智能指针的注意事项16.2.2有关智能指针的......
  • C++ 面向对象程序设计 ---- 类2重点
    1.构造函数,代替Init()函数构造函数的特点1.函数名与类名相同2.无返回值,void也不需要写3.对象实例化时,系统会自动调用构造函数4.构造函数可以重载classDate{public://函数名与类名相同,无返回值Date()//函数重载,无参{_year=1;......
  • Java多线程入门
    创建线程的三种方式继承Thread类classMyThreadextendsThread{@Overridepublicvoidrun(){for(inti=0;i<100;i++){System.out.println(getName()+""+i);}}publicstaticvoidmain(String[]args......
  • 多线程三-线程安全之可见性与有序性
    volatile关键字来确保线程间的可见性,可以利用线程可见性在某些场景进行无锁化编程。下载Hotspot源码:官网:https://openjdk.org/左侧菜单,SourceCode下面的Mecurial点击jdk8点击hotspot点击zipvolatile关键字来确保线程间的可见性,可以利用线程可见性在某些场景进行无锁化......
  • OI学习笔记(C++)
    笔记完整版链接参照oi.wiki整理了基础dp的一些笔记:学习笔记+模板(Adorable_hly)(自己结合网络和做题经验总结的,dalao勿喷)第一大板块:DP动态规划适用场景:1.最优化原理:若该问题所包含的子问题解是最优的,就称该问题具有最优子结构,满足最优化原理。2.无后效性:指某一阶段的状......
  • C++异常
    异常描述std::exception该异常是所有标准C++异常的父类。std::bad_alloc该异常可以通过new抛出。std::bad_cast该异常可以通过dynamic_cast抛出。std::bad_typeid该异常可以通过typeid抛出。std::bad_exception这在处理C++程序中无法预期的异......
  • C++文件和流
    要在C++中进行文件处理,必须在C++源代码文件中包含头文件<iostream>和<fstream>。数据类型描述fstream该数据类型通常表示文件流,且同时具有ofstream和ifstream两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。ofstream该数据类型表示输出......
  • C++中的vector对应Java中的什么类型?
    C++中的vector对应Java中的ArrayList类型。‌C++中的vector和Java中的ArrayList都是可变长的数组或数组列表,‌它们具有以下相似特性:‌两者都是动态数组,‌可以根据需要自动增长。‌它们都支持通过索引访问元素,‌并且元素是有序的。‌它们都提供了添加、‌删除和查询元素的方法......
  • 基于SpringBoot的宠物领养系统-07863(免费领源码+开发文档)可做计算机毕业设计JAVA、PHP
    摘 要21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所认识,科学化的管理,使信息存储达到准确、快速、完善,并能提高工作管理效率,促进其发展。论文主要是对宠物领养系统......