Linux下编写udp群聊室
来源:互联网 发布:东华软件his系统 编辑:程序博客网 时间:2024/05/27 02:26
这个正好是我的一个课堂上机小练习,为了实现这个功能,我们需要了解一下几个知识点
- 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); }}intmain(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 ,¶)!=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;}}intmain(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);//绑定端口号9877 addr.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群聊室
- 编写Linux下的UDP Client/Server程序
- 编写Linux下的UDP Client/Server程序
- 编写Linux下的UDP Client/Server程序
- 编写Linux下的UDP Client/Server程序
- 编写Linux下的UDP Client/Server程序
- 编写Linux下的UDP Client/Server程序
- 编写Linux下的UDP Client/Server程序
- 编写Linux下的UDP Client/Server程序
- 编写Linux下的UDP Client/Server程序
- linux下UDP编程
- Linux下UDP端口扫描
- linux下udp编程
- linux下使用boost库编写UDP客户端/服务器程序官方简易教程
- Linux下的UDP编程
- Linux 下的TCP/UDP
- linux下的udp句柄
- linux下udp大文件传输
- iOS 切指定圆角,防止离屏渲染
- mybatis自连接查询子集合(一对多查询)
- mysql 分页语句limit使用与优化
- oracle数据库自带函数
- 用Vim完成BashShell脚本编写(bash-support : Bash IDE)
- Linux下编写udp群聊室
- zabbix调优
- Spring Boot 应用可视化监控(Prometheus + Grafana)
- C++ STL笔记
- 递归
- 两个项目之间如何通信
- EJb状态和无状态bean区分
- 15.3Sum
- HashMap工作原理