设计思路

考虑到只是一个简易版本的UDP聊天服务,所以很多不完善的地方

服务器

服务器我是开了一个父子进程,分别负责的

  • 接受客户端的消息&&发送某一个客户端的信息
  • 服务器的命令终端(只不过没有实现,只写了广播功能)

然后我封装了一个数据结构,每当客户端发一个包过来,这个包由三部分组成

  • 消息类型(login、cheat、end)
  • 发送者的姓名
  • 发送者的消息构成

然后我们分三种情况来解析这三种消息就行,具体实现就是一个简单的链表结构,详情可以看代码。

CODE

/*** 缺点:* 1.以奇怪的方式下线的用户不能清理* */#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#define false 0
#define true 1#define N 1024struct MSG{//消息封装char type;char name[20];char text[N];
};struct Node {//用户节点信息struct sockaddr_in addr;struct Node * next;
};struct Node * init() {//用户节点初始化struct Node * p = (struct Node *)malloc(sizeof(struct Node));memset(p,0,sizeof(struct Node));p->next = NULL;return p;
}const char *ip = "127.0.0.1";
const int port = 8000;
struct Node *head;void login(int sockfd,struct MSG msg,struct sockaddr_in clientaddr) {//登陆消息struct Node *p = head;strcpy(msg.text,msg.name);msg.text[strlen(msg.text) - 1]  = '\0';strcat(msg.text," 已经上线欢迎来聊^_^~");//puts(msg.text);int cnt = 0;while(p->next) {//发给已经上线的用户if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&p->next->addr,sizeof(p->next->addr)) < 0){perror("online error");}p = p->next;cnt++;}struct Node *k = (struct Node *)malloc(sizeof(struct Node));k->addr = clientaddr;k->next = NULL;p->next = k;printf("当前总用户数:%d \n上线用户的port: %u \n",cnt,ntohl(clientaddr.sin_port));
}void chat(int sockfd,struct MSG msg,struct sockaddr_in clientaddr) {//聊天消息处理函数struct Node *p = head;   char str[N]= {0};//puts(msg.name);//puts(msg.text);sprintf(str,"%s : %s",msg.name,msg.text);strcpy(msg.text,str);puts(msg.text);//输出到服务器终端while(p->next) {if (memcmp(&clientaddr, &p->next->addr, sizeof(clientaddr)) != 0) {if (sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *) &p->next->addr, sizeof(p->next->addr)) < 0) {perror("chat sendto error");}}p = p->next;}
}void quit(int sockfd,struct MSG msg,struct sockaddr_in clientaddr) {//退出函数struct Node *p = head;struct Node *q = NULL;sprintf(msg.text,"%s用户已下线",msg.name);puts(msg.text);while(p->next) {//因为头号节点是服务器if(memcmp(&clientaddr,&p->next->addr,sizeof(clientaddr)) == 0) {q = p->next;p->next = q->next;free(q);q=NULL;//free后将指针赋为NULL}else {sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&p->next->addr,sizeof(p->next->addr));}p = p->next;}
}int socketfd;void my_end(int sign_no) {//这个是注册一个ctr+c退出时候的一个信号处理close(socketfd);exit(1);
}int main() {signal(SIGINT,my_end);head = init();struct sockaddr_in serveraddr,clientaddr;bzero(&serveraddr,sizeof(serveraddr));bzero(&clientaddr,sizeof(clientaddr));struct MSG msg;socklen_t addrlen = sizeof(struct sockaddr);char buf[N] = "";if((socketfd = socket(AF_INET, SOCK_DGRAM, 0))< 0) {perror("socket error");}printf("socketfd = %d\n",socketfd);serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);serveraddr.sin_port = htons(port);printf("serveraddr.sin_port = %d\n", htonl(port));if(bind(socketfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0) { // bind绑定perror("bind error");}printf("serveraddr.sin_port = %u\n", serveraddr.sin_port);printf("Accepting connections ...\n");pid_t pid = fork();if(pid < 0) {perror("fork error");}if(pid == 0) {//服务器的命令终端(只不过没有实现,只写了广播功能)memset(&msg,0,sizeof(msg));strncpy(msg.name,"server",6);msg.type = 'C';while(1) {fgets(buf,N,stdin);strcpy(msg.texxt,buf);msg.text[strlen(msg.text)-1] = '\0';puts(msg.text);sendto(socketfd,&msg,sizeof(msg),0,(struct sockaddr*)&serveraddr,addrlen);}}else {//接受客户端的消息&&发送某一个客户端的信息while(1) {// puts("father");if(recvfrom(socketfd,&msg,sizeof(msg),0,(struct sockaddr *)&clientaddr,&addrlen) <= 0){perror("father recvfrom error");}if(msg.type == 'L') {//loginputs("YES");login(socketfd,msg,clientaddr);}else if(msg.type == 'C') {//chatchat(socketfd,msg,clientaddr);}else if(msg.type == 'Q') {//quitquit(socketfd,msg,clientaddr);}}}close(socketfd);//正常退出的close处理return 0;
}

客户端

客户端这边的话就稍微简单一点了,因为只有接受和发送这两个事件

  • 发送事件

    • 登陆发送一次
    • 退出发送一次
    • 聊天一直发送
  • 接收事件

    其实我们只用接受服务器给我们发送的消息即可

总体逻辑非常的简单,详情请看代码

CODE

//
// Created by Mangata on 2021/10/27.
//
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>#define N 1024
struct MSG{char type;char name[20];char text[N];
}msg;int port = 8000;
char *ip = "127.0.0.1";//int socketfd;
//struct sockaddr_in serveraddr;/*void my_fun(int signal_) {msg.type = 'Q';if(sendto(socketfd,&msg,sizeof msg,0,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0) {perror("Q - sendto error");exit(1);}printf("You Quit !");kill(getppid(),SIGKILL);exit(1);
}
*/
int main() {struct MSG msg;int socketfd;struct sockaddr_in serveraddr;bzero(&serveraddr,sizeof(serveraddr));socklen_t addrlen;//signal(SIGINT, my_fun);if((socketfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket failed");printf("socket failed");exit(1);}serveraddr.sin_family = AF_INET;
//    serveraddr.sin_addr.s_addr = inet_addr(ip);serveraddr.sin_port = htons(port);inet_pton(AF_INET,"127.0.0.1",&serveraddr.sin_addr);printf("输入用户名:");fgets(msg.name,sizeof(msg.name),stdin);msg.name[strlen(msg.name)-1]='\0';msg.text[0]='\0';msg.type= 'L';printf("name = %s\t type = %c\n",msg.name,msg.type);if(sendto(socketfd,&msg,sizeof(msg),0,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0) {//发送登陆信号perror("fail to sendto");printf("fail to sendto");exit(1);}pid_t pid=fork();if(pid == 0) {//子进程输入数据while(1) {fgets(msg.text,N,stdin);msg.text[strlen(msg.text)-1]='\0';printf("send msg = %s\n",msg.text);if(!strncmp(msg.text,"quit",4)) {msg.type = 'Q';if(sendto(socketfd,&msg,sizeof msg,0,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0) {perror("Q - sendto error");exit(1);}printf("You Quit !");kill(getppid(),SIGKILL);exit(1);}else {msg.type = 'C';if(sendto(socketfd,&msg,sizeof msg,0,(struct sockaddr*)&serveraddr,sizeof(serveraddr)) < 0) {printf("C - sendto error");exit(1);}}}}else {while(1){recvfrom(socketfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, &addrlen);puts(msg.text);}}close(socketfd);return 0;
}

