项目名称:网络聊天室

该文详细介绍了如何在Linux环境下,使用UDP协议开发一个网络聊天室。项目要求服务器端记录客户端地址,接收并转发消息。程序设计涉及多进程,服务器端用链表保存用户信息,客户端通过登录、聊天和退出功能与服务器交互。文章还阐述了UDP通信的基本流程及recvfrom和sendto函数的使用,并提供了客户端和服务器端的代码片段。
摘要由CSDN通过智能技术生成

目录

一,简述

二,项目要求

三,程序流程图

服务器端:

客户端:

 四,相关知识点

通信流程:

 函数接口:

五,代码实现

客户端:

服务器:

主程序

函数——server_quit

​函数——server_chat

函数——server_login

函数——链表


一,简述

开发工具: Linux平台GCC交叉编译环境

网络聊天室

  • 登录:服务器存储新的客户端的地址。把某个客户端登录的消息发给其它客户端。
  • 聊天:服务器只需要把某个客户端的聊天消息转发给所有其它客户端。
  • 退出:服务器删除退出客户端的地址,并把退出消息发送给其它客户端。

二,项目要求

   利用UDP协议,实现一套聊天室软件。服务器端记录客户端的地址,客户端发送消息后,服务器群发给各个客户端软件。

三,程序流程图

  客户端登录之后,为了实现一边发送数据一边接收数据,可以使用多进程。
服务器既可以发送系统信息,又可以接收客户端信息并处理,可以使用多进程。
服务器需要给多个用户发送数据,所以需要保存每一个用户的信息,使用链表来保存。
数据传输的时候要定义结构体,结构体中包含操作码、用户名以及数据。

 对登录聊天室的用户,需要保存用户信息,将登录的客户端信息新建节点插入链表。

服务器端:

客户端:

 四,相关知识点

通信流程:

udp流程:(类似发短信)
server:
创建数据报套接字(socket(,SOCK_DGRAM,))----->有手机
绑定网络信息(bind())---------------------->绑定号码(发短信知道发给谁)
接收信息(recvfrom())--------------------->接收短信
关闭套接字(close())----------------------->接收完毕

client:
创建数据报套接字(socket())----------------------->有手机
指定服务器的网络信息------------------------------>有对方号码
发送信息(sendto())---------------------------->发送短信
关闭套接字(close())--------------------------->发送完

 函数接口:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
					struct sockaddr *src_addr, socklen_t*addrlen);
功能:接收数据
参数:
	sockfd:套接字描述符
	buf:接收缓存区的首地址
	len:接收缓存区的大小
	flags:0
	src_addr:发送端的网络信息结构体的指针
	addrlen:发送端的网络信息结构体的大小的指针
  
返回值:
	成功接收的字节个数
	失败:-1
	0:客户端退出

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                  const struct sockaddr *dest_addr, socklen_t addrlen);
功能:发送数据

参数:
	sockfd:套接字描述符
	buf:发送缓存区的首地址
	len:发送缓存区的大小
	flags:0
	src_addr:接收端的网络信息结构体的指针
	addrlen:接收端的网络信息结构体的大小

返回值: 
	成功发送的字节个数
	失败:-1

五,代码实现

客户端:

int main(int argc, char const *argv[])
{
    //创建数据报
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }

    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[2]));
    saddr.sin_addr.s_addr = inet_addr(argv[1]);

    //1.登录
    MSG_t msg;
    msg.type = login;
    printf("please input your name:");
    fgets(msg.name, sizeof(msg.name), stdin);
    if (msg.name[strlen(msg.name) - 1] == '\n')
        msg.name[strlen(msg.name) - 1] = '\0';

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

    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err.");
        return -1;
    }
    else if (pid == 0)
    {
        while (1)
        {
            if (recvfrom(sockfd, &msg, sizeof(msg), 0, NULL, NULL) < 0)
            {
                perror("recvfrom err.");
                return -1;
            }
            printf("%s said %s\n", msg.name, 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 (strncmp(msg.text, "quit", 4) == 0)
            {
                msg.type = quit;
                sendto(sockfd, &msg, sizeof(msg), 0,
                       (struct sockaddr *)&saddr, sizeof(saddr));
                break;
            }
            else
            {
                msg.type = chat;
            }
            sendto(sockfd, &msg, sizeof(msg), 0,
                   (struct sockaddr *)&saddr, sizeof(saddr));
        }
        kill(pid, SIGKILL);
        wait(NULL);
    }
    close(sockfd);
    return 0;
}

