网络编程练习: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;
}

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

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

相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒2 天前
TShark:基础知识
linux
AlfredZhao2 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪3 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5203 天前
Linux 11 动态监控指令top
linux
网络研究院3 天前
2026年网络安全
网络·安全·法律·法规·趋势·发展