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.Conn
func main() {
fmt.Println("初始化用户信息")
connMaps=map[string]net.Conn{}
//获取用户列表存储文件
filepath:=getXmlFile()
//读取所有用户列表,用户验证登录和推送用户朋友信息
users,err:=getXmlContent(filepath)
if err!=nil{
return
}
userArrs=users
userArrs.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]=conn
fmt.Println(conn.RemoteAddr().String()+" 连接成功")
//异步方式读取客户端信息
go ReadFromTCP(conn);
}
}
}
}
}
//读取客户端信息
func ReadFromTCP(conn net.Conn){
var condion bool=true
for 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("验证成功......")
//验证成功,返回successful
succValue:="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=false
case Msgtype_users:
condion=false
default:
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:=256
buf:=make([]byte,bufsize)
var bufs []byte
ln:=0
for{
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.User
err:=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=inerr
fmt.Println("open xml file err",inerr)
return
}
var result usr.Users
inerr = xml.Unmarshal(content, &result)
if inerr!=nil{
err=inerr
fmt.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.RWMutex
connMaps 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 string
var 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=ladr
targetAddress=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=true
for 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=true
for 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:=256
buf:=make([]byte,bufsize)
var bufs []byte
ln:=0
for{
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:=256
buf:=make([]byte,bufsize)
var bufs []byte
ln:=0
for{
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.User
err:=json.Unmarshal(b, &users)
return users,err
}
3、实现效果
完整代码下载地址:https://download.csdn.net/download/luoye4321/10722042