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、实现效果
完整代码下载地址:https://download.csdn.net/download/luoye4321/10722042
git:https://github.com/zhangfeng1210/goSocket