项目名称:网络聊天室

news2024/11/26 23:34:15

目录

一,简述

二,项目要求

三,程序流程图

服务器端:

客户端:

 四,相关知识点

通信流程:

 函数接口:

五,代码实现

客户端:

服务器:

主程序

函数——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;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/770016.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android性能优化——启动优化

App 的启动速度是用户的第一体验&#xff0c;互联网中有一个八秒定律&#xff0c;如果用户等待八秒App 还没打开&#xff0c;70%的用户都会停止等待 一、启动分类 官方 App startup time 冷启动 耗时最多&#xff0c;衡量标准 热启动 最快。 后台&#xff5e;前台 温启动…

Git 新建本地仓库,推送到远程仓库

1、在项目的根目录右键 Git Bash Here 打开目录下的 git 命令 2、输入 git init 回车&#xff0c;初始化项目&#xff0c;把这个项目变成一个Git可以管理的仓库 项目根目录出现 .git 隐藏文件夹。这个目录是Git来跟踪管理版本库的&#xff0c;没事千万不要手动修改这个目录里面…

MateBook E Go Wi-Fi性能版(GK-W76)工厂模式win11原厂系统,含F10智能恢复功能

HUAWEI华为MateBook E平板笔记本电脑(GK-W76)原装出厂Windows11系统包&#xff0c;带F10一键智能还原 系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件、华为电脑管家等预装程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&#xff1a;zip 文件大小&#xff1a;1…

Linux基础(三)端口、进程及主机状态管理、环境变量、文件管理

目录 端口 nmap netstat 进程管理 查看进程 关闭进程 主机状态监控 系统资源top命令 磁盘信息监控 网络状态监控 环境变量 $符号 自己设置环境变量 自定义环境变量PATH Linux的文件和下载 压缩和解压 tar命令 zip和unzip命令 端口 每个电脑有一个ip地址&#xff…

自动化测试(一):网页结构分析与Google翻译2023.7.18爬虫实例

目录 1. 网页分析1.1 静态网页1.2 静态网页的爬取案例1.3 动态网页1.4 Google翻译2023.7.18爬虫实例1.4.1 基于网页分析的Google翻译2023.7.18爬虫实例1.4.2 基于Selenium的Google翻译2023.7.18爬虫实例 1. 网页分析 网页分析即通过检查元素&#xff0c;确定想提取的内容的区域…

pyright 的配置方法

珍惜发量,拥抱python&#x1f602; 可以很快的构建一个平台管理程序,嵌入式设备,芯片厂商&#xff0c;都是python 的示例代码了 Lua 真的很棒&#xff0c;值得嵌入式领域的推广 实时性要求不高的&#xff0c;嵌入式设备,可以考虑lua python PC 的程序 ,python/Rust/C &#xf…

波奇学Linux:冯诺依曼和进程

现代计算机大多是冯诺依曼体系结构&#xff0c;这是一种硬件结构&#xff0c;规定了设备组成和设备类型。 存储器:内存&#xff0c;磁盘/U盘是外存是输入输出设备。 输入设备&#xff1a;键盘/磁盘/摄像头/话筒/网卡 输出设备&#xff1a;显示器&#xff0c;播放器硬件&#…

Spring6 初始

Spring6 初始 文章目录 Spring6 初始每博一文案:1. 初始 Spring61.1 OCP开闭原则1.2 依赖倒置原则DIP1.3 控制反转IoC 2. Spring 初始2.1 Spring特点2.2 Spring6 的下载&#xff1a;2.3 Spring的jar文件 3. 第一个Spring 程序的编写4. 第一个Spring程序详细剖析4.1 bean标签的i…

SQL28 计算用户8月每天的练题数量

select day(date) as day,count(question_id) from question_practice_detail where month(date)8 and year(date)2021 group by date

C语言如何计算结构体大小(结构体的内存对齐)

前言&#xff1a; 结构体的内存对齐是有关结构体内容的很重要一个知识点&#xff0c;主要考察方式是计算结构体的字节大小。 引言&#xff1a; 当我们对计算结构体一无所知&#xff0c;我们不妨自己思索如何计算&#xff0c;是不是直接计算结构体成员变量占用内存的大小呢&a…

