2024.8.18 大作业

1、udp实现网络聊天室

代码:

/*******************************************/

文件名:chat.h

/*******************************************/

#ifndef CHAT_H
#define CHAT_H
#include <myhead.h>
struct Msg
{
    char type;
    char name[10];
    char languege[128];
};
//绑定服务器
void ser_bind(struct ip_mreqn *imr, struct sockaddr_in *min);
//端口号重新启用
void port_reuse(int sockfd);
//登录界面
void log_menu();
//注册
void log_reg();
//登录
void log_in();
#endif

/*******************************************/

文件名:chat.c

/*******************************************/

#include "chat.h"
//绑定服务器
void ser_bind(struct ip_mreqn *imr, struct sockaddr_in *min)
{
    imr->imr_multiaddr.s_addr = inet_addr("224.1.2.3");  //组播ip
    imr->imr_address.s_addr = inet_addr("192.168.2.91"); //主机ip
    imr->imr_ifindex = 2;                                //网卡编号
    min->sin_family = AF_INET;
    min->sin_port = htons(9999);
    min->sin_addr.s_addr = inet_addr("224.1.2.3");
}
//端口号重新启用
void port_reuse(int sockfd)
{
    //将端口号快速重用
    int reuse = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
    {
        perror("setsockopt error");
        return;
    }
    printf("端口号快速重用成功\n");
}
//登录界面
void log_menu()
{
    printf("/*******************/\n");
    printf("/*****1、登录******/\n");
    printf("/*****2、注册******/\n");
    printf("/*****0、退出******/\n");
    printf("/*******************/\n");
}
//注册
void log_reg()
{
    //账号
    char reg_acc[21] = "";
    //密码
    char reg_pass[21] = "";
    //再次输入密码
    char repass[21] = "";
    while (1)
    {
        printf("请输入注册账号:");
        fgets(reg_acc, sizeof(reg_acc), stdin);
        reg_acc[strlen(reg_acc) - 1] = 0;
        int size = strlen(reg_acc);
        if (size <= 0 || size > 20)
        {
            printf("账号格式错误,请重新输入\n");
            continue;
        }
        else
        {
            break;
        }
    }
    while (1)
    {
        printf("请输入密码:");
        fgets(reg_pass, sizeof(reg_pass), stdin);
        reg_pass[strlen(reg_pass) - 1] = 0;
        int size = strlen(reg_pass);
        if (size < 6 || size > 20)
        {
            printf("密码格式错误,请重新输入\n");
            continue;
        }
        else
        {
            break;
        }
    }
    while (1)
    {
        printf("请重新输入密码:");
        fgets(repass, sizeof(repass), stdin);
        repass[strlen(repass) - 1] = 0;
        if (strcmp(repass, reg_pass) != 0)
        {
            printf("密码不一致,请重新输入\n");
            continue;
        }
        else
        {
            break;
        }
    }
    char buf[60] = "";
    //组合信息
    FILE *fp = fopen("./text.txt", "w");
    if (NULL == fp)
    {
        perror("fopen error");
        return;
    }
    fprintf(fp, "%s %s\n", reg_acc, reg_pass);
    fclose(fp);
    fp = fopen("./text.txt", "r");
    if (NULL == fp)
    {
        perror("fopen error");
        return;
    }
    rewind(fp);
    fread(buf, 1, sizeof(buf), fp);
    fclose(fp);
    //追加形式写入新文件
    int fd = open("./log.txt", O_WRONLY | O_CREAT | O_APPEND, 0664);
    if (fd == -1)
    {
        perror("open error");
        return;
    }
    write(fd, buf, strlen(buf));
    close(fd);
}
//登录
void log_in()
{
    char in_acc[21] = "";
    char in_pass[21] = "";
    //用于读取已注册的信息
    FILE *fp=fopen("./log.txt","r");
    if(NULL==fp){
        perror("fopen error");
        return ;
    }
    while (1)
    {
        printf("请输入登录账号:");
        fgets(in_acc, sizeof(in_acc), stdin);
        in_acc[strlen(in_acc) - 1] = 0;
        fseek(fp, 0, SEEK_SET);
        int len = 2;
        //用于判断退出循环
        int flag = 0;
        //接收数据
        char buf1[22] = "";
        char buf2[22]="";
        while (len == 2)
        {
            len = fscanf(fp,"%s %s\n",buf1,buf2);
            //从所有数据分别比较,符合则退出
            if (strcmp(buf1, in_acc) == 0)
            {
                flag = 1;
                break;
            }
        }
        if (flag == 0)
        {
            printf("账号错误,请重新输入\n");
        }
        else if (flag == 1)
        {
            break;
        }
    }
    while (1)
    {
        printf("请输入登录密码:");
        fgets(in_pass, sizeof(in_pass), stdin);
        in_pass[strlen(in_pass) - 1] = 0;
        fseek(fp, 0, SEEK_SET);
        int len = 2;
        //接收数据
        char buf1[22] = "";
        char buf2[22]="";
        while (len == 2)
        {
            len = fscanf(fp,"%s %s",buf1,buf2);
            //从所有数据分别比较,符合则退出
            if (strcmp(buf2, in_pass) == 0)
            {
                fclose(fp);
                return ;
            }
        }
        printf("密码错误,请重新输入\n");
    }
}