关于BUG

由于写的时间比较短,有一些发现的bug,但是还不清楚原因

  • 当所有的用户退出后,服务器会段错误然后宕机
  • 用户推出后会有一个用户收不到退出消息(这个原因是删除退出用户的链表的问题,只不过不想修了)
  • 服务器断开的时候,客户端不会有反馈,emmm这个其实只是我没写,逻辑还是蛮简单的,可以给用户发送一个Q类型的消息,然后让用户也自动退出即可
    其他倒是没有什么东西了,服务器和用户端的端口是设死了的,所以想要更改的直接改源码就行

效果图

服务器端

客户端口


over

LinuxC语言のUDP简易聊天室 sokcet相关推荐

  1. LinuxC TCP实现简易聊天室

    目录 1.概述 1.1聊天室设计内容 2.系统设计 2.1系统功能设计 2.1.1用户管理 2.1.2聊天室管理 2.1.3聊天管理 2.2系统数据结构设计 2.3系统主要函数设计 3.系统实现 3. ...

  2. java用UDP作简易聊天室

    -------------------------------------------------java用UDP作简易聊天室------------------------------------- ...

  3. Socket编程实现简易聊天室

    1.Socket基础知识 Socket(套接字)用于描述IP地址和端口,是通信链的句柄,应用程序可以通过Socket向网络发出请求或者应答网络请求. Socket是支持TCP/IP协议的网络通信的基本 ...

  4. 基于Node.js + WebSocket 的简易聊天室

    代码地址如下: http://www.demodashi.com/demo/13282.html Node.js聊天室运行说明 Node.js的本质就是运行在服务端的JavaScript.Node.j ...

  5. 2020暑假集训项目——Java简易聊天室

    经过一周的学习与搬砖,我成功的完成了暑假集训的第一个项目--Java简易聊天室,这里对整个项目做一个总结.(文末附下载地址) 本项目支持的功能: 1.可同时开启多个客户端进行多人聊天: 2.可与在线的 ...

  6. 网络编程之java简易聊天室实现

    最近浅学习了一些关于网络编程方面的知识,视频是跟着狂神学习的,可能学习的不是很深 说到网络,相信大家都对TCP.UDP和HTTP协议这些都不是很陌生,学习这部分应该先对端口.Ip地址这些基础知识有一定 ...

  7. 用eclipse和Tomcat写一个简易聊天室

    本文讲述如何用JAVA语言写一个简易的聊天室 简易聊天室的代码实现,因为笔者还是个正在好好学习努力进步的学生,所以代码可能还有很多可以优化的地方,仅供参考. 开发步骤: 第一部分:UI构建 第一步:编 ...

  8. PHP百行代码快速构建简易聊天室

    基本功能:能登陆,聊天,记录在线人数IP事件,能控制聊天的字体颜色,自动把转化聊天中网址为链接地址,能定制聊天室标题.广告信息等等.使用文本作为存储媒体,又兴趣可以参考代码,并且进行扩展. 其实PHP ...

  9. PHP百行代码快速构建简易聊天室 1

    基本功能:能登陆,聊天,记录在线人数IP事件,能控制聊天的字体颜色,自动把转化聊天中网址为链接地址,能定制聊天室标题.广告信息等等.使用文本作为存储媒体,又兴趣可以参考代码,并且进行扩展. 其实PHP ...

