2 Star 0 Fork 0

slh92 / plugin-sip

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
sip_client.go 6.74 KB
一键复制 编辑 原始数据 按行查看 历史
slh92 提交于 2024-05-14 15:28 . update:注销流程优化
package client
import (
"errors"
"fmt"
"gitee.com/slh1992/plugin-sip/sip"
"gitee.com/slh1992/plugin-sip/transaction"
"gitee.com/slh1992/plugin-sip/tu"
"net"
"net/http"
"strings"
"time"
)
const FFMPEG_TO_FLV = "FFmpeg to FLV"
const SIP_STATUS_ON = "ON"
const SIP_STATUS_OFF = "OFF"
const REGIST_STATUS_OK = "1"
const REGIST_STATUS_FATIL = "0"
type SipClient struct {
*transaction.Core `json:"-"`
ID string
sn int
LocalIp string
from *sip.Contact
to *sip.Contact
RegisterTime time.Time
UpdateTime time.Time
LastKeepaliveAt time.Time
TryCnt uint
Done chan struct{}
MinPort uint16
MaxPort uint16
Status string
handle SipHandleFunc
}
type SipHandleFunc interface {
HandleRegister(req *sip.Request, tx *transaction.GBTx)
HandleInvite(req *sip.Request, tx *transaction.GBTx)
HandleMessage(req *sip.Request, tx *transaction.GBTx)
HandleAck(req *sip.Request, tx *transaction.GBTx)
HandleByte(req *sip.Request, tx *transaction.GBTx)
HandleSubsribe(req *sip.Request, tx *transaction.GBTx)
KeepLiveCall(deviceId, status string)
RegisterCall(deviceId, status string)
}
func NewClient(config *transaction.Config) (*SipClient, error) {
if config.SipIP == "" {
return nil, errors.New("Sip服务器地址不能为空!")
}
if config.SipPort == 0 {
return nil, errors.New("Sip服务器端口号未设置!")
}
if config.LocPort == 0 {
config.LocPort = 15060
}
if config.Username == "" {
return nil, errors.New("Sip服务器用户名不能为空!")
}
if config.Serial == "" {
return nil, errors.New("Sip服务器序列不能为空!")
}
if config.Realm == "" {
return nil, errors.New("Sip服务器域不能为空!")
}
client := &SipClient{
sn: 1,
UpdateTime: time.Now(),
TryCnt: 0,
Done: make(chan struct{}),
Status: REGIST_STATUS_FATIL,
}
c, err := transaction.NewCliCore(config)
if err != nil {
return nil, err
}
client.Core = c
client.ID = config.Username
return client, nil
}
func (c *SipClient) SetUp(handle SipHandleFunc) error {
c.RegistHandler(sip.REGISTER, handle.HandleRegister)
c.RegistHandler(sip.INVITE, handle.HandleInvite)
c.RegistHandler(sip.MESSAGE, handle.HandleMessage)
c.RegistHandler(sip.ACK, handle.HandleAck)
c.RegistHandler(sip.BYE, handle.HandleByte)
c.RegistHandler(sip.SUBSCRIBE, handle.HandleSubsribe)
c.handle = handle
err := c.StartAndWait()
if err != nil {
return err
}
time.Sleep(1 * time.Second)
if c.LocalIp == "" {
c.LocalIp = c.GetLocalIp()
}
go func(c *SipClient) {
defer func() {
fmt.Sprintf("%s>>>>>>心跳已停止:%s\n", time.Now().String(), c.SipIP)
}()
timer := time.NewTicker(time.Duration(c.HeartbeatInterval) * time.Second)
for {
select {
case <-timer.C:
//校验过期就重新注册
go c.checkRegisterExpires()
c.sendHeartMsg()
case <-c.Done:
return
}
}
}(c)
return c.toRegister()
}
func (c *SipClient) sendHeartMsg() {
keeplive := sip.BuildKeepLvieXML(c.sn, c.Username)
msg := c.CreateMessage(sip.MESSAGE, keeplive)
resp, err := c.SipRequestForResponse(&sip.Request{Message: msg})
if resp == nil || err != nil {
fmt.Printf("%s心跳发送失败[%d]:%s\n", c.SipIP, c.TryCnt, err.Error())
opErr, ok := err.(*net.OpError)
if ok && strings.Index(opErr.Error(), "closed network connection") != -1 {
c.resetWait()
return
}
c.TryCnt++
if c.TryCnt > uint(c.HeartbeatRetry) {
fmt.Printf("%s心跳发送超出限制[%d]次,停止心跳机制\n", c.SipIP, c.HeartbeatRetry)
c.handle.KeepLiveCall(c.Username, SIP_STATUS_OFF)
c.UnRegister()
return
}
if c.TryCnt == uint(c.HeartbeatRetry) {
err = c.toRegister()
if err != nil {
fmt.Printf(">>>>>>>>>>>>>>%s\n", err.Error())
}
}
return
}
if resp.StartLine.Code == http.StatusOK {
//当心跳成功且前一次失败,说明sip服务器存在失联情况,必须重新注册
if c.TryCnt > 0 && c.TryCnt < uint(c.HeartbeatRetry) {
err = c.toRegister()
if err != nil {
fmt.Printf(">>>>>>>>>>>>>>>%s\n", err.Error())
}
}
fmt.Printf("======%s心跳发送成功,失败[%d]次=====\n", c.SipIP, c.TryCnt)
c.TryCnt = 0
c.LastKeepaliveAt = time.Now()
c.UpdateTime = time.Now()
c.handle.KeepLiveCall(c.Username, SIP_STATUS_ON)
return
}
}
func (c *SipClient) checkRegisterExpires() {
expireTimes := c.RegisterTime.Add(time.Duration(c.RegisterValidity) * time.Second)
if time.Now().After(expireTimes) {
err := c.toRegister()
if err != nil {
c.handle.RegisterCall(c.Username, REGIST_STATUS_FATIL)
return
}
c.handle.RegisterCall(c.Username, REGIST_STATUS_OK)
}
}
func (c *SipClient) toRegister() error {
msg := c.CreateMessage(sip.REGISTER, "")
resp, err := c.SipRequestForResponse(&sip.Request{Message: msg})
if resp == nil {
c.destory()
return errors.New("服务注册失败:" + err.Error())
}
if resp.StartLine.Code == http.StatusUnauthorized {
msg := c.CreateMessage(sip.REGISTER, "")
msg.Authorization = sip.ToAuthorization(resp.To.Uri.UserInfo(), msg.From.Uri.String(), c.Password, resp.WwwAuthenticate)
resp, err = c.SipRequestForResponse(&sip.Request{Message: msg})
if resp == nil {
c.destory()
return errors.New("服务注册失败:" + err.Error())
}
if resp.GetStatusCode() != http.StatusOK {
c.destory()
return errors.New("服务注册不成功")
}
}
if resp.GetStatusCode() == http.StatusOK {
c.RegisterTime = time.Now()
c.UpdateTime = time.Now()
c.Status = REGIST_STATUS_OK
c.TryCnt = 0
c.sendHeartMsg()
}
return nil
}
func (c *SipClient) UnRegister() {
msg := c.CreateMessage(sip.REGISTER, "")
msg.Expires = 0
resp, err := c.SipRequestForResponse(&sip.Request{msg})
if err != nil || resp == nil {
c.destory()
return
}
if resp.StartLine.Code == http.StatusUnauthorized {
msg := c.CreateMessage(sip.REGISTER, "")
msg.Authorization = sip.ToAuthorization(resp.To.Uri.UserInfo(), msg.From.Uri.String(), c.Password, resp.WwwAuthenticate)
msg.Expires = 0
resp, err = c.SipRequestForResponse(&sip.Request{Message: msg})
if resp == nil {
c.destory()
return
}
if resp.GetStatusCode() != http.StatusOK {
c.destory()
return
}
}
if resp.GetStatusCode() == http.StatusOK {
c.destory()
}
}
func (c *SipClient) destory() {
c.Status = REGIST_STATUS_FATIL
close(c.Done)
c.Close()
}
func (c *SipClient) resetWait() error {
c.Close()
err := c.StartAndWait()
if err != nil {
fmt.Printf(">>>>>>>>>>>>>重新发起连接:%s\n", c.SipIP)
return err
}
c.toRegister()
return nil
}
func (c *SipClient) CreateMessage(mehod sip.Method, body string) *sip.Message {
c.sn++
return tu.BuildClientMessageRequest(mehod, c.SipNetwork, c.Serial, c.Realm, c.Username, c.LocalIp, c.LocPort, c.RegisterValidity, c.sn, body)
}
Go
1
https://gitee.com/slh1992/plugin-sip.git
git@gitee.com:slh1992/plugin-sip.git
slh1992
plugin-sip
plugin-sip
v1.5.0

搜索帮助