/*******************************************/

文件名:main.c

/*******************************************/

#include "chat.h"
int main(int argc, char const *argv[])
{
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sfd == -1)
    {
        perror("socket error");
        return -1;
    }
    port_reuse(sfd);
    struct sockaddr_in sin;
    struct ip_mreqn imr;
    ser_bind(&imr, &sin);
    if (setsockopt(sfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) == -1)
    {
        perror("setsockopt error");
        return -1;
    }
    if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1){
        perror("bind error");
        return -1;
    }
    struct Msg msg_send;
    struct Msg msg_recv;
    //定义用于退出的变量
    int flag = 0;
    while (1)
    {
        int key1 = -1;
        log_menu();
        printf("操作指令:");
        scanf("%d", &key1);
        getchar();
        switch (key1)
        {
        case 1:
        {
            log_in();
            usleep(1000);
            printf("请输入昵称:");
            fgets(msg_send.name, sizeof(msg_send.name), stdin);
            msg_send.name[strlen(msg_send.name) - 1] = 0;
            msg_send.type = 'j';//将此次发送的信息性质定义为加入信息
            sendto(sfd, &msg_send, sizeof(msg_send), 0, (struct sockaddr *)&sin, sizeof(sin));
            pid_t pid = -1;
            pid=fork();
            if (pid > 0)
            {
                while (1)
                {
                    //清除容器需要附加新内容的部分的缓存
                    memset(msg_send.languege, 0, sizeof(msg_send.languege));
                    fgets(msg_send.languege, sizeof(msg_send.languege), stdin);
                    msg_send.languege[strlen(msg_send.languege) - 1] = 0;
                    msg_send.type = 'r';//将此次发送的信息性质定义为正常阅读对话
                    if (strcmp(msg_send.languege, "quit") == 0)
                    {
                        msg_send.type = 'q';//将此次发送的信息性质定义为退出信息
                        sendto(sfd, &msg_send, sizeof(msg_send), 0, (struct sockaddr *)&sin, sizeof(sin));
                        // 获取当前系统中的所有子进程
                        pid_t child_pid = waitpid(-1, NULL, WNOHANG);
                        usleep(10000);
                        while (child_pid > 0)
                        {
                            // 发送SIGKILL信号给每个子进程
                            kill(child_pid, SIGKILL);
                            child_pid = waitpid(-1, NULL, WNOHANG);
                        }
                        break;
                    }
                    sendto(sfd, &msg_send, sizeof(msg_send), 0, (struct sockaddr *)&sin, sizeof(sin));
                }
            }
            else if(pid==0){
                while(1){
                    memset(msg_recv.languege,0,sizeof(msg_recv.languege));
                    recv(sfd,&msg_recv,sizeof(msg_recv),0);
                    switch(msg_recv.type){
                        case 'j':
                        {
                            printf("[%s]加入了群聊\n",msg_recv.name);
                        }
                        break;
                        case 'r':
                        {
                            printf("[%s]:%s\n",msg_recv.name,msg_recv.languege);
                        }
                        break;
                        case 'q':
                        {
                            printf("[%s]退出了群聊\n",msg_recv.name);
                        }
                        break;
                        default:
                        printf("error\n");
                    }
                }
            }
        }
        break;
        case 2:
        {
            log_reg();
        }
        break;
        case 0:
        {
            flag = 1;
        }
        break;
        default:
            printf("error\n");
        }
        if (flag == 1)
        {
            break;
        }
    }
    return 0;
}

