循环服务器TCP

服务器代码

cpp 复制代码
/*
 * ============================================================================================
 * 文件名称:server.c
 * 创建者: mf
 * 创建日期:2024年05月20日
 * 描述:此程序实现了一个简易的TCP服务器,监听8888端口,接受客户端连接后,每隔一秒向客户端发送当前时间,
 *       直到写入操作失败或客户端断开连接。服务器通过忽略SIGPIPE信号来处理潜在的管道错误。
 * ============================================================================================
 */
#include <stdio.h>          // 标准输入输出定义
#include <sys/types.h>      // 基本系统数据类型定义
#include <sys/socket.h>     // 套接字编程相关函数声明
#include <netinet/in.h>     // 网络地址结构体定义
#include <netinet/ip.h>     // IP协议头文件
#include <arpa/inet.h>      // 地址转换函数声明
#include <unistd.h>         // 通用Unix函数声明,如close()
#include <signal.h>         // 信号处理函数声明
#include <time.h>           // 时间函数声明

#define PORT 8888           // 监听端口号

int main(int argc, char *argv[]) 
{
    int ret = -1;           // 返回值变量
    int sfd = -1;           // 服务器套接字描述符
    int cfd = -1;           // 客户端连接套接字描述符
    int opt = 1;            // 选项值,用于设置套接字选项

    // 创建TCP套接字
    sfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sfd == -1) 
    {
        perror("socket");   // 错误处理
        return -1;
    }

    // 设置套接字选项,允许端口重用
    ret = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if (ret == -1) 
    {
        perror("setsockopt");
        return -1;
    }

    // 服务器地址结构体初始化
    struct sockaddr_in adds, addc;
    memset(&adds, 0, sizeof(adds));  // 清零
    adds.sin_family = AF_INET;       // 协议族为IPv4
    adds.sin_port = htons(PORT);     // 设置监听端口
    adds.sin_addr.s_addr = INADDR_ANY; // 监听所有IP地址

    // 绑定套接字到指定地址和端口
    ret = bind(sfd, (struct sockaddr*)&adds, sizeof(adds));
    if (ret == -1)
    {
        perror("bind");
        return -1;
    }

    // 开始监听,最多挂起10个连接请求
    ret = listen(sfd, 10);
    if (ret == -1) 
    {
        perror("listen");
        return -1;
    }

    // 忽略SIGPIPE信号,避免因写入已关闭的连接而退出
    signal(SIGPIPE, SIG_IGN);

    printf("等待客户端在端口 %d 上的连接...\n", PORT);

    // 循环等待并接受客户端连接
    while (1) 
    {
        socklen_t addrlen = sizeof(addc);
        cfd = accept(sfd, (struct sockaddr*)&addc, &addrlen);
        if (cfd == -1) 
        {
            perror("accept");
            continue; // 出错则继续等待下一个连接
        }

        // 打印客户端连接信息
        printf("连接来自 IP: %s 端口: %d\n", inet_ntoa(addc.sin_addr), ntohs(addc.sin_port));

        // 向客户端发送当前时间,每秒一次
        while (1) 
        {
            time_t tm = time(NULL); // 获取当前时间
            sprintf(buf, "%s", ctime(&tm)); // 将时间转换为字符串
            ret = write(cfd, buf, strlen(buf)); // 发送给客户端
            if (ret == -1) 
            {
                perror("write"); // 写入错误处理
                break; // 发生错误则跳出循环
            }
            sleep(1); // 暂停一秒
        }

        // 客户端连接结束,关闭连接套接字
        close(cfd);
        printf("客户端已断开连接。等待新连接...\n");
    }

    // 关闭服务器套接字,尽管这里可能永远不会执行到
    close(sfd);
    return 0;
}

客户端代码

cpp 复制代码
/*
 * ============================================================================================
 * 文件名称:client.c
 * 创建者: mf
 * 创建日期:2024年05月20日
 * 描述:此程序实现了一个简易的TCP客户端,用于连接指定IP地址和端口的服务器(此处为192.168.110.6:8888),
 *       并不断从服务器接收数据,打印至标准输出。若连接失败或读取数据时发生错误,则程序将终止。
 * ============================================================================================
 */
#include <stdio.h>          // 标准输入输出函数库
#include <sys/types.h>      // 基本系统数据类型定义
#include <sys/socket.h>     // 套接字编程所需函数声明
#include <netinet/in.h>     // 网络地址结构体定义
#include <arpa/inet.h>      // 地址转换函数声明
#include <unistd.h>         // 提供了POSIX操作系统API中的函数声明,如close()

#define IP   "192.168.110.6" // 服务器IP地址
#define PORT 8888           // 服务器端口号

int main(int argc, char *argv[]) 
{
    int fd = -1;           // 客户端套接字描述符
    int ret = -1;          // 函数返回值
    char buf[32] = {0};     // 用于接收服务器数据的缓冲区
    int opt = 1;           // 设置套接字选项的参数

    // 创建TCP套接字
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1)
    {
        perror("socket");   // 错误处理
        return -1;
    }

    // 设置套接字选项,允许端口重用(注意:此选项在客户端通常不是必须的)
    ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if (ret == -1) 
    {
        perror("setsockopt");
        return -1;
    }

    // 服务器地址结构体初始化
    struct sockaddr_in adds;
    memset(&adds, 0, sizeof(adds));  // 清零
    adds.sin_family = AF_INET;       // 协议族为IPv4
    adds.sin_port = htons(PORT);     // 设置服务器端口
    adds.sin_addr.s_addr = inet_addr(IP); // 设置服务器IP地址

    // 尝试连接服务器
    ret = connect(fd, (struct sockaddr*)&adds, sizeof(adds));
    if (ret == -1) 
    {
        perror("connect");
        return -1;
    }

    // 连接成功,开始循环读取服务器发送的数据
    printf("已连接到服务器,开始接收数据...\n");
    while(1) 
    {
        // 读取服务器发送的数据
        ret = read(fd, buf, sizeof(buf));
        if (ret == -1)
        {
            perror("read");  // 读取错误处理
            return -1;
        } else if (ret == 0) 
        {
            // 对端关闭连接,read返回0
            printf("服务器已关闭连接。\n");
            return -1;
        } else 
        {
            // 打印接收到的数据
            printf("%s", buf);
        }
    }

    // 正常情况下不会执行到此处,但为了规范依然关闭套接字
    close(fd);
    return 0;
}
相关推荐
上海运维Q先生24 分钟前
面试题整理17----K8s中request和limit资源限制是如何实现的
服务器·云原生·kubernetes
Tester_孙大壮42 分钟前
了解智能运维
运维
码农君莫笑2 小时前
《信管通低代码信息管理系统开发平台》Windows环境安装说明
服务器·数据库·windows·低代码·c#·bootstrap·.netcore
fnd_LN2 小时前
Linux文件目录 --- mkdir命令,创建目录,多级目录,设置目录权限
linux·运维·服务器
会飞的土拨鼠呀2 小时前
Flannel是什么,如何安装Flannel
运维·云原生·kubernetes
木与子不厌2 小时前
微服务自定义过滤器
运维·数据库·微服务
行思理2 小时前
Linux 下SVN新手操作手册
linux·运维·svn
沛沛老爹3 小时前
CI/CD是什么?
运维·git·ci/cd
IPdodo全球网络服务4 小时前
如何通过TikTok引流到私域流量池
运维·服务器·网络
手心里的白日梦4 小时前
网络层协议--ip协议
网络·网络协议·tcp/ip