共享与协作:时下最热门的企业共享网盘推荐!

现代企业面临着越来越大的数据存储和共享压力。为了提高公司的生产力和效率&#xff0c;许多企业开始寻找共享网盘解决方案。这些共享网盘平台可以帮助企业集中管理文件和数据&#xff0c;并方便快速地与同事、客户或供应商共享。以下是几款好用的企业共享网盘。 Zoho Workdriv…

Android BlueToothBLE入门(三)——数据的分包发送和接收(源码已更新)

学更好的别人&#xff0c; 做更好的自己。 ——《微卡智享》 本文长度为3675字&#xff0c;预计阅读12分钟 前言 接上篇《Android BlueToothBLE入门&#xff08;二&#xff09;——设备的连接和通讯&#xff08;附Demo源码地址&#xff09;》最后提到过蓝牙BLE通讯每次默认发送…

qt 32位编译 内存溢出 无法 运行在win7 32位

项目在 编译32位系统 内存溢出 设置成了x64 但是 最后在xp32位系统运行提示 在下载了n个dll之后发现这种状况无穷无尽&#xff0c;后来在查阅资料时发现可以直接打开qt安装目录下的“vcredist”文件夹&#xff0c;将对应位数的程序拷到win7电脑上&#xff0c;直接运行&…

优化营商环境:构建智能营销平台,助力企业经营发展

对于企业来说&#xff0c;没有了客户&#xff0c;就像身体没有了血液&#xff0c;将失去生命力和活力&#xff0c;续存难发展更难。区域产业又是由一个个企业集聚而形成&#xff0c;企业的成败也就决定着区域产业的兴衰。 在当今竞争激烈的商业环境中&#xff0c;传统的销售手段…

基于python的爬虫实现

定义 爬虫&#xff08;Web crawler&#xff09;&#xff0c;也被称为网络爬虫、网络蜘蛛或网络机器人&#xff0c;是一种自动化程序&#xff0c;用于浏览互联网并收集网页内容。 基本原理 爬虫的工作原理是通过发送HTTP请求从网页服务器获取网页的内容&#xff0c;然后解析网…

何时使用Windbg静态分析?何时使用Windbg动态调试?

目录 1、概述 2、使用Windbg静态分析dump文件 2.1、异常捕获模块自动生成dump文件 2.2、从Windows任务管理器中导出dump文件 2.3、从正在动态调试的Windbg中使用命令导出dump文件 2.4、使用Windbg静态分析dump文件的一般步骤 3、使用Windbg动态调试目标进程 3.1、程序发…

Win10 配置NDK安装2023.7.19版本

NDK安装流程 1. 下载&#xff1a;2. 安装&#xff1a;3. 测试&#xff1a; 在大多数情况下&#xff0c;使用 Android SDK 管理器安装 NDK 会更轻松。本文单独安装NDK&#xff0c;但后续也可以使用管理器进行管理。 1. 下载&#xff1a; 地址 Fig.1 最新稳定版本 2. 安装&…

精益生产的五大管理工具:提升效率,降低成本!

在制造业的世界里&#xff0c;精益生产是一种以追求在制造过程的各个方面减少浪费为中心的方法。为了实现这一目标&#xff0c;有几个经常使用的管理工具。这些工具使制造商能够识别和消除生产过程中任何效率低下或浪费的资源。本文将讨论精益生产中使用的一些关键管理工具&…

Android 进程与进程之间的通信--Messager 详细教程,两个app实现

Messenger是一种轻量级的IPC方案,它的底层实现其实就是AIDL.跨进程通信使用Messenger时,Messenger会将所有服务调用加入队列,然后服务端那边一次处理一个调用,不会存在同时调用的情况.而AIDL则可能是多个调用同时执行,必须处理多线程问 步骤详情 一、服务端 public class MyM…

如何使用DiskPart命令行格式化分区?

想要格式化磁盘分区&#xff0c;您可以使用磁盘管理工具&#xff0c;或在Windows文件资源管理器中右键单击驱动器并选择“格式化”。如果您更想使用命令行来格式化磁盘&#xff0c;那么Windows自带的DiskPart将是首选。 DiskPart有很多优点&#xff0c;例如&#xff0c;如果您想…