/*******************************************/

文件名:ad.c

/*******************************************/

#include <myhead.h>
struct Msg
{
    char type;
    char name[10];
    char languege[128];
};
//绑定服务器
void ser_bind(struct ip_mreqn *imr, struct sockaddr_in *min)
{
    imr->imr_multiaddr.s_addr = inet_addr("224.1.2.3");   //组播ip
    imr->imr_address.s_addr = inet_addr("192.168.2.91"); //主机ip
    imr->imr_ifindex = 2;                                 //网卡编号
    min->sin_family = AF_INET;
    min->sin_port = htons(9999);
    min->sin_addr.s_addr = inet_addr("224.1.2.3");
}
//端口号重新启用
void port_reuse(int sockfd)
{
    //将端口号快速重用
    int reuse = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
    {
        perror("setsockopt error");
        return;
    }
    printf("端口号快速重用成功\n");
}
int main(int argc, char const *argv[])
{
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sfd == -1)
    {
        perror("socket error");
        return -1;
    }
    port_reuse(sfd);
    struct sockaddr_in sin;
    struct ip_mreqn imr;
    ser_bind(&imr, &sin);
    if (setsockopt(sfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) == -1)
    {
        perror("setsockopt error");
        return -1;
    }
    if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1){
        perror("bind error");
        return -1;
    }
    struct Msg msg_send;
    struct Msg msg_recv;
    pid_t pid = fork();
    if (pid > 0)
    {
        while (1)
        {
            memset(msg_send.languege, 0, sizeof(msg_send.languege));
            fgets(msg_send.languege, sizeof(msg_send.languege), stdin);
            msg_send.languege[strlen(msg_send.languege) - 1] = 0;
            msg_send.type = 'r';
            strcpy(msg_send.name,"管理员");
            if (strcmp(msg_send.languege, "quit") == 0)
            {
                msg_send.type = 'q';
                sendto(sfd, &msg_send, sizeof(msg_send), 0, (struct sockaddr *)&sin, sizeof(sin));
                // 获取当前系统中的所有子进程
                pid_t child_pid = waitpid(-1, NULL, WNOHANG);
                usleep(10000);
                while (child_pid > 0)
                {
                    // 发送SIGKILL信号给每个子进程
                    kill(child_pid, SIGKILL);
                    child_pid = waitpid(-1, NULL, WNOHANG);
                }
                break;
            }
            sendto(sfd, &msg_send, sizeof(msg_send), 0, (struct sockaddr *)&sin, sizeof(sin));
        }
    }
    else if (pid == 0)
    {
        while (1)
        {
            memset(msg_recv.languege, 0, sizeof(msg_recv.languege));
            recv(sfd, &msg_recv, sizeof(msg_recv), 0);
            switch (msg_recv.type)
            {
            case 'j':
            {
                printf("[%s]加入了群聊\n", msg_recv.name);
                fflush(stdout);
            }
            break;
            case 'r':
            {
                printf("[%s]:%s\n", msg_recv.name, msg_recv.languege);
                fflush(stdout);
            }
            break;
            case 'q':
            {
                printf("[%s]退出了群聊\n", msg_recv.name);
                fflush(stdout);
            }
            break;
            default:
                printf("error\n");
            }
        }
    }
    return 0;
}

结果:

2、tftp协议上传下载

代码:

/*******************************************/

文件名:load.h

/*******************************************/

#ifndef LOAD_H
#define LOAD_H
#include <myhead.h>
#define SER_PORT 69
#define SER_IP "192.168.3.54"
#define CLI_PORT 8888
#define CLI_IP "192.168.3.87"
typedef struct LOAD
{
    char buf[516];
    size_t size;
} LOAD;
//传输菜单
void load_menu();
//下载申请
void load_apply(struct LOAD *ld, char *text);
//绑定服务器
void ser_bind(struct sockaddr_in *sin);
//端口号重新启用
void port_reuse(int sockfd);
//安装
void load_in(int sockfd, int fd, struct LOAD *ld, struct sockaddr_in *sin);