服务器:

主程序

int main(int argc, char const *argv[])
{
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");

    socklen_t len = sizeof(caddr);

    if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err.");
        return -1;
    }
    MSG_t msg;
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err.");
        return -1;
    }
    else if (pid == 0)
    {
        //创建一个空的链表
        list_t *p = createList();
        while (1)
        {
            if (recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, &len) < 0)
            {
                perror("recvfrom err.");
                return -1;
            }
            switch (msg.type)
            {
            case login:
                server_login(sockfd, p, msg, caddr);
                break;
            case chat:
                server_chat(sockfd, p, msg, caddr);
                break;
            case quit:
                server_quit(sockfd, p, msg, caddr);
                break;
            }
        }
    }else{
        while(1)
        {
            msg.type=chat;
            strcpy(msg.name,"server");
            fgets(msg.text, sizeof(msg.text), stdin);
            if (msg.text[strlen(msg.text) - 1] == '\n')
                msg.text[strlen(msg.text) - 1] = '\0';
            sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, sizeof(saddr));
        }
    }
    close(sockfd);
    return 0;
}

函数——server_quit


//1.将谁退出消息转发给所有登录的客户端
//2.将推出客户端信息从链表中清除

void server_quit(int sockfd, list_t *p, MSG_t msg, struct sockaddr_in caddr)
{
    list_t *pdel = NULL;
    sprintf(msg.text, "%s quit", msg.name);
    while (p->next != NULL)
    {
        if (memcmp(&(p->next->addr), &caddr, sizeof(caddr)) == 0)
        {
            pdel = p->next;
            p->next = pdel->next;
            free(pdel);
            pdel = NULL;
        }
        else
        {
            p = p->next;
            sendto(sockfd, &msg, sizeof(msg), 0,
                   (struct sockaddr *)&(p->addr), sizeof(p->addr));
        }
    }
}

函数——server_chat

//chat:
//1.将聊天转发除自己之外的所有客户端(sockfd,p,msg,caddr)

void server_chat(int sockfd, list_t *p, MSG_t msg, struct sockaddr_in caddr)
{
    //遍历链表转发消息
    while (p->next != NULL)
    {
        p = p->next;
        if (memcmp(&(p->addr), &caddr, sizeof(caddr)) == 0)
            continue;
        sendto(sockfd, &msg, sizeof(msg), 0,
               (struct sockaddr *)&(p->addr), sizeof(p->addr));
    }
}

函数——server_login

//login:
//1.将谁登录转发所有已经登录的客户端  (链表、sockfd,msg)
//2.将新登录的客户端信息新建节点插入链表 (caddr)

void server_login(int sockfd, list_t *p, MSG_t msg, struct sockaddr_in caddr)
{
    sprintf(msg.text, "%s login", msg.name);
    while (p->next != NULL)
    {
        p = p->next;
        sendto(sockfd, &msg, sizeof(msg), 0,
               (struct sockaddr *)&(p->addr), sizeof(p->addr));
    }
    //创建新节点
    list_t *pnew = (list_t *)malloc(sizeof(list_t));
    pnew->addr = caddr;
    pnew->next = NULL;

    //链接到链表最后
    p->next = pnew;
}

函数——链表

//创建一个空的有头单向链表

//创建一个空的有头单向链表
list_t *createList(void)
{
    list_t *p = (list_t *)malloc(sizeof(list_t));
    if (NULL == p)
    {
        perror("malloc err.");
        return NULL;
    }
    //初始化节点
    p->next = NULL;
    return p;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值