网络编程练习:UDP聊天室

【1】服务器代码

cs 复制代码
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "head.h"

// 服务器-------------------------------------------------------------------------》短信的接收方

/*创建一个空的单向链表*/
Node_p Create();

/*登录,聊天,退出*/
void login(int fd_s, Msg msg, Node_p p, struct sockaddr_in caddr);
void chat(int fd_s, Msg msg, Node_p p, struct sockaddr_in caddr);
void quit(int fd_s, Msg msg, Node_p p, struct sockaddr_in caddr);

int main(int argc, char const *argv[])
{
    int fd_s;
    // 1.创建数据报套接字(socket)------------------》有手机
    fd_s = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd_s < 0)
    {
        perror("soc err\n");
        return -1;
    }
    // printf("%d\n", fd_s);
    // 2.指定网络信息--------------------------------------》有号码
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = INADDR_ANY;
    int len = sizeof(caddr);

    // 3.绑定套接字(bind)------------------------------》绑定手机
    if (bind(fd_s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err\n");
        return -1;
    }
    // printf("bind ok\n");

    printf("欢迎使用欢欢聊天室\n");

    Msg msg;
    Node_p p = Create(); // 创建空链表

    pid_t pid;
    pid = fork(); // 创建新进程
    if (pid < 0)
    {
        perror("fork err\n");
        return -1;
    }
    else if (pid == 0)
    {
        fgets(msg.text, sizeof(msg.text), stdin);
        if (msg.text[strlen(msg.text) - 1] == '\n')
            msg.text[strlen(msg.text) - 1] = '\0';
        msg.type = 'C';
        strcpy(msg.name, "server");

        sendto(fd_s, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, len);
    }
    else
    {
        // 4.接收、发送消息(recvfrom sendto)-------》收短信
        while (1)
        {
            int ret;
            ret = recvfrom(fd_s, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, &len);
            if (ret < 0)
            {
                perror("recv err\n");
                return -1;
            }
            // printf("addr: %s  port: %d\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
            if (msg.type == 'L')
            {
                // printf("addr: %s  port: %d\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
                printf("<%s> 已登录\n", msg.name);
                sprintf(msg.text, "<%s> 已登录", msg.name);
                login(fd_s, msg, p, caddr);
            }
            else if (msg.type == 'C')
            {

                printf("[%s]: %s\n", msg.name, msg.text);
                chat(fd_s, msg, p, caddr);
            }
            else if (msg.type == 'Q')
            {
                sprintf(msg.text, "<%s> 已下线", msg.name);
                printf("<%s> 已下线\n", msg.name);
                quit(fd_s, msg, p, caddr);
            }
        }
    }
    // 5.关闭套接字(close)----------------------------》接收完毕
    close(fd_s);
    return 0;
}

/*创空表*/
Node_p Create()
{
    Node_p p = (Node_p)malloc(sizeof(Node_t)); // 开辟一个节点大小的堆区空间
    if (NULL == p)                             // 开辟失败
    {
        printf("malloc lost");
        return NULL;
    }
    p->next = NULL; // 初始化头节点指针域
    return p;       // 开辟成功,返回
}