//上传申请
void up_apply(struct LOAD *ld, char *text);
//上传
void load_out(int sockfd, int fd, struct LOAD *ld, struct sockaddr_in *sin);

#endif

/*******************************************/

文件名:load.c

/*******************************************/

#include "load.h"
//传输菜单
void load_menu()
{
    printf("/***********************/\n");
    printf(" /*******1、上传********/\n");
    printf(" /*******2、下载********/\n");
    printf(" /*******0、退出********/\n");
    printf("/***********************/\n");
}
//下载申请
void load_apply(struct LOAD *ld, char *text)
{
    short *p1 = (short *)ld->buf; //操作码
    *p1 = htons(1);
    char *p2 = ld->buf + 2; //文件名
    strcpy(p2, text);
    char *p4 = p2 + strlen(p2) + 1; //模式位
    strcpy(p4, "octet");
    ld->size = 2 + strlen(p2) + strlen(p4) + 2;
}
//绑定服务器
void ser_bind(struct sockaddr_in *sin)
{
    sin->sin_family = AF_INET;
    sin->sin_port = htons(SER_PORT);
    sin->sin_addr.s_addr = inet_addr(SER_IP);
}
//端口号重新启用
void port_reuse(int sockfd)
{
    //将端口号快速重用
    int reuse = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
    {
        perror("setsockopt error");
        return;
    }
    printf("端口号快速重用成功\n");
}
//安装
void load_in(int sockfd, int fd, struct LOAD *ld, struct sockaddr_in *sin)
{
    if (sockfd < 0 || fd < 0 || ld == NULL || sin == NULL)
    {
        fprintf(stderr, "Invalid arguments.\n");
        return;
    }
    int num = 0; // 用于跟踪数据块编号
    socklen_t addrlen = sizeof(*sin);
    while (1)
    {
        // 清空缓冲区
        memset(ld->buf, 0, sizeof(ld->buf));
        // 接收数据
        ssize_t bytes = recvfrom(sockfd, ld->buf, sizeof(ld->buf), 0, (struct sockaddr *)sin, &addrlen);
        if (bytes < 0)
        {
            perror("recvfrom error");
            return;
        }
        else if (bytes == 0)
        {
            // 对方关闭连接
            printf("Server closed the connection.\n");
            break;
        }
        // 检查操作码是否表示错误
        if (ld->buf[1] == 5)
        { 
            printf("Error code received: %d\n", (int)ld->buf[1]);
            // 这里可以添加错误处理逻辑
            break;
        }
        // 检查操作码是否表示数据块
        if (ld->buf[1] == 3)
        { 
            unsigned short block_num = ntohs(*(unsigned short *)(ld->buf + 2));
            if (block_num == num + 1 && bytes > 4)
            { 
                // 写入文件,跳过操作码和块编号
                if (write(fd, ld->buf + 4, bytes - 4) < 0)
                {
                    perror("write error");
                    return;
                }
                num = block_num; // 更新块编号
                // 发送 ACK
                ld->buf[1] = 4; 
                if (sendto(sockfd, ld->buf, 4, 0, (struct sockaddr *)sin, addrlen) < 0)
                {
                    perror("send ack error");
                    return;
                }
            }
            else
            {
                // 接收到的数据块不是预期的块编号
                printf("Received block number is not continuous or invalid size.\n");
                break;
            }
        }
        else
        {
            // 未知操作码
            printf("Unknown operation code received.\n");
            break;
        }
        // 检查是否接收完毕
        if (bytes < sizeof(ld->buf))
        {
            printf("下载完成\n");
            break;
        }
    }
}
//上传申请
void up_apply(struct LOAD *ld, char *text)
{
    short *p1 = (short *)ld->buf; //操作码
    *p1 = htons(2);
    char *p2 = ld->buf + 2; //文件名
    strcpy(p2, text);
    char *p4 = p2 + strlen(p2) + 1; //模式位
    strcpy(p4, "octet");
    ld->size = 2 + strlen(p2) + strlen(p4) + 2;
}
//上传
void load_out(int sockfd, int fd, struct LOAD *ld, struct sockaddr_in *sin)
{
    if (sockfd < 0 || fd < 0 || ld == NULL || sin == NULL)
    {
        fprintf(stderr, "Invalid arguments.\n");
        return;
    }
    int num = 0; // 用于跟踪数据块编号
    socklen_t addrlen = sizeof(*sin);
    while (1)
    {
        ssize_t bytes = recvfrom(sockfd, ld->buf, sizeof(ld->buf), 0, (struct sockaddr *)sin, &addrlen);
        if (bytes < 0)
        {
            perror("recvfrom error");
            return ;
        }
        if (bytes == 4)
        {
            //长度是4是应答报文
            if (ld->buf[1] == 4)
            {
                if (htons(num) == *(unsigned short *)(ld->buf + 2))
                {
                    num++;
                    printf("ack码正确\n");
                }
                else
                {
                    printf("ack玛错误\n");
                }
            }
            else if (ld->buf[1] == 5)
            {
                printf("操作玛为5,错误\n");
                return ;
            }
        }
        // 清空缓冲区
        bzero(ld->buf + 4, 512);
        ssize_t rate = read(fd, ld->buf + 4, 512);
        if (rate < 0)
        {
            perror("read error");
            return;
        }
        else if (rate == 0)
        {
            printf("未读取到数据\n");
            break;
        }
        ld->buf[1]=3;
        *((unsigned short *)(ld->buf + 2)) = htons(num);
        // 写入文件,跳过操作码和块编号
        if (sendto(sockfd, ld->buf, sizeof(ld->buf), 0, (struct sockaddr *)sin, sizeof(*sin)) < 0)
        {
            perror("sendto error");
            return;
        }
        // 检查是否接收完毕
        if (rate < 512)
        {
            printf("上传完成\n");
            break;
        }
    }
}

