坐牢第二十九天

思维导图

 作业

作业: 运行1个服务器和2个客户端 实现效果: 服务器和2个客户端互相聊天,服务器和客户端都需要使用select模型去实现 服务器要监视2个客户端是否连接,2个客户端是否发来消息以及服务器自己的标准输入流 客户端要监视服务器是否发来消息以及客户端自己的标准输入流 在不开线程的情况下,实现互相聊天

 服务器代码:

#include <myhead.h>
#define SER_PORT 6666          // 端口号
#define SER_IP "192.168.0.151" // 服务器ip地址
// 添加函数
void insert_newfd(int *newfd_arr, int *len, int newfd)
{
    newfd_arr[*len] = newfd;
    (*len)++;
}
// 查询函数
int find_newfd(int *newfd_arr, int len, int newfd)
{
    for (int i = 0; i < len; i++)
    {
        if (newfd_arr[i] == newfd)
        {
            return i;
        }
    }
    return -1;
}
// 删除函数
void remove_newfd(int *newfd_arr, int *len, int newfd)
{
    int tar = find_newfd(newfd_arr, *len, newfd);
    if (tar == -1)
    {
        return;
    }
    int i = -1;
    for (i = tar; i < *len - 1; i++)
    {
        newfd_arr[i] = newfd_arr[i + 1];
    }
    (*len)--;
}

/**********************主函数*********************/
int main(int argc, char const *argv[])
{
    // 1.创建套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    // 参数1表示ipv4网络通信
    // 参数2表示tcp通信方式
    // 参数3表示默认使用一个协议
    if (sfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("socket succes,sfd=%d\n", sfd);
    // 将端口号快速重用
    int reuse = 1;
    if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口快速重用成功\n");
    // 2.为套接字绑定ip地址和端口号
    // 2.1填充地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;                // 通信域
    sin.sin_port = htons(SER_PORT);          // 端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP); // ip地址
    // 2.2绑定地址
    if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");
    // 3.将套接字设置成被动监听状态
    if (listen(sfd, 128) == -1)
    {
        perror("listen error");
        return -1;
    }
    printf("listen success\n");
    // 4.阻塞等待客户端连接请求
    // 4.1定义变量用于接受客户端的信息
    struct sockaddr_in cin;          // 用于接收地址信息
    socklen_t addrlen = sizeof(cin); // 用于接受长度

    // 创建readfds集合
    fd_set readfds;
    FD_ZERO(&readfds);     // 初始化
    FD_SET(sfd, &readfds); // 套接字添加进描述符集合
    FD_SET(0, &readfds);   // 把标准输入流添加进描述符集合
    // 套接字集合数组
    int newfd_arr[128];
    // 数组大小表示客户端连接个数
    int newfd_len = 0;
    while (1)
    {
        fd_set temp = readfds;
        select(FD_SETSIZE, &temp, 0, 0, 0);
        //激活标准输入流
        if (FD_ISSET(0, &temp))
        {
            // 标准输入流激活,把消息发送给所有客户端
            char buf[128] = "";
            read(0, buf, sizeof(buf));
            buf[strlen(buf) - 1] = 0;
            for (int i = 0; i < newfd_len; i++)
            {
                int newfd = newfd_arr[i];
                send(newfd, buf, strlen(buf), 0);
                printf("发送成功\n");
            }
        }
        //激活套接字
        if (FD_ISSET(sfd, &temp) == 1)
        {
            int newfd = accept(sfd, (struct sockaddr *)&cin, &addrlen);
            if (newfd == -1)
            {
                perror("accept error");
                return -1;
            }
            printf("%s;%d:已成功连接\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
            //添加新的客户端套接字到描述符集合
            FD_SET(newfd, &readfds);
            insert_newfd(newfd_arr, &newfd_len, newfd);
        }
        //激活客户端套接字
        for (int i = 0; i < newfd_len; i++)
        {
            int newfd = newfd_arr[i];
            if (FD_ISSET(newfd, &temp))
            {
                // 4.收数据
                char buf[128] = "";
                int res = read(newfd, buf, 128);
                if (res == 0)
                {
                    printf("%s;%d:已断开连接\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
                    FD_CLR(newfd, &readfds);
                    remove_newfd(newfd_arr, &newfd_len, newfd);
                    close(newfd);
                    i--;
                    break;
                }
                printf("%s;%d:发来消息:%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);
                for (int j = 0; j < newfd_len; j++)
                {
                    if (newfd_arr[j]!=newfd)
                    {
                       send(newfd_arr[j], buf, strlen(buf), 0);
                    } 
                }
            }
        }
    }
    close(sfd);
    return 0;
}

客户端代码:

#include <myhead.h>
#define SER_PORT 6666      // 服务器端口
#define SER_IP "192.168.0.151" // 客户端IP地址
#define CLI_PORT 8888      // 客户端端口
#define CLI_IP "192.168.0.151" // 客户端IP地址
/***********************主函数**************************/
int main(int argc, char const *argv[])
{
    // 1.创建用于通信的套接字文件描述符
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    if (cfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("cfd=%d\n", cfd); // 3
    // 将端口号快速重用
    int reuse = 1;
    if (setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口快速重用成功\n");
    // 2.绑定ip地址和端口号
    // 2.1填充地址信息结构体
    struct sockaddr_in cin;
    cin.sin_family = AF_INET;                // 通信域
    cin.sin_port = htons(CLI_PORT);          // 端口号
    cin.sin_addr.s_addr = inet_addr(CLI_IP); // ip地址
    // 3.连接到服务器
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;                // 通信域
    sin.sin_port = htons(SER_PORT);          // 服务器端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP); // 服务器ip地址
    // 3.2连接服务器
    if (connect(cfd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
    {
        perror("connect error");
        return -1;
    }
    printf("连接服务器成功\n");
    fd_set readfds;
    FD_ZERO(&readfds);//
    FD_SET(cfd, &readfds);//套接字添加进描述符集合
    FD_SET(0, &readfds); // 把标准输入流添加进
    // 4.数据收发
    char buf[128] = "";
    while (1)
    {
        fd_set temp = readfds;
        select(FD_SETSIZE, &temp, 0, 0, 0);
        //激活套接字
        if (FD_ISSET(cfd, &temp))
        {
            char buf[128] = "";
            recv(cfd, buf, sizeof(buf), 0);
            printf("%s\n", buf);
        }
        //激活标准输入流
        if (FD_ISSET(0, &temp))
        {
            char buf[128] = "";
            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf) - 1] = 0;
            if (strcmp(buf, "quit") == 0)
            {
                break;
            }
            // 将数据发送给服务器
            send(cfd, buf, strlen(buf), 0);
        }
    }
    // 5.关闭套接字
    close(cfd);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值