最新文章

  1. 中序遍历的非递归算法
  2. 全球及中国医用腋拐行业竞争格局及供需前景预测报告2021年版
  3. LightOJ - 1027 数学期望
  4. kafka streams_Kafka REST Proxy for MapR Streams入门
  5. c语言-基本计算 pm2.5,C语言程序设计题(A卷).doc
  6. JetBrains:更改webstrom、IDEA等主题
  7. Intellij IDEA 构建Spring Web项目 — 用户登录功能
  8. 如何制作频数折线图(详细)
  9. 简单循迹小车实验心得_简单循迹小车的制作
  10. outlook html 图片,Outlook邮件内容中的图片无法显示 图片
  11. java中的gc是什么意思的缩写_gc是什么意思(gc的缩写是什么意思)
  12. python unpack_python struct中pack和unpack
  13. js-函数式编程-柯里化和语义化
  14. UE4 虚幻 常用的流程控制蓝图节点介绍
  15. 大数据运维工作(Linux,OGG,链路监控,Hadoop运维等)
  16. excel从身份证号码中获取邮编信息?
  17. 动手学深度学习:3.16 实战Kaggle比赛:房价预测
  18. stm32f105vct6例程_stm32f105例程 CAN500K USART_LED
  19. 内存耗用:VSS/RSS/PSS/USS
  20. C语言实现可写入文件的账号密码登录系统,密码输入时掩盖,登录界面菜单选择,更改密码系统,课设必备。

热门文章

  1. Mtlab之图形标注
  2. tensorflow实现余弦cosine相似度
  3. Android.mk文件官方使用说明
  4. [课堂实践与项目]IOS优先级的计算器
  5. C语言大数阶乘的求法
  6. BZOJ1027 [JSOI2007]合金 【计算几何 + floyd】
  7. web播放器-jwplayer
  8. 单循环链表(用尾指针表示)
  9. C++ 类的前向声明
  10. C#值类型和引用类型的区别