循环服务器TCP

服务器代码

/*
 * ============================================================================================
 * 文件名称: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;
}

客户端代码 

/*
 * ============================================================================================
 * 文件名称: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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值