这个正好是我的一个课堂上机小练习,为了实现这个功能,我们需要了解一下几个知识点

  • udp发送和接受数据的过程
  • select语句功能
  • 如何开启一个线程
  • *如何传输结构体struct

一.udp发送数据和接受数据流程


无论是客户端还是服务器端,刚开始都需要向系统申请套接字socket,然后通过socket来实现发送和接受消息,只不过服务器端需要把该套接字绑定到某个端口通过调用bind()函数(很多书上说bind()函数公开自己所要监听的端口),其实客户端也是可以执行bind()函数来指明发送数据的端口,不过一般不写,系统会自动分配一个端口给它。总的梳理一下
客户端:申请socket——通过该socket发送数据和接受数据
服务器:申请socket——bind()端口——调用recvfrom()阻塞

二.select语句功能

#include<sys/select.h>
#include<sys/time.h>
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)

先说一下select的功能:这属于IO复用模型,进程会受阻与select调用,select同时监听多个套接字,只要有一个套接字响应了,就不会阻塞,可以执行你想要的处理过程,简单一点就是一个监听多个socket功能。
看一下它的参数
maxfdp1:指定带监听的描述符个数,它的值是待测试的最大描述符加1,换句话说调用socket函数返回int参数就是socket文件描述符
read_set,read_set,exceptset:就是我们要监听的socket文件描述符的集合,一个读,一个写,一个异常
timeout:就是指定监听时长(null代表永久)

三.开启线程

#include<pthread.h>
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void), void *restrict arg);)

第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
最后一个参数是运行函数的参数。如果函数不需要参数变成NULL

四.传输结构体

因为socket只能传输字符串,所以我们必须把struct转成char数组才能传输,接收端也只能char数组接受再转成结构体
使用memcpy将文件、结构体、数字等,可以转换为char数组,之后进行传输,接收方在使用memcpy将char数组转换为相应的数据。

void * memcpy ( void * destination, const void * source,size_t num );

 函数说明:从source指向的地址开始拷贝num个字节到以destination开始的地址。其中destination与source指向的数据类型无关。

五.项目代码

说明:因为我是复用老师给的代码中几个函数Sendto和Recv函数,所以需要替换还有头文件需要自己添加一下
客户端

#include    "unp.h"
#include<string.h>
struct fun_para{int port;
};
typedef struct{char username[10];char msg_buf[1024];int type;struct sockaddr_in address;
}MSG;
static void func(void * para){struct fun_para* kk=(struct fun_para *)para;int port=kk->port;int s=socket(AF_INET,SOCK_DGRAM,0);struct sockaddr_in serv;bzero(&serv,sizeof(serv));serv.sin_family=AF_INET;serv.sin_addr.s_addr=htonl(INADDR_ANY);serv.sin_port=htons(port);int sin_len=sizeof(serv);bind(s,(struct sockaddr *)&serv,sizeof(serv));if(s==-1)printf("create socket erro:");for(;;){char recvBuf[1200]={0};MSG *client_msg=(MSG*)malloc(sizeof(MSG));int kk=recvfrom(s,recvBuf,1200,0,(SA*)&serv,&sin_len);memcpy(client_msg,recvBuf,sizeof(MSG));printf("username: %smessage:%s\n",client_msg->username,client_msg->msg_buf);}
}int
main(int argc, char **argv)
{int sockfd;struct sockaddr_in servaddr;pthread_t tid;char sendline[1024];char username[10];if (argc != 3)err_quit("usage: tcpcli <IPaddress> <Port>");sockfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(9877);Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);sockfd=Socket(AF_INET,SOCK_DGRAM,0);bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));struct fun_para para;para.port=atoi(argv[2]);struct sockaddr_in serv;bzero(&serv,sizeof(serv));serv.sin_family=AF_INET;serv.sin_addr.s_addr=htonl(INADDR_ANY);serv.sin_port=htons(atoi(argv[2]));printf("client port: %d\n",atoi(argv[2]));printf("input your name\n");int flag=0;//注册消息// Sendto(sockfd,&msg,sizeof(struct MSG),0,(SA*)&servaddr,sizeof(servaddr));if(pthread_create(&tid,NULL,&func ,&para)!=0){printf("thread_create Failed\n");} while(Fgets(sendline,1024,stdin)!=NULL){MSG *send=(MSG*)malloc(sizeof(MSG));if(flag==0){strcpy(send->username,sendline);strcpy(send->msg_buf,sendline);strcpy(username,sendline);send->type=0;flag++;}else{strcpy(send->msg_buf,sendline);strcpy(send->username,username);send->type=1;}send->address=serv;char information[1200]={0};memcpy(information,send,sizeof(MSG));Sendto(sockfd,information,sizeof(information),0,(SA*)&servaddr,sizeof(servaddr));}//str_cli(stdin, sockfd);       /* do it all */exit(0);
}

