网络编程 — Linux TCP服务端和客户端

1. 服务端

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <iostream>
#include <forward_list>
#include <thread>

class Server {
    constexpr static int DEFAULT_BACKLOG = 10;
public:
    Server(): _listener(-1), _should_exit(false) {}
    ~Server() {
        for (auto it = _threads.begin(); it != _threads.end(); it++) {
            it->join();
        }
    }

    // 判断 server 是否有效
    operator bool() const {
        return _listener != -1;
    }

    // 监听并接受连接
    void listen_and_serve(unsigned short port, int backlog= DEFAULT_BACKLOG) {
        // 创建套接字
        _listener = socket(AF_INET, SOCK_STREAM, 0);
        if (_listener == -1) {
            std::cerr << "cannot create listener\n";
            return;
        }

        // 设置套接字选项
        int value = 1;
        if (setsockopt(_listener, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) == -1) {
            std::cerr << "cannot set option: reuse_address\n";
        }

        // 绑定地址
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(sockaddr_in));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        if (bind(_listener, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
            std::cerr << "bind failed\n";
            return;
        }

        // 监听连接
        if (listen(_listener, backlog) == -1) {
            std::cerr << "listen failed\n";
            return;
        }

        int conn;
        std::cout << "listening ...\n";

        // 接受连接
        while (!_should_exit) {
            conn = accept(_listener, NULL, NULL);
            if (conn == -1) {
                std::cerr << "accept failed\n";
            }

            _threads.push_front(std::thread(Server::handle_connection, conn));
        }
    }

    // 关闭套接字
    void close() {
        _should_exit = true;
        ::close(_listener);
    }

    // 处理连接
    static void handle_connection(int conn) {
        char buf[16] = { 0 };
            int n = recv(conn, buf, sizeof(buf), 0);
            send(conn, buf, n, 0);
            ::close(conn);
    }

private:
    int _listener;
    std::forward_list<std::thread> _threads;
    bool _should_exit;
};

Server server;

// 信号处理器
void handler(int signum) {
    std::cout << "exit ...\n";
    if (server) {
        server.close();
    }
    exit(EXIT_SUCCESS);
}

int main() {
    // 处理 ctrl+c
    signal(SIGINT, handler);

    server.listen_and_serve(6666);
}

 

2. 客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

const char* SERVER_IP = "127.0.0.1";
const short SERVER_PORT = 6666;

int main() {
    int sockfd;
    struct sockaddr_in addr;

    // 创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        printf("socket: %s\n", strerror(errno));
        return 1;
    }

    // 发起连接
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERVER_PORT);
    if (inet_pton(AF_INET, SERVER_IP, &addr.sin_addr) <= 0) {
        printf("inet_pton: %s\n", strerror(errno));
        return 1;
    }

    if (connect(sockfd, (const struct sockaddr*)&addr, sizeof(addr)) < 0) {
        printf("connect: %s\n", strerror(errno));
        return 1;
    }

    // 发送、接收数据
    const char* msg = "hello";
    char buf[16] = { 0 };
    send(sockfd, msg, strlen(msg), 0);
    int n = recv(sockfd, buf, sizeof(buf), 0);
    printf("%s\n", buf);

    close(sockfd);
    return 0;
}

 

网络编程 — Linux TCP服务端和客户端

上一篇:PHP下载远程文件的3种方法以及性能考虑


下一篇:【shell脚本大全-01】统计一个文本文件内有多少行