/*登录*/
void login(int fd_s, Msg msg, Node_p p, struct sockaddr_in caddr)
{
    while (p->next != NULL)
    {
        p = p->next;
        sendto(fd_s, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
    }

    /*创建新的节点*/
    Node_p p_new = (Node_p)malloc(sizeof(Node_t));
    if (p_new == NULL)
    {
        perror("malloc err\n");
        return;
    }

    /*初始化节点*/
    p_new->addr = caddr;
    p_new->next = NULL;

    /*将新节点连接到链表中*/
    p->next = p_new;
    // p = p_new;
    return;
}

/*聊天*/
void chat(int fd_s, Msg msg, Node_p p, struct sockaddr_in caddr)
{
    while (p->next != NULL)
    {
        p = p->next;
        if ((memcmp(&(p->addr), &caddr, sizeof(caddr))) == 0)
        {
            continue;
        }
        sendto(fd_s, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
    }
}

/*退出*/
void quit(int fd_s, Msg msg, Node_p p, struct sockaddr_in caddr)
{
    while (p->next != NULL)
    {
        if ((memcmp(&(p->next->addr), &caddr, sizeof(caddr))) == 0)
        {
            Node_p p_del = NULL;
            p_del = p->next;
            p->next = p_del->next;
            free(p_del);
            p_del = NULL;
            continue;
        }
        else
        {
            p = p->next;
            sendto(fd_s, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
        }
    }
    return;
}

【2】客户端代码

cs 复制代码
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include "head.h"
#include <pthread.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

// 客户端---------------------------------------------------------------------------》短信的发送方

struct sockaddr_in saddr;
int len = sizeof(saddr);

int main(int argc, char const *argv[])
{
    // 1.创建数据报套接字(socket)------------------》有手机
    int fd_c;
    fd_c = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd_c < 0)
    {
        perror("soc err\n");
        return -1;
    }
    // printf("%d\n", fd_c);
    // 2.指定网络信息--------------------------------------》有对方号码
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = inet_addr("192.168.31.187");

    Msg msg;
    msg.type = 'L';
    printf("欢迎使用欢欢聊天室\n");
    printf("请输入用户名:");
    fgets(msg.name, N, stdin);
    if (msg.name[strlen(msg.name) - 1] == '\n')
        msg.name[strlen(msg.name) - 1] = '\0';

    sendto(fd_c, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, sizeof(saddr));

    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        exit(-1);
    }
    else if (pid == 0)
    {
        while (1)
        {
            if (recvfrom(fd_c, &msg, sizeof(msg), 0, NULL, NULL) < 0)
            {
                perror("recvfrom err");
                return -1;
            }
            if (msg.type == 'L')
                printf("%s\n", msg.text);
            else if (msg.type == 'C')
                printf("[%s]:%s\n", msg.name, msg.text);
            else if (msg.type == 'Q')
                printf("%s\n", msg.text);
        }
    }
    else
    {
        while (1)
        {
            fgets(msg.text, sizeof(msg.text), stdin);
            if (msg.text[strlen(msg.text) - 1] == '\n')
                msg.text[strlen(msg.text) - 1] = '\0';
            if (strcmp(msg.text, "quit") == 0)
            {
                msg.type = 'Q';
                sendto(fd_c, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, len);
                kill(pid, SIGKILL);
                wait(NULL);
                exit(-1);
            }
            else
            { 
                msg.type = 'C';
            }
            // 发送消息
            sendto(fd_c, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, len);
        }
    }
    close(fd_c);

    return 0;
}

今天的分享就到这里结束啦,如果有哪里写的不好的地方,请指正。

如果觉得不错并且对你有帮助的话点个关注支持一下吧!

相关推荐
liu****12 分钟前
27.epoll(三)
服务器·开发语言·网络·tcp/ip·udp
Caven7713 分钟前
【Linux 技巧】如何在登录时自动激活 Conda Base 环境
linux·运维·conda
凌寒1136 分钟前
Linux(Debian)安装、卸载 MySQL
linux·运维·mysql·debian
云飞云共享云桌面40 分钟前
如何降低非标自动化工厂的研发软件采购成本
运维·服务器·网络·数据库·性能优化·自动化
IT小哥哥呀42 分钟前
如何从 Windows SSH 进入 VirtualBox Ubuntu 虚拟机——密码认证(逐步指南)
linux·windows·ubuntu·ssh·教程·虚拟机
怀旧,2 小时前
【Linux系统编程】7. 进程的概念(上)
linux·运维·服务器
7***n752 小时前
API网关设计模式
linux·服务器·设计模式
时代新威powertime3 小时前
等保三级|安全通信网络自评估指南
网络·安全·等保测评
国服第二切图仔3 小时前
检测隧道HTTP代理的可用性的几种方法
网络·网络协议·http
哈里谢顿3 小时前
环境变量 HTTP_PROXY/HTTPS_PROXY 深度解析:为什么 cURL 行,Docker 不行?
linux