服务器

#include "unp.h"typedef struct {char username[10];char msg_buf[1024];int type;struct sockaddr_in address;
}MSG;
// 建立一个链表存储客户端的功能
typedef struct client{struct sockaddr_in ct_addr;struct client* next;
}CNODE,*pCNODE;
void list_insert(pCNODE *phead,pCNODE p){p->next=*phead;*phead=p;
}
void msg_brocast(int send_sockfd,char* msg,pCNODE phead){while(phead){printf("Port: %s %d message:%s ",inet_ntoa((phead->ct_addr).sin_addr),htons((phead->ct_addr).sin_port),msg);Sendto(send_sockfd,msg,1200,0,(SA*)&(phead->ct_addr),sizeof(SA));phead=phead->next;
}
}
int
main(int argc, char **argv)
{int sockfd,send_socket;int maxfdp1;int select_int;struct sockaddr_in addr,cli;pCNODE my_list=NULL;sockfd=Socket(AF_INET,SOCK_DGRAM,0);send_socket=Socket(AF_INET,SOCK_DGRAM,0);addr.sin_family=AF_INET;addr.sin_port=htons(9877);//绑定端口号9877addr.sin_addr.s_addr=htonl(INADDR_ANY);Bind(sockfd,(SA*)&addr,sizeof(addr));fd_set rset;FD_ZERO(&rset);while(1){FD_SET(sockfd,&rset);maxfdp1=sockfd+1;select_int = select(maxfdp1,&rset,NULL,NULL,NULL);if(select_int<0){printf("erro\n");return;}else if(select_int==0){printf("timeout\n");}else{if(FD_ISSET(sockfd,&rset)){pCNODE pNew =(pCNODE)calloc(1,sizeof(CNODE));printf("receve data\n");char recvBuf[1200]={0};MSG *client_msg=(MSG*)malloc(sizeof(MSG));int lent=sizeof(cli);printf("bigsize is %d\n",sizeof(MSG));int kk=recvfrom(sockfd,recvBuf,1200,0,(SA*)&(cli),&lent);  printf("1address: %s  %d \n",inet_ntoa(cli.sin_addr),htons(cli.sin_port));     memcpy(client_msg,recvBuf,sizeof(MSG));pNew->ct_addr=client_msg->address;if(client_msg->type==0){printf("添加新成员\n");list_insert(&my_list,pNew);}else if(client_msg->type==1){msg_brocast(send_socket,recvBuf,my_list);       }// printf("IP地址为:%s 端口号为:%d",inet_ntoa(cli.sin_addr),htons(cli.sin_port));}
}
}
}

