1、实现原理

为了最大限度的减少服务器的负担,这里使用P2P模式实现仿QQ的聊天功能,服务器端和客户端职责如下:

1)服务器端:通过TCP方式实现客户端身份认证、向已经认证的用户推送好友信息、向其他登录用户推送新的好友IP和状态。

2)客户端:TCP方式从服务器端获取好友列表及状态,UDP方式实现群聊和单聊。

2、服务器端实现代码:

const(Msgtype_log="0"Msgtype_state="1"Msgtype_users="2"Msgtype_info="3"Msgtype_info_1="4"Msgtype_line="5"
)const(TcpType="tcp4"LocalAddr="127.0.0.1:8880"
)type OutError struct{string
}func(e *OutError) Error() string{return e.string
}var userArrs usr.Users;
var connMaps map[string]net.Connfunc main() {fmt.Println("初始化用户信息")connMaps=map[string]net.Conn{}//获取用户列表存储文件filepath:=getXmlFile()//读取所有用户列表,用户验证登录和推送用户朋友信息users,err:=getXmlContent(filepath)if err!=nil{return} userArrs=usersuserArrs.RMutex=new(sync.RWMutex)fmt.Println("创建监听服务对象")//创建一个TCP服务端tcpaddr,_:= net.ResolveTCPAddr(TcpType, LocalAddr);//创建一个监听对象listenTcp,err:=net.ListenTCP(TcpType, tcpaddr);//监听对象创建成功if(err==nil){fmt.Println("监听对象创建成功")//通过循环的方式等待用户的连接for{fmt.Println("等待用户连接......")//阻塞状态,持续等待用户的连接conn,conerr:=listenTcp.Accept()//如果有用户成功连接if(conerr==nil){        addr:=conn.RemoteAddr().String()if _,ok:=connMaps[addr];!ok{connMaps[addr]=connfmt.Println(conn.RemoteAddr().String()+" 连接成功")//异步方式读取客户端信息go ReadFromTCP(conn);}}        }}}//读取客户端信息
func ReadFromTCP(conn net.Conn){           var condion bool=truefor condion{data,ln:=read(conn)if ln > 0 {logValue:=string(data[0:1]) valueByte:=data[1:ln]switch logValue{case Msgtype_log:if user,ok:=checkUser(valueByte);ok{fmt.Println("验证成功......")//验证成功,返回successfulsuccValue:="1successful"        sendToCLient(conn,[]byte(succValue))    //更新用户信息userArrs.UpUser(user,conn,true) //获取用户好友列users:=userArrs.GetFriends(user)fmt.Println(users)//发送好友列表userByte,_:=WriteToByte(users)userContent:=append([]byte("2"),userByte...)                          sendToCLient(conn,userContent)//将该用户的登录状态发送给已经登录的好友for i:=0;i<len(users);i++{if users[i].Linestate{if userConn,ok:=connMaps[users[i].Address];ok{svalue:=fmt.Sprintf("%s%s,%s,%s", Msgtype_line,user.Name,user.Address,"1")fmt.Println(svalue)sendToCLient(userConn,[]byte(svalue))}}}                              }case Msgtype_state:condion=falsecase Msgtype_users:condion=falsedefault:condion=false                               }           }else{condion=false}}
}func sendToCLient(conn net.Conn,value []byte){conn.Write(value)
}func checkUser(valueByte []byte)(*usr.User,bool){values:=string(valueByte)valuesArr:=strings.Split(values, ",")if len(valuesArr)==3{user:=userArrs.CheckUser(valuesArr[0],valuesArr[1])if user!=nil{       return user,true}                       }       return nil,false
}func read(conn net.Conn)([]byte,int){bufsize:=256buf:=make([]byte,bufsize)var bufs []byteln:=0for{n,err:=conn.Read(buf)if n>0{ln+=n}bufs=append(bufs,buf...)if n<bufsize{break}if err!=nil{break}}return bufs,ln
}func WriteToByte(users []usr.User)([]byte, error){return json.Marshal(users)}func ReadFromByte(b []byte)([]usr.User,error){var users []usr.Usererr:=json.Unmarshal(b,  &users)return users,err
}//读取xml配置文件
func getXmlContent(xmlfile string)(users usr.Users,err error){content, inerr := ioutil.ReadFile(xmlfile)if inerr!=nil{err=inerrfmt.Println("open xml file err",inerr)return}var result usr.Usersinerr = xml.Unmarshal(content, &result)if inerr!=nil{err=inerrfmt.Println("read xml file to Users err",inerr)return}return result,nil
}//获取xml文件目录
func getXmlFile()string{dir, err := filepath.Abs(filepath.Dir(os.Args[0]))var filepath string;if err == nil {index:=strings.LastIndex(dir, "\\")filepath=string([]byte(dir)[:index+1])+"src\\xml\\users.xml"}return filepath
}

2、客户端实现代码:

const(TcpType="tcp4"UdpType="udp"
)const(Msgtype_log="0"Msgtype_state="1"Msgtype_users="2"Msgtype_info="3"Msgtype_info_1="4"Msgtype_line="5"
)type UdpConns struct{rMutex *sync.RWMutexconnMaps map[string] *net.UDPConn
}func(u *UdpConns) add(address string,conn *net.UDPConn){u.rMutex.Lock()_,ok:=u.connMaps[address]if !ok{u.connMaps[address]=conn}u.rMutex.Unlock()
}func(u *UdpConns) get(address string)*net.UDPConn{u.rMutex.RLock()defer u.rMutex.RUnlock()return u.connMaps[address]
}func(u *UdpConns) del(address string){u.rMutex.RLock()defer u.rMutex.RUnlock()delete(u.connMaps,address)
}var tcpConn net.Conn
var udpConn *net.UDPConn
var localAddress,targetAddress stringvar FunWin func(interface{})
var FunUpList func(name,addr,state string)
var FunSetInfo func(name,info string)
var FunSetSingleInfo func(usrname,info string)func InitAddress(ladr,tadr string){localAddress=ladrtargetAddress=tadr
}func ConnToTCP()(net.Conn,error){//创建请求服务器tcpaddrserver, _ := net.ResolveTCPAddr(TcpType, targetAddress);//本机客户端地址tcpaddrlocal, _ := net.ResolveTCPAddr(TcpType, localAddress);//连接请求的服务器dialTcp, err := net.DialTCP(TcpType, tcpaddrlocal, tcpaddrserver);if err==nil{tcpConn=dialTcp}return dialTcp, err
}//读取数据
func ReadFromTCP(conn net.Conn){var condion bool=truefor condion{data,ln:=ReadBuffer(conn)        if ln > 0 {logValue:=string(data[0:1]) value:=data[1:ln]switch logValue{case Msgtype_state:valueStr:=string(value)if valueStr!="successful"{condion=false}case Msgtype_users:users,err:=ReadFromByte(value)if err==nil{FunWin(users)}case Msgtype_line:lineValue:=string(value) lineSplit:=strings.Split(lineValue, ",")if len(lineSplit)==3{FunUpList(lineSplit[0],lineSplit[1],lineSplit[2])}                      default:condion=false                                  }}          }conn.Close()
}func ReadTCPAsync(conn net.Conn){go ReadFromTCP(conn)
}//发送数据
func WriteToTCP(conn net.Conn,value []byte){conn.Write(value)
}//启动UDP监听
func ListenUDP(){//创建请求服务器udpaddrserver, _ := net.ResolveUDPAddr(UdpType, localAddress);conn, err := net.ListenUDP(UdpType, udpaddrserver)if err==nil{udpConn=conn     go ReadFromUDP(udpConn)}
}//读取UDP数据
func ReadFromUDP(conn *net.UDPConn){var condion bool=truefor condion{data,ln:=ReadBufferUDP(conn)if ln > 0 {logValue:=string(data[0:1]) value:=data[1:ln]switch logValue{case Msgtype_info:valueStr:=string(value)valueArr:=strings.Split(valueStr, ",")if len(valueArr)==2{FunSetInfo(valueArr[0],valueArr[1])}case Msgtype_info_1:valueStr:=string(value)valueArr:=strings.Split(valueStr, ",")if len(valueArr)==2{go FunSetSingleInfo(valueArr[0],valueArr[1])}                               default:fmt.Println("value error:"+string(value))                                 }       }else{condion=false}}conn.Close()
}//发送UDP数据
func WriteToUDP(info,addr string){udpaddr, _ := net.ResolveUDPAddr(UdpType, addr);udpConn.WriteTo([]byte(info),udpaddr)
}//读取TCP缓存数据
func ReadBuffer(conn net.Conn)([]byte,int){bufsize:=256buf:=make([]byte,bufsize)var bufs []byteln:=0for{n,err:=conn.Read(buf)if n>0{ln+=n}bufs=append(bufs,buf...)if n<bufsize{break}if err!=nil{break}}return bufs,ln
}//读取UDP缓存数据
func ReadBufferUDP(conn *net.UDPConn)([]byte,int){      bufsize:=256buf:=make([]byte,bufsize)var bufs []byteln:=0for{n,err,_:=conn.ReadFromUDP(buf)if n>0{ln+=n}bufs=append(bufs,buf...)if n<bufsize{break}if err!=nil{break}}return buf,ln
}func setUserState(name,linestate string){}//JSON序列化
func WriteToByte(users []usr.User)([]byte, error){return json.Marshal(users)}//反序列化
func ReadFromByte(b []byte)([]usr.User,error){var users []usr.Usererr:=json.Unmarshal(b,  &users)return users,err
}

3、实现效果

完整代码下载地址:https://download.csdn.net/download/luoye4321/10722042

git:https://github.com/zhangfeng1210/goSocket

go语言实现仿QQ聊天功能相关推荐

  1. 仿QQ 聊天 @输入功能

    QQAtInput 仿QQ at 功能 最近在做即时通讯功能,在开发之余,封装了一个仿QQ at 功能组件.可以用在聊天,评论等模块中.该组件主要解决两个难点问题: @选取的成员作为一个整体,不可编辑 ...

  2. 仿QQ聊天软件(JavaFX+云端数据库)

    仿QQ聊天软件(JavaFX+云端数据库) 这个项目是这学期(大二上学期学完Java后的期末项目),寒假闲着无聊就整理下发上来供大家学习以及参考啦(因为国内关于JavaFX的各种资料感觉都太浅了,本来 ...

  3. 仿QQ聊天程序(java)

    仿QQ聊天程序(java) 2014年03月17日 15:52:47 标签: java / 聊天 / 仿QQ 90246 仿QQ聊天程序 转载出处: https://blog.csdn.net/u01 ...

  4. 转载:仿QQ聊天软件2.0版

    仿QQ聊天软件2.0版 这是大神的地址:牟尼的专栏 http://blog.csdn.net/u012027907 详细的过程本人没看,但是看见他的实现效果,相当诱人!     上次课设做了Java版 ...

  5. java仿qq群聊_[转载]仿QQ聊天室群聊的练习心得

    javase的学习即将告一段落,作为最后的一个项目练习,仿聊天室的程序编写让我很是头疼了一阵子.说起来还是自己java基础不牢的缘故导致的,虽然整体框架都已经很清晰了但是实际编写过程中却依然磕磕绊绊, ...

  6. 视频教程-网络聊天室Java基础版(Socket_Swing编程)仿QQ聊天-Java

    网络聊天室Java基础版(Socket_Swing编程)仿QQ聊天 IT行业资深从业者,7年资深Java高级开发,Java架构师.曾就职银行.电信等行业多家上市公司.担任项目负责人,软件架构师.有丰富 ...

  7. 网络编程-基于MFC的仿QQ聊天室-2020

    基于MFC的仿QQ聊天室(2020) 有幸学习过网络编程的一些知识,出于对编程的热爱,把曾经的一次简单实践编程作业进行了自定义的完成. 编程所需: 编程工具为VS 2010,需要掌握MFC的基本操作以 ...

  8. 仿QQ聊天软件(登录界面、好友界面、聊天界面)-Java(Swing、Socket)

    文章目录 一.项目结构 二.项目功能 三.制作界面 (一).登录界面的制作 (二).好友列表界面 (三).聊天界面 四.制作服务器 五.设计通信协议 六.项目缺点 学习了socket通信后,就想来制作 ...

  9. java qq通信,Java通信-仿QQ聊天项目

    前后历时一个多月的Java实现聊天通信项目-仿QQ聊天室基本告一段落,期间面对了很多问题,也有不同的解决方案,重写了几次核心代码,等等问题.现在在项目的结束之时,给自己做一个总结,算是一个回顾,算是一 ...

最新文章

  1. 如何写新的Python OP
  2. Struts权威著作
  3. 剑桥少儿英语预备级教案(上) unit8 How many doors?
  4. oracle简写sql,Oracle通过sqlplus编辑sql命令
  5. 谈谈你对IOC的理解
  6. angularjs1.X进阶笔记(3)——如何重构controller
  7. mysql 集群怎么卸载节点_Redis集群重新分片(新增/移除节点)【理论】
  8. python账户密码_python02 用户名密码
  9. java对【配置文件的读取】与【读配置文件时的路径问题】代码总结
  10. 2招PDF去密码,秒学秒懂秒用上!
  11. viper12a电源电路图_viper12a电源维修技巧
  12. 2008r2配置 iis mysql php_Windows Server 2008 R2 IIS7+PHP5(FastCGI)+MySQL5环境搭建
  13. Html炫酷代码例程
  14. C语言指针的正确打开方式!
  15. GPT生成情人节表白情话,AI撩骚情人卡很搞笑!
  16. 程序员如何快速了解和掌握一门新技术
  17. 2017免费发布信息平台排行大全!
  18. 领域泛化文献综述阅读笔记
  19. QS2016年全球高等教育系统实力排名 中国排名世界第八亚洲第一
  20. [已迁移]pwn-House of Orange+FSOP

热门文章

  1. 如何将数据表格快速转换成LaTeX格式?
  2. 《和码字形技术》6.0版说明
  3. BTS7008-2EPA 翻译
  4. 【甄选靶场】Vulnhub百个项目渗透——项目三十八:Tommy-Boy-1(修改UA,脏牛提权)
  5. 关于机器人流量对抗的一点感想
  6. 加密市场月爆仓200亿背后:谁离开了?
  7. 中国摇滚:历史上的今天,中国摇滚十年生死两茫茫
  8. 史帝奇文旅产品篇——4D动感影院、主题特效影院
  9. c语言ansi c89_ANSI C语法
  10. winy 计算机没桌面,批处理文件如何运行程序并设置窗口的位置和大小?