1、UDP局域网聊天室实现1.UDP聊天室基本功能分析2.UDP实现流程分析(客户端,服务器)3.UDP聊天功能实现(群聊,私聊)4.总结1.UDP基本功能分析 在学完网络中UDP的编程之后,我们想通过实现一个UDP的聊天室来实际运用一下!那么聊天室需要哪些功能呢!我们可以类比 QQ的聊天的功能,QQ最基本的功能:1.登录注册2.聊天3.退出 那么我们也大致实现这些功能。我们做一些简化,不需要登录注册的功能,登录时,给所有连接上来的客户端发送消息,告诉说谁登录上来了客户端给服务器发送消息时就指定需要聊天的对象。我们分析一下我们聊天的过程:(1).我们实心的效果是如:A - B (A与B进行聊天)
2、A与B聊天 实际上是 A - “消息”-服务器转发-“消息“-B(2).服务器发送消息服务器AB-C这是一个群发的消息2.UDP实现流程分析(客户端,服务器) 分析客户端的流程:客户端:整体的流程首先是 网络的基本编程s o c k e tp i d = f o r k ( ) ;i f ( p i d 0 ) / / 父进程, 主要功能,数据发送w h i l e ( 1 ) / / 大循环 1 . 发送一个登录的消息,告诉大家我上线了2 . . 从键盘获得数据 a . 聊天的对端的名字S Y S T E R M 群聊消息 非S Y S T E R M 私聊消息c . 聊天的内容 d . 输
3、入q u i t 时,结束3 . 聊天结束,发送一个聊天结束的消息给大家,告诉大家说我下线了 e l s e i f ( p i d = = 0 ) / / 子进程,主要功能,数据接收 w h i l e ( 1 ) 1 . 接收数据 2 . 显示数据 分析“服务器端“的流程:整体的流程是 UDP的网络服务器的 基本编程s o c k e tb i n dp i d = f o r k ( ) ;i f ( p i d 0 ) / / 父进程 发数据,系统级别的消息,给所有客户端发送消息1 . 从键盘获得数据2 . 发送给服务器自己 e l s e i f ( p i d = = 0 ) /
4、/ 子进程,接收数据,处理消息1 . 接收发过来的消息2 . 根据消息类型进行处理a . 登录消息b . 退出消息d . 群聊消息这三类消息都需要给每个客户端发送e . 私聊消息 需要进行指定聊天对象 分析数据结构体:首先我们要考虑一个服务器端的程序如何给每个客户端依次发送消息?1.UDP中如果向某个客户端发送消息,那么我们需要指定对端的地址信息(ip+port)的信息那么服务器端如何保留这些客户端的信息就是我们需要关注的问题?想想看如何可以实现?。balabala那么我们这里就用的非常广泛的一种数据结构,链表来实现对 客户端地址信息的保留。链表的特点,就不需要说了。我们这里主要关注其 可以动
5、态增长的特点来使用那么这一问题解决之后,我们要做的就是实现相关的数据结构的操作。那么我们分析一下该链表的数据结构构建。数据结构 地址信息节点:t y p e d e f s t r u c t n o d e s t r u c t s o c k a d d r _ i n a d d r ; s t r u c t n o d e * n e x t ; d a t a _ t ; / / 消息类型# d e f i n e C L I E N T _ T A L K 1 0 0 / / 私聊 + c l i e n t n a m e# d e f i n e S E R V E R _
6、 T A L K 2 0 0 / / 给所有客户端发送# d e f i n e C L I E N T _ L O G I N 3 0 0 / / 有客户端登陆,地址存入链表# d e f i n e C L I E N T _ L O G O U T 4 0 0 / / 有客户端退出,地址从链表删除/ / 消息结构体t y p e d e f s t r u c t l o n g t y p e ; / / 消息类型 c h a r s r c _ n a m e 2 0 ; / / 自己客户端名字,在运行客户端命令行指定名字c h a r d s t _ n a m e 2 0 ; /
7、 / 对方客户端名字,在运行客户端命令行指定名字 / / 客户端登陆时给服务器发自己的名字和地址,/ * * / / 私聊时客户端用于指定对方名字,在服务器端可以根据名字找到对应对方的地址 / / 服务器端转发私聊消息时如果要知道是谁发送,根据地址在链表中查找名字/ * * /c h a r m t e x t B U F _ S Z ; / / 消息正文 m s g _ t ; 分析函数实现:服务器端:1 . i n t s e n d _ a s s i g n _ c l i e n t ( i n t s e r f d , L i n k l i s t * h e a d , m
8、s g _ t * p m s g ) ;功能:通过名字查找对应的客户端是否存在,存在则发送消息不存在返回出错信息表示客户端不存在给指定的客户端发送消息参数: s e r f d 进行通信的s o c k e t 对应的文件描述符 h e a d 链表的头指针 p m s g 指定的客户端的名字返回值:0 成功 - 1 表示没有找到对应的客户端 2 . b r o a d c a s t _ a l l _ c l i e n t ( i n t s e r f d , L i n k l i s t * h e a d , m s g _ t * p m s g ) ;功能:实现对每个客户端
9、发送消息参数: 同上返回值:0 成功3 . s t r u c t s o c k a d d r _ i n * f i n d _ a s s i g n _ c l i e n t ( L i n k l i s t * h e a d , c h a r * n a m e ) ;功能: 从链表中查找对应名字的客户端,找到之后返回其地址信息 3.UDP聊天功能实现(群聊,私聊) /1.头文件# i f n d e f _ H E A D _ H _# d e f i n e _ H E A D _ H _# i n c l u d e # i n c l u d e # i n c l
10、 u d e # i n c l u d e # i n c l u d e # i n c l u d e # i n c l u d e # i n c l u d e # i n c l u d e # i n c l u d e / * S e e N O T E S * /# i n c l u d e # i n c l u d e # i n c l u d e # i n c l u d e / / # d e f i n e _ D E B _ 1# d e f i n e B U F _ S Z 1 0 2 4# d e f i n e C L I E N T _ T A
11、L K 1 0 0# d e f i n e S E R V E R _ T A L K 2 0 0# d e f i n e C L I E N T _ L O G I N 3 0 0# d e f i n e C L I E N T _ L O G O U T 4 0 0# d e f i n e h a n d l e _ e r r o r ( m s g ) d o p e r r o r ( m s g ) ; e x i t ( E X I T _ F A I L U R E ) ; w h i l e ( 0 )t y p e d e f s t r u c t s o c k
12、 a d d r s a _ t ;t y p e d e f s t r u c t l o n g t y p e ; / / 消息类型 / / c h a r n a m e 2 0 ; / / 客户端名字/ / L O N G I N 时给服务器发自己的名字,私聊时指定对方名字c h a r s r c _ n a m e 2 0 ; c h a r d s t _ n a m e 2 0 ;c h a r m t e x t B U F _ S Z ; / / 消息正文 m s g _ t ;/ / 链表的结点数据/ / t y p e d e f s t r u c t s o c
13、 k a d d r _ i n d a t a _ t ; / / / t y p e d e f i n t d a t a _ t ;t y p e d e f s t r u c t s t r u c t s o c k a d d r _ i n a d d r ; / / 客户端地址c h a r n a m e 2 0 ; / / 客户端名字 d a t a _ t ;/ / 链表的结点类型t y p e d e f s t r u c t _ n o d e _ d a t a _ t d a t a ;s t r u c t _ n o d e _ * n e x t ;
14、l i n k n o d e _ t , * l i n k l i s t _ t ;/ / 创建链表的操作e x t e r n l i n k l i s t _ t c r e a t e _ e m p t y _ l i n k l i s t ( ) ;/ / 插入链表e x t e r n i n t i n s e r t _ h e a d _ l i n k l i s t ( l i n k l i s t _ t h e a d , d a t a _ t d a t a ) ;/ / 删除链表e x t e r n i n t d e l e t e _ a s
15、s i g n _ n o d e ( l i n k l i s t _ t h e a d , d a t a _ t d a t a ) ;# e n d i f /2.链表操作的函数实现# i n c l u d e “ h e a d . h “l i n k l i s t _ t c r e a t e _ e m p t y _ l i n k l i s t ( )l i n k n o d e _ t * h e a d = N U L L ;h e a d = ( l i n k n o d e _ t * ) m a l l o c ( s i z e o f ( l
16、i n k n o d e _ t ) ) ; h e a d - n e x t = N U L L ;r e t u r n h e a d ;i n t i n s e r t _ h e a d _ l i n k l i s t ( l i n k l i s t _ t h e a d , d a t a _ t d a t a )l i n k n o d e _ t * t e m p = N U L L ;t e m p = ( l i n k n o d e _ t * ) m a l l o c ( s i z e o f ( l i n k n o d e _ t )
17、) ; t e m p - d a t a = d a t a ;t e m p - n e x t = h e a d - n e x t ; h e a d - n e x t = t e m p ;r e t u r n 0 ;i n t d e l e t e _ a s s i g n _ n o d e ( l i n k l i s t _ t h e a d , d a t a _ t d a t a )l i n k n o d e _ t * p = h e a d ; l i n k n o d e _ t * t e m p = N U L L ;/ * w h i l
18、 e ( p - n e x t i f ( p - n e x t = = N U L L ) r e t u r n - 1 ; t e m p = p - n e x t ; p - n e x t = t e m p - n e x t ;f r e e ( t e m p ) ; t e m p = N U L L ;r e t u r n 0 ;# i f 0i n t p r i n t _ l i n k l i s t ( l i n k l i s t _ t h e a d ) l i n k n o d e _ t * p = h e a d - n e x t ;w
19、h i l e ( p ) p r i n t f ( “ % - 4 d “ , p - d a t a ) ; p = p - n e x t ; p u t c h a r ( n ) ;r e t u r n 0 ;i n t m a i n ( i n t a r g c , c o n s t c h a r * a r g v )l i n k l i s t _ t h e a d = N U L L ; i n t i = 0 ;h e a d = c r e a t e _ e m p t y _ l i n k l i s t ( ) ;f o r ( i = 0 ; i
20、 n e x t ;w h i l e ( p ) i f ( s t r n c m p ( p m s g - s r c _ n a m e , p - d a t a . n a m e , 2 0 ) ! = 0 ) / / 不给自己回发消息s e n d t o ( s o c k f d , p m s g , s i z e o f ( m s g _ t ) , 0 , ( s a _ t * ) / * p r i n t f ( “ m s g . m t e x t = % s n “ , m s g . m t e x t ) ; * /# i f d e f _ D
21、 E B _ p u t s ( “ = = = = = = = = = = = = = = b r o a d c a s t = = = = = = = = = = = = = = = = = = = “ ) ;p r i n t f ( “ I p : t % s n “ , i n e t _ n t o a ( p - d a t a . s i n _ a d d r ) ) ; p r i n t f ( “ P o r t : t % d n “ , n t o h s ( p - d a t a . s i n _ p o r t ) ) ;p r i n t f ( “ I
22、 n f o : t % s n “ , p m s g - m t e x t ) ; p u t s ( “ = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = “ ) ;# e n d i fp = p - n e x t ; r e t u r n 0 ;s t r u c t s o c k a d d r _ i n * f i n d _ c l i e n t _ a d d r ( l i n k l i s t _ t a d d r l i s t , c
23、h a r * p n a m e )l i n k n o d e _ t * p = a d d r l i s t - n e x t ; w h i l e ( p ) i f ( s t r c m p ( p - d a t a . n a m e , p n a m e ) = = 0 ) b r e a k ;p = p - n e x t ; i f ( p = = N U L L ) r e t u r n N U L L ;r e t u r n i n t s e n d _ a s s i g n _ c l i e n t ( i n t s o c k f d ,
24、 l i n k l i s t _ t a d d r l i s t , m s g _ t * p m s g , s t r u c t s o c k a d d r _ i n * m y a d d r )s t r u c t s o c k a d d r _ i n * p a d d r = N U L L ; p a d d r = f i n d _ c l i e n t _ a d d r ( a d d r l i s t , p m s g - d s t _ n a m e ) ; i f ( p a d d r = = N U L L ) / / 未找到指
25、定客户端,回送失败信息p m s g - t y p e = C L I E N T _ T A L K ; s p r i n t f ( p m s g - m t e x t , “ E r r o r , % s c l i e n t n o t e x i s t ! n “ , p m s g - d s t _ n a m e ) ;s p r i n t f ( p m s g - d s t _ n a m e , “ % s “ , p m s g - s r c _ n a m e ) ; s e n d t o ( s o c k f d , p m s g , s
26、i z e o f ( m s g _ t ) , 0 , ( s a _ t * ) m y a d d r , s i z e o f ( s a _ t ) ) ;r e t u r n - 1 ; / / 找到指定客户端,直接转发 s e n d t o ( s o c k f d , p m s g , s i z e o f ( m s g _ t ) , 0 , ( s a _ t * ) p a d d r , s i z e o f ( s a _ t ) ) ;r e t u r n 0 ;i n t m a i n ( i n t a r g c , c o n s t
27、c h a r * a r g v )/ * i n t n = 0 ; * / m s g _ t m s g ;p i d _ t p i d = 0 ; d a t a _ t d a t a ;i n t s o c k f d = 0 ; / * s t a t i c s t r u c t s o c k a d d r _ i n a d d r b a k ; * / * c h a r b u f B U F _ S Z = 0 ; * / s o c k l e n _ t a d d r l e n = s i z e o f ( s a _ t ) ;s t r u
28、c t s o c k a d d r _ i n p e e r _ a d d r ; s t r u c t s o c k a d d r _ i n s e r v e r _ a d d r ;l i n k l i s t _ t a d d r _ l i s t = N U L L ; / / 1 . 命令行参数的处理i f ( a r g c ! = 3 ) f p r i n t f ( s t d e r r , “ U s a g e % s i p p o r t n “ , a r g v 0 ) ; e x i t ( E X I T _ F A I L U R
29、 E ) ; / / 1 . 创建s o c k e ti f ( ( s o c k f d = s o c k e t ( A F _ I N E T , S O C K _ D G R A M , 0 ) ) 0 ) / / 服务器端:父进程 1 . 从键盘获得数据,2 . 发送给服务器自己,有子进程接受处理 w h i l e ( 1 ) p u t c h a r ( ) ; f g e t s ( m s g . m t e x t , s i z e o f ( m s g . m t e x t ) , s t d i n ) ;m s g . m t e x t s t r
30、l e n ( m s g . m t e x t ) - 1 = 0 ;m s g . t y p e = S E R V E R _ T A L K ; / / 服务器端的消息,相当与系统消息 s p r i n t f ( m s g . s r c _ n a m e , “ S Y S T E M “ ) ; / / 服务器端名为S Y S T E R Ms e n d t o ( s o c k f d , e l s e / / 创建空链表a d d r _ l i s t = c r e a t e _ e m p t y _ l i n k l i s t ( ) ; w h
31、 i l e ( 1 ) r e c v f r o m ( s o c k f d , # i f d e f _ D E B _ p u t s ( “ = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = “ ) ;p r i n t f ( “ I p : t % s n “ , i n e t _ n t o a ( p e e r _ a d d r . s i n _ a d d r ) ) ; p r i n t f ( “ P o r t : t % d n “ , n t o h s ( p e
32、e r _ a d d r . s i n _ p o r t ) ) ;p r i n t f ( “ I n f o : t % s n “ , m s g . m t e x t ) ; p u t s ( “ = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = “ ) ;# e n d i fs w i t c h ( m s g . t y p e ) c a s e C L I E N T _ T A L K : / / 转发给对应的客户端 s e n d _ a s s i g n _ c l i e
33、 n t ( s o c k f d , a d d r _ l i s t , b r e a k ; c a s e S E R V E R _ T A L K : / / 转发给所有的客户端# i f d e f _ D E B _ p r i n t f ( “ S o m e o n e t a l k ! n “ ) ;# e n d i f b r o a d c a s e _ t o _ c l i e n t ( s o c k f d , a d d r _ l i s t , b r e a k ; c a s e C L I E N T _ L O G I N : /
34、 / 登录消息 ,通知所有的客户端# i f d e f _ D E B _ p r i n t f ( “ C L I E N T _ L O G I N n “ ) ;# e n d i f b r o a d c a s e _ t o _ c l i e n t ( s o c k f d , a d d r _ l i s t , / / 链表中数据 b z e r o ( d a t a . a d d r = p e e r _ a d d r ; s p r i n t f ( d a t a . n a m e , “ % s “ , m s g . s r c _ n a
35、m e ) ;i n s e r t _ h e a d _ l i n k l i s t ( a d d r _ l i s t , d a t a ) ; / * i n s e r t _ h e a d _ l i n k l i s t ( a d d r _ l i s t , p e e r _ a d d r ) ; * /b r e a k ; c a s e C L I E N T _ L O G O U T : / / 登出消息,通知所有的客户端# i f d e f _ D E B _ p r i n t f ( “ C L I E N T _ L O G O U T
36、 n “ ) ;# e n d i f / / 链表中数据b z e r o ( d a t a . a d d r = p e e r _ a d d r ;s p r i n t f ( d a t a . n a m e , “ % s “ , m s g . s r c _ n a m e ) ;d e l e t e _ a s s i g n _ n o d e ( a d d r _ l i s t , d a t a ) ; / * d e l e t e _ a s s i g n _ n o d e ( a d d r _ l i s t , p e e r _ a d d
37、 r ) ; * /b r o a d c a s e _ t o _ c l i e n t ( s o c k f d , a d d r _ l i s t , b r e a k ;d e f a u l t : p r i n t f ( “ O t h e r m e s s a g e t y p e ! n “ ) ;c o n t i n u e ; c l o s e ( s o c k f d ) ;r e t u r n 0 ; /4.客户端# i n c l u d e “ h e a d . h “/ / . / c l i e n t i p p o r t c
38、l i e n t _ n a m ei n t m a i n ( i n t a r g c , c o n s t c h a r * a r g v ) / * i n t n = 0 ; * /m s g _ t m s g ; p i d _ t p i d = 0 ;i n t s o c k f d = 0 ; / * c h a r b u f B U F _ S Z = 0 ; * /s o c k l e n _ t a d d r l e n = s i z e o f ( s a _ t ) ; / * s t r u c t s o c k a d d r _ i
39、n p e e r _ a d d r ; * /s t r u c t s o c k a d d r _ i n s e r v e r _ a d d r ; / / 1 . 命令行参数的判断i f ( a r g c 0 ) / / 父进程 1 . 从键盘获得数据,2 . 发送给服务器端 w h i l e ( 1 ) p r i n t f ( “ I n p u t c l i e n t n a m e : “ ) ; / / 填入对方名字f g e t s ( m s g . d s t _ n a m e , s i z e o f ( m s g . d s t _ n a
40、 m e ) , s t d i n ) ; m s g . d s t _ n a m e s t r l e n ( m s g . d s t _ n a m e ) - 1 = 0 ;/ / 清空输入缓冲区 / * w h i l e ( g e t c h a r ( ) ! = n ) ; * / * p u t c h a r ( ) ; * / p r i n t f ( “ I n p u t : “ ) ;/ / 填入内容 f g e t s ( m s g . m t e x t , s i z e o f ( m s g . m t e x t ) , s t d i
41、n ) ;m s g . m t e x t s t r l e n ( m s g . m t e x t ) - 1 = 0 ; i f ( s t r n c m p ( m s g . m t e x t , “ q u i t “ , 4 ) = = 0 )b r e a k ;i f ( s t r n c m p ( m s g . d s t _ n a m e , “ S Y S T E R M “ , 7 ) = = 0 ) / / 填入“ 群聊“ 消息类型 m s g . t y p e = S E R V E R _ T A L K ; e l s e / / 填入“
42、私聊“ 消息类型 m s g . t y p e = C L I E N T _ T A L K ; / / 填入客户端自己名字s p r i n t f ( m s g . s r c _ n a m e , “ % s “ , a r g v 3 ) ;s e n d t o ( s o c k f d , / / 填充登出消息 / / 填入消息类型m s g . t y p e = C L I E N T _ L O G O U T ; / / 填入客户端自己名字s p r i n t f ( m s g . s r c _ n a m e , “ % s “ , a r g v 3 )
43、 ; s p r i n t f ( m s g . m t e x t , “ c l i e n t l o g o u t ! “ ) ;s e n d t o ( s o c k f d , k i l l ( p i d , S I G U S R 1 ) ; / / 杀死子进程w a i t ( N U L L ) ; / / 回收子进程 e l s e w h i l e ( 1 ) r e c v f r o m ( s o c k f d , p u t s ( “ = = = = = = = = = = = = = = = = = = = = = = = = = = = =
44、 = = = = = “ ) ;# i f d e f _ D E B _ p r i n t f ( “ I p : t % s n “ , i n e t _ n t o a ( s e r v e r _ a d d r . s i n _ a d d r ) ) ;p r i n t f ( “ P o r t : t % d n “ , n t o h s ( s e r v e r _ a d d r . s i n _ p o r t ) ) ;# e n d i fp r i n t f ( “ : t % s n “ , m s g . s r c _ n a m e , m
45、 s g . m t e x t ) ; p u t s ( “ = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = “ ) ; c l o s e ( s o c k f d ) ;r e t u r n 0 ; 运 行 验 证 :1 . 分 别 运 行 客 户 端 和 服 务 器 程 序. / s e r v e r 1 2 7 . 0 . 0 . 1 8 8 8 8 2 . 运 行 两 个 客 户 端 程 序 . / c l i e n t 1 2 7 . 0 . 0 . 1 8 8 8 8 AA 客 户 端
46、 运 行 之 后 , 运 行 B 客 户 端. / c l i e n t 1 2 7 . 0 . 0 . 1 8 8 8 8 B之 后 A 端 的 效 果服 务 器 发 送 “ 系 统 级 别 “ 的 消 息客 户 端 A 的 情 况 :客 户 端 B 的 情 况 :注 意 : B 本 身 不 会 接 受 到 自 己 的 群 聊 消 息后 续 可 以 依 次 测 试 : 1 . 服 务 器 端 输 入 信 息 发 送 消 息 的 情 况 2 . 输 入 q u i t 退 出 的 情 况 3 . 指 定 客 户 端 进 行 聊 天 的 情 况4.总结 本练习集合了数据结构的相关使用,以及UDP网络编程的知识,实现了一个小的功能,初步体会了知识之间的一个贯穿和学习 学习要掌握逻辑性,理清关系。理解+记忆的学习!