坐牢第二十七天(聊天室)

news/2024/9/23 5:02:13/

基于UDP的网络聊天室

一.项目需求:

1.如果有用户登录,其他用户可以收到这个人的登录信息

2.如果有人发送信息,其他用户可以收到这个人的群聊信息

3.如果有人下线,其他用户可以收到这个人的下线信息

4.服务器可以发送系统信息

二.代码 

udp.h

#ifndef UDP_H
#define UDP_H
#include <myhead.h>
#define SER_PORT 8888           // 服务器端口号
#define SER_IP "192.168.0.105" // 服务器ip地址
#define CLI_PORT 5555          // 客户端端口号
#define CLI_IP "192.168.0.105" // 客户端地址
//枚举
enum type_t
{Login,Chat,Quit,
};
typedef struct MSG
{char type;//Login名字  Chat内容 Quit退出  //内容编号char name[32];//名字char text[128];//内容
}msg_t;
typedef struct NODE//链表
{struct sockaddr_in cin;struct NODE *next;
}Node,*Nodeptr;
//创建头节点函数
Nodeptr create();
//登录的函数
//功能:
//1.将新登录的用户转发给所有已经登录的用户(遍历链表发送谁登录的消息)
//2.创建新节点来保存新登录用户的信息,链接到链表尾就可以
void do_login(int sockfd,msg_t msg,Nodeptr p,struct sockaddr_in cin);
//群聊的函数
//功能:将客户端发来的聊天内容转发给所有已登录的用户,除了发送聊天内容的用户以外
void do_chat(int sockfd,msg_t msg,Nodeptr p,struct sockaddr_in cin);
//退出函数
//功能:
//1.将谁退出的消息转发给i所有用户
//2.将链表中保存这个推出的用户信息的节点删除
void do_quit(int sockfd,msg_t msg,Nodeptr p,struct sockaddr_in cin);
#endif 

udp.c

#include "udp.h"
// 定义创建头节点函数
Nodeptr create()
{Nodeptr p = (Nodeptr)malloc(sizeof(Node));if (p == NULL){perror("malloc error");return NULL;}p->next = NULL;return p;
}
// 定义登录的函数
void do_login(int sockfd, msg_t msg, Nodeptr p, struct sockaddr_in cin)
{sprintf(msg.text, "%s 以上线", msg.name);while (p->next != NULL){p = p->next;sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->cin), sizeof(p->cin));}Nodeptr new = (Nodeptr)malloc(sizeof(Node));// 初始化new->cin = cin;new->next = NULL;// 链接到链表尾p->next = new;return;
}
// 定义群聊的函数
void do_chat(int sockfd, msg_t msg, Nodeptr p, struct sockaddr_in cin)
{// 遍历链表while (p->next != NULL){p = p->next;if (memcmp(&(p->cin), &cin,sizeof(cin))!= 0){sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->cin), sizeof(p->cin));}}return;
}
// 定义退出函数
void do_quit(int sockfd, msg_t msg, Nodeptr p, struct sockaddr_in cin)
{sprintf(msg.text, "%s 以下线", msg.name);while (p->next != NULL){if (memcmp(&(p->cin), &cin,sizeof(cin)) == 0){Nodeptr q = NULL;q = p->next;p->next = q->next;free(q);q = NULL;}else{p = p->next;sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->cin), sizeof(p->cin));}}return;
}

sen.c

// 服务器
#include "udp.h"
int main(int argc, char const *argv[])
{// 创建UDP套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){perror("socket error");exit(-1);}    // 填充服务器网络信息结构体//定义服务器结构体 struct sockaddr_in sin;sin.sin_family=AF_INET;sin.sin_port = htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);// 定义保存客户端网络信息的结构体struct sockaddr_in cin;cin.sin_family = AF_INET;cin.sin_port = htons(CLI_PORT);cin.sin_addr.s_addr = inet_addr(CLI_IP);socklen_t len = sizeof(cin);// 绑定套接字和服务器网络信息的结构体bind(sockfd, (struct sockaddr *)&sin, sizeof(sin));printf("绑定成功!\n");msg_t msg;Nodeptr p = create();char s[20]="";while (1){if (recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, &len) < 0){perror("recvfrom error");return -1;}if (msg.type == Login){strcpy(msg.text, "以上线");printf("ip:%s pord:%d name:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), msg.name);printf("状态:%s\n", msg.text);//调用登录函数do_login(sockfd, msg, p, cin);}else if (msg.type == Chat){//调用群聊函数do_chat(sockfd, msg, p, cin);}else if (msg.type == Quit){strcpy(msg.text, "以下线");printf("ip:%s pord:%d name:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), msg.name);printf("状态:%s\n", msg.text);//调用退出函数do_quit(sockfd, msg, p, cin);}}close(sockfd);return 0;
}

