TCP链接测试分析(linux命令分析)

#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define MAX_EVENTS 10
#define BUFFER_SIZE 1024
#define PORT 8888

// 设置非阻塞套接字
void set_nonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

int main() {
    int listen_sock, epoll_fd;
    struct epoll_event ev, events[MAX_EVENTS];
    
    // 创建监听套接字
    if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置地址重用
    int opt = 1;
    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(PORT);

    if (bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        perror("bind");
        close(listen_sock);
        exit(EXIT_FAILURE);
    }

    if (listen(listen_sock, SOMAXCONN) == -1) {
        perror("listen");
        close(listen_sock);
        exit(EXIT_FAILURE);
    }

    // 创建epoll实例
    epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        perror("epoll_create1");
        close(listen_sock);
        exit(EXIT_FAILURE);
    }

    // 添加监听套接字到epoll
    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
        perror("epoll_ctl");
        close(listen_sock);
        close(epoll_fd);
        exit(EXIT_FAILURE);
    }

    printf("Server started on port %d\n", PORT);

    while (1) {
        int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        if (nfds == -1) {
            perror("epoll_wait");
            break;
        }

        for (int i = 0; i < nfds; ++i) {
            // 处理新连接
            if (events[i].data.fd == listen_sock) {
                struct sockaddr_in client_addr;
                socklen_t addrlen = sizeof(client_addr);
                int conn_sock = accept(listen_sock, 
                                    (struct sockaddr*)&client_addr,
                                    &addrlen);
                if (conn_sock == -1) {
                    perror("accept");
                    continue;
                }

                set_nonblocking(conn_sock);
                ev.events = EPOLLIN | EPOLLET; // 边缘触发模式
                ev.data.fd = conn_sock;
                if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) {
                    perror("epoll_ctl");
                    close(conn_sock);
                }
                printf("New connection: %d\n", conn_sock);
            } 
            // 处理客户端数据
            else {
                char buffer[BUFFER_SIZE];
                ssize_t bytes_read;
                
                while ((bytes_read = read(events[i].data.fd, buffer, BUFFER_SIZE)) > 0) {
                    printf("Received %zd bytes from %d\n", bytes_read, events[i].data.fd);
                    write(events[i].data.fd, buffer, bytes_read); // 回显数据
                }

                if (bytes_read == 0 || (bytes_read == -1 && errno != EAGAIN)) {
                    printf("Connection closed: %d\n", events[i].data.fd);
                    close(events[i].data.fd);
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
                }
            }
        }
    }

    close(listen_sock);
    close(epoll_fd);
    return 0;
}

这里主要简单实现了一下TCP的链接,我这里链接的端口是8888,我采用两个客户端去链接这个服务器。

查看这个端口我们可以发现这个程序的进程是389853,采用的IPV4协议,还能看到FD的编号。可以看到这里有一个sockfd专门是用来监听的。还有两个fd是可以看到状态时ESTABLISHED建立链接的。

再上一步中我看得到了8888端口的DIP进程号,通过TOP -p PID查看资源使用情况。主要看VIRT表示虚拟内存,RES表示物理内存,SHR表示贡献内存,此时CPU的占用率为0,CPU累计时间也为0.进程的名称为epoll_server。

虚拟内存(VIRT)组成

- 包含进程所有地址空间:代码段、数据段、堆、栈、共享库、内存映射文件

+-------------------+-------+
| 内存区域          | 大小  |
+-------------------+-------+
| 程序代码段        | ~500K | 
| 共享库映射        | ~1408K| <-- SHR列的值
| 堆栈预分配空间    | ~800K |
+-------------------+-------+

物理内存(RES)组成仅统计实际装入RAM的页帧:

+-------------------+-------+
| 内存区域          | 大小  |
+-------------------+-------+
| 共享库已加载部分  | ~1408K| 
| 程序私有内存      | ~0K   | <-- RES-SHR=0
+-------------------+-------+ 

这里可以发现实际上虚拟内存比实际内存多了堆区的预分配空间,和程序代码段。

PS:堆空间预分配:通过 malloc() 申请的虚拟内存不会立即转为物理内存,直到实际写入数据

netstat得到的信息并不多,lsof -i :8888中得到的信息更加多一些

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值