/*******************************************/

文件名:main.c

/*******************************************/

#include "load.h"
int main(int argc, char const *argv[])
{
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("sfd=%d\n", sfd);
    port_reuse(sfd);
    struct sockaddr_in sin;
    ser_bind(&sin);
    struct LOAD ld;
    int key = -1;
    int flag = 0;
    while (1)
    {
        load_menu();
        printf("指令:");
        scanf("%d", &key);
        getchar();
        switch (key)
        {
        case 1:
        {
            while (1)
            {
                char text[128] = "";
                printf("请输入你要上传的文件名:");
                fgets(text, sizeof(text), stdin);
                text[strlen(text) - 1] = 0;
                if (strcmp(text, "quit") == 0)
                {
                    printf("退出成功\n");
                    break;
                }
                up_apply(&ld, text);
                sendto(sfd, ld.buf, ld.size, 0, (struct sockaddr *)&sin, sizeof(sin));
                int fd = open(text, O_RDONLY);
                if (fd == -1)
                {
                    perror("open error");
                    return -1;
                }
                lseek(fd,0,SEEK_SET);
                load_out(sfd, fd, &ld, &sin);
                close(fd);
            }
        }
        break;
        case 2:
        {
            while (1)
            {
                char text[128] = "";
                printf("请输入你要安装的文件名:");
                fgets(text, sizeof(text), stdin);
                text[strlen(text) - 1] = 0;
                if (strcmp(text, "quit") == 0)
                {
                    printf("退出成功\n");
                    break;
                }
                load_apply(&ld, text);
                sendto(sfd, ld.buf, ld.size, 0, (struct sockaddr *)&sin, sizeof(sin));
                int fd = open(text, O_WRONLY | O_CREAT | O_TRUNC, 0664);
                if (fd == -1)
                {
                    perror("open error");
                    return -1;
                }
                lseek(fd,0,SEEK_SET);
                load_in(sfd, fd, &ld, &sin);
                close(fd);
            }
        }
        break;
        case 0:
        {
            flag = 1;
        }
        break;
        default:
            printf("error\n");
        }
        //退出外层循环
        if (flag == 1)
        {
            break;
        }
    }
    close(sfd);
    return 0;
}

结果:

当然是成功了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值