Linux下编写udp群聊室相关推荐

  1. Linux下编写UDP/TCP版本的服务器和客户端的流程

    Linux下编写UDP/TCP版本的服务器和客户端的流程 文章目录 Linux下编写UDP/TCP版本的服务器和客户端的流程 一:UDP和TCP的区别 二.UDP编写服务器的步骤 三.UDP编写客户端 ...

  2. 在Linux下编写Daemon

    在Linux下编写Daemon 转自:http://blog.163.com/prevBlogPerma.do?host=manyhappy163&srl=164476831201071811 ...

  3. 专业的LaTeX: 在Linux下编写高质量的文档

    专业的LaTeX: 在Linux下编写高质量的文档 Linux下的OpenOffice.KWord等字处理软件虽然在功能上与Microsoft Word类似,但目前在易用性和可用性方面仍然存在许多不足 ...

  4. 利用多线程实现linux下C语言的聊天室程序:

    转载:http://www.360doc.com/content/16/0421/11/478627_552531090.shtml 利用多线程实现linux下C语言的聊天室程序: 客户端代码: th ...

  5. linux udp 端口映射,Linux下的UDP/TCP端口映射(netcat and socat)

    原文链接: http://www.wenquan.name/?p=1158 说起来有点土,事到如今才第一次用socat. 不过今天看了一眼,netcat(nc)这东西ms已经N年没有人维护了.最先有个 ...

  6. spdlog linux编译出错,Linux下编写Makefile引入第三方库

    Linux下编写Makefile引入第三方库 前言:一直在使用CmakaList 生成Makefile文件,其实很少去写Makefile,但是最近帮朋友处理了一个Makefile引入第三方库的问题,就 ...

  7. linux udp 端口测试,RAKsmart:Linux下TCP/UDP 端口测试及验证方法说明

    RAKsmart:Linux下TCP/UDP 端口测试及验证方法说明2020-06-11 在 Linux 系统中,有时需要在系统中测试端口的连通性,以便确认系统的TCP.UDP协议栈是否可以正常运行. ...

  8. 基于Linux下的即时通讯聊天室项目(全代码 有注释 可直接运行)

    基于Linux下的即时通讯聊天室项目 一.序言 二.具体功能 三.系统客户要求 四.具体代码 1.服务器代码 2.客户端代码 一.序言 最近在写一个基于Linux下的聊天工具 它适合于局域网内所有人进 ...

  9. 在Linux下编写Daemon(Linux启动流程2)

    在Linux(以Redhat Linux Enterprise Edition 5.3为例)下,有时需要编写Service.Service也是程序,一般随系统启动用户不干预就不退出的程序,可以称为Se ...

  10. linux下和嵌入式linux下通过udp接收来自vlc播放器的视频并转发播放

    1.最近需要在linux下基于udp写一个应用程序,实现接收vlc播放器发送的视频流,并将接收到的视频流转发到另一个vlc客户端播放.并将这个应用程序交叉编译到ARM上执行测试通过.主机端ip地址:1 ...

最新文章

  1. 全球及中国磁性分离滑轮行业竞争战略及未来产销需求预测报告2022版
  2. android 保存文件_Android 数据库操作框架LitePal使用介绍(一)
  3. 线程调度优先级和关联性
  4. 金山云服务器内网带宽,性能提升40%!第三代金山云服务器全面覆盖不同企业计算力需求...
  5. 编译32位_实战经验:在Windows平台编译x264
  6. Java中数据库模糊查询写法
  7. PHP+mysql共享自行车租赁管理系统
  8. SecureCRT解压版使用
  9. opencv 模板匹配形状匹配
  10. 曲面局部理论介绍——从曲面的概念、基本形式到高斯曲率及其 Pthyon 计算
  11. flutter TextField删除文字后光标跑到文字末尾
  12. 运维自动化之-----ansible之intnet和http协议技术 (10)
  13. 人像柯达金胶片效果调色
  14. android 微博一键关注,新浪微博怎样一键关注多个好友
  15. MCS:离散随机变量——Binomial分布
  16. 考研日语线上笔记(八):完型易混易考知识点梳理篇
  17. java 实现繁简字体转换(笨方法)
  18. HDU 6555 The Fool
  19. FFmpeg源代码简单分析-编码-av_write_trailer()
  20. 【mybatis原理工作原理】

热门文章

  1. java打包apk_APK打包流程
  2. android 年月日倒计时,手机日期倒计时
  3. 计算机基础进制转换(二进制、八进制、十进制、十六进制)
  4. h5 如何录音保存上传_html5 网页录音、试听以及上传
  5. java中级参考教材答案_Java中级开发工程师笔试题及答案(2)
  6. matlab 线型、标记、颜色
  7. PDF如何编辑,怎么删除PDF页眉页脚
  8. win10隐藏任务栏_让你的 Windows 任务栏智能化起来
  9. python sobel算子_图像边缘检测:Canny算子、Prewitt算子和sobel算子
  10. Java设计模式(思维导图)