go语言实现仿QQ聊天功能

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;i0{
			ln+=n
		}
		
		bufs=append(bufs,buf...)
		
		if n

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 n0{
			ln+=n
		}
		
		bufs=append(bufs,buf...)
		
		if n

3、实现效果

go语言实现仿QQ聊天功能_第1张图片

go语言实现仿QQ聊天功能_第2张图片

go语言实现仿QQ聊天功能_第3张图片

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

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

你可能感兴趣的:(Go语言)