rec.c

// 客户端
#include "udp.h"
int main(int argc, char const *argv[])
{int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){perror("socket error");exit(-1);}struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);socklen_t len = sizeof(sin);msg_t msg;// 先执行登录操作printf("请登录:\n");msg.type = Login;printf("请输入用户名:");fgets(msg.name, 32, stdin);msg.name[strlen(msg.name) - 1] = 0;// 发送登录消息if (sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&sin, len) < 0){perror("sendto err");exit(-1);}//创建多进程pid_t pid = fork();if (pid < 0){perror("fork error");exit(-1);}else if (pid == 0){while (1){if (recvfrom(sockfd, &msg, sizeof(msg), 0, NULL, NULL) < 0){perror("recvfrom error");return -1;}printf("[%s]:%s\n", msg.name, msg.text);}}else{while (1){fgets(msg.text, sizeof(msg.text), stdin);msg.text[strlen(msg.text) - 1] = 0;if (strcmp(msg.text, "quit") == 0){msg.type = Quit;sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&sin, len);kill(pid, SIGKILL);wait(NULL);exit(EXIT_SUCCESS);}else{msg.type = Chat;}// 发送消息sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&sin, len);}}close(sockfd);return 0;
}


http://www.ppmy.cn/news/1510599.html

相关文章

liblzma库Android平台编译

1.下载源码: git clone https://github.com/tukaani-project/xz.git --recursive 2.配置交叉编译环境: 生成Android平台makefile export ANDROID_API=25 export ANDROID_NDK=/opt/aarch64-darwin-android export ANDROID_NDK_REVISION=r25b export AR=/opt/aarch64-darwin-a…

QML基础学习

QML学习 一、基础知识 一、基础知识 QML 的文件名首字母要大写&#xff0c;否则找不到文件 QML文件名作为组件对象名 alias 为属性取的别名&#xff0c;可以在其它地方实例化该组件时&#xff0c;更改其对应属性的值 implicitWidth/Height一般用在可重用控件,可以理解为控件…

linux下对目录文件进行操作(打开目录,读取目录项,关闭目录),进入目录的函数chdir,七篇文件I/O文章小结

目录文件介绍 目录也是一种文件&#xff0c;因此操作流程与普通文件类似&#xff0c;有诸如打开、关闭、定位等概念&#xff0c;但目录是一种特殊的文件&#xff0c;目录存储的数据的最小单位并不是字符&#xff0c;而是目录项。这使得目录跟普通文件又有区别。目录项指的是结…

数据分析面试常见50个问题及解答要点

为了帮助各位学习数据分析的小伙伴们成功拿到offer&#xff01;本期给大家整理了一些数据分析面试时的高频问题&#xff0c;分享给大家 数据分析高频面试50题&#xff0c;点击下方链接进行下载完整版&#xff0c;下面展示部分面试题&#xff0c;希望大家积极点赞收藏加关注&…

Android 12系统源码_多屏幕(二)模拟辅助设备功能开关实现原理

前言 上一篇我们通过为Android系统开启模拟辅助设备功能开关&#xff0c;最终实现了将一个Activity显示到多个屏幕的效果。 本篇文章我们具体来分析一下当我们开启模拟辅助设备功能开关的时候&#xff0c;Android系统做了什么哪些操作。 一、模拟辅助设备功能开关应用位置 …

C语言-使用指针数组作为函数参数,实现对10个字符串进行排序

使用指针数组作为函数参数&#xff0c;实现对10个字符串进行排序 1.输入 lisi hahaha hehehe helloa leihoua lisi nihaoa wangwu ajax bureau2.输出 ajax bureau hahaha hehehe helloa leihoua lisi lisi nihaoa wangwu3.程序&#xff1a; #define _CRT_SECURE_NO_WARNING…

ffmpeg使用x11录屏

version #define FFMPEG_VERSION "6.1.1" note x11视频采集结构:AVInputFormat ff_xcbgrab_demuxer code void CFfmpegOps::CaptureVideo(const char *outFileName) {const AVInputFormat *iFmt nullptr;size_t n 0;AVFormatContext *iFmtCtx nullptr;AVDict…

短链接绕过限制

利用目的 隐藏恶意内容&#xff1a;攻击者可以将恶意内容&#xff08;如重定向、恶意脚本等&#xff09;隐藏在短链接之后&#xff0c;从而绕过对长URL的限制。当用户点击短链接时&#xff0c;他们实际上被重定向到了一个更长的、包含恶意内容的URL 绕过安全检查&#xff1a;在…