SIP协议包括三个部分:起始行、一个/多个字段(field)组成的消息头、一个标志消息头结束的空行(CRLF)以及作为可选项的消息体(message body)组成。
1. 起始行
起始行分请求的起始行和响应的起始行
1.1 请求起始行
一个 Request_line 包含方法名字, Request-URI,用单个空格(SP)间隔开的协议版本。 Request-Line 由 CRLF 结束。除了用作行结束标志以外,不允许 CR 或者 LF 出现在其他地方。
格式:
Request-Line = Method SP Request-URI SP SIP-VERSION CRLF
举例:REGISTER sip: telcomx.tel.com SIP2.0
具体含义:
名称 | 含义 |
---|---|
Method | 规定了 6 中方法:REGISTER 用于登记联系信息,INVITE, ACK,CANCEL 用于建立会话,BYE 用于结束会话,OPTIONS 用于查询服务器负载。 SIP 扩展、标准 RFC 追加可能包含扩展的方法。 |
SIP | 元素可以支持除了 SIP 或者 SIPS 之外所规定的 Request-URIs。比如”tel” URI 模式(RFC 2806[9])。SIP 元素可以用他们自己的机制来转换 non-SIP URIs 到 SIP URI, SIPS URI 或者其他什么格式的 URI |
Request-URI | 是一个 SIP 或者 SIPS URI,也可以是一个通用的 URI(RFC 2396[5])。它标志了这个请求所用到的用户或者服务的地址。 Request-URI 禁止包含空白字符或者控制字符,并且禁止用”<>”括上 |
SIP-Version | 请求和应答消息都包含当前使用的 SIP 版本,这个遵循[H3.1](类似 HTTP用 SIP 替代,用 SIP/2.0 替代 HTTP/1.1)中关于版本的规定,版本依赖,升级版本号。一个应用,发出的 SIP 消息一定包含了 SIP-Version “SIP/2.0”。这个 SIP 版本串是大小写不铭感的,但是在实现中必须发送大写。 |
// SIP请求消息的起始行(RFC3261-7.1)
type RequestLine struct {
Method string // 请求方法
RequestURI URI // SIP消息目标地址,无"<>"尖括号
SIPVersion string // SIP版本号
}
1.2 响应起始行
SIP 应答和 SIP 请求的区别在于在 START-LINE 中是否包含一个 STATUS-LINE。一个status-line 在由数字表达的 status-code 之前,是一个协议的版本串,每一个元素之间用一个单个 SP(空格)分开。 除了最后用作结束标志以外,CR/LF 不允许出现在其他地方。
格式:
status-line = SIP-VERSION SP STATUS-CODE SP Reasong-Phrase CRLF
举例:SIP/2.0 200 OK
具体含义:
名称 | 含义 |
---|---|
SIP-Version | 请求和应答消息都包含当前使用的 SIP 版本,这个遵循[H3.1](类似 HTTP用 SIP 替代,用 SIP/2.0 替代 HTTP/1.1)中关于版本的规定,版本依赖,升级版本号。一个应用,发出的 SIP 消息一定包含了 SIP-Version “SIP/2.0”。这个 SIP 版本串是大小写不铭感的,但是在实现中必须发送大写。 |
Status-Code | 一个 3 位的数字 result code,用来标志处理请求的一个结果。 |
Reason-Phrase | 是一个简短的 Status-Code 的说明。Status-Code 是为了能自动处理使用的,但是 Reason-Phrase 是用来给用户看得。一个客户端并不要求一定要显示或者解释这个 Reason-Phrase。 |
其中,status-code 的第一个数字表示了应答的类型。接下来两个数字并不作分类使用。基于这个原因,任何 status code 在 100 到 199 的可以统称位”1xx 应答”,类似的,在 200到 299 的可以统称位”2xx 应答”,依此类推。SIP/2.0 允许 6 类应答:
1xx:临时应答-请求已经接收,正在处理这个请求。
2xx:成功处理-请求已经成功接收,并且正确处理了这个请求。
3xx:重定向-还需要附加的操作才能完成这个请求,本请求转发到其他的服务器上处理。
4xx:客户端错误--请求包含错误的格式或者不能在这个服务器上完成。
5xx:服务器错误-服务器不能正确的处理这个显然合法的请求。
6xx:全局错误-请求不能被任何服务器处理。
// SIP应答消息的起始行(RFC3261-7.2)
type ResponseLine struct {
SIPVersion string // SIP版本号
StatusCode int // 状态码
ReasonPhrase string // 状态码的说明
}
```cpp
package sip
const (
// SIP版本号
SIPVersion = "SIP/2.0"
// 协议类型
SchemeSip = "sip"
// SIP请求方法-RFC3261
MethodInvite = "INVITE"
MethodAck = "ACK"
MethodBye = "BYE"
MethodCancel = "CANCEL"
MethodOptions = "OPTIONS"
MethodRegister = "REGISTER"
// SIP请求方法-其他协议
MethodPrack = "PRACK" // [RFC3262]
MethodSubscribe = "SUBSCRIBE" // [RFC6665]
MethodNotify = "NOTIFY" // [RFC6665]
MethodPublish = "PUBLISH" // [RFC3903]
MethodInfo = "INFO" // [RFC6086]
MethodRefer = "REFER" // [RFC3515]
MethodMessage = "MESSAGE" // [RFC3428]
MethodUpdate = "UPDATE" // [RFC3311]
MethodPing = "PING" // [https://tools.ietf.org/html/draft-fwmiller-ping-03]
)
**2. SIP 头域**
SIP 的包头语法都是基于如下范式的:
*header = “ header-name” HCOLON header-value *(COMMA header-value)* *
这个允许合并在具有同一个域名的多个头域,到一个用逗号分割的单个头域中。Contact头域除了当域值是”*”之外,都允许用逗号分割的列表。
每一个头域都由一个域名加上冒号(”:”)和域值组成。
*field-name:field-value*
在消息头中,允许在冒号的左右有任意个数的空白;但是,在实现中,建议避免域名和冒号中间有空格,并且建议在冒号和值之间使用单个空格(SP)。
每一个头域值的格式是依赖于它的头域名的。他可以是任意顺序的 TEXT-UTF8 字符,也可以是一个空格,标记,分隔符,引号括起来的字串的组合。很多头域都回附带一个通用的域值格式。这个域值格式是由分号分开的参数名和参数值的组合:
*field-name: field-value *(;parameter-name=parameter-value)*
虽然在域值里边可以有任意数量的 parameter-name/parameter-value 对,但是不能允许有相同的 parameter-name 存在(唯一性)。除了特别指出的头域之外,头域中的域名、域值、parameter name parameter-value 都是大小写不敏感的。标记词始终是大小写不铭感的。除非有特别的指定,引号串的字符串是大小写敏感的。例如:
***Contact: ;expires=3600***
```cpp
// SIP头域(RFC3261-7.3)
// 行范式:header = "header-name" HCOLON header-value *(COMMA header-value)
// 头域至少包含TO、FROM、CSeq、Call-ID、Max-Forwards、Via字段
type Header struct {
Via ViaList // (RFC3261-8.1.1.7) 请求路径
From User // (RFC3261-8.1.1.3) 请求的原始发起者
To User // (RFC3261-8.1.1.2) 请求的原始到达者
CallID string // (RFC3261-8.1.1.4) 唯一标志
CSeq CSeq // (RFC3261-8.1.1.5) 命令序列号
MaxForwards MaxForwards // (RFC3261-8.1.1.6) 最大转发数量限制
ContentLength int // 正文长度
ContentType string // (可选) 正文格式描述
Contact *User // (可选) (RFC3261-8.1.1.8) 直接访问方式
Expires Expires // (可选) 消息或内容过期时间
Route Route // (可选) 请求的路由表
RecordRoute RecordRoute // (可选) 后续消息流处理的服务器列表
UserAgent string // (可选) UAC的信息
Authorization string // (可选) 用户认证信息
WWWAuthenticate string // (可选) 支持的认证方式和适用realm的参数的拒绝原因
UnsupportLines []string // 暂不支持的行
}
3. SIP包体
请求信息,包括这个规范以后的扩展的新请求,都可以包含一个消息正文体。对消息正文体的解释依赖域请求的方法(请求类型)。对于应答消息来说,请求方法和应答状态(response status code)决定了消息正文体的格式。所有的应答消息都可以有一个消息正文体(body)。
消息中的 internet 媒体类别必须在 Content-Type 头域中指明。如果消息正文(body)通过某种形式的编码(encoding),比如压缩等等,都必须在 Content-Encoding 头域中指明,否则 Content-Encoding 域必须忽略。如果可行,消息体的字符集作为 Content-type头域的值的一部分表达。
在 RFC2046[11]中定义的多部分”multipart” MIME 类型可以在消息体中应用。在由多部分组成的消息体发送的时候,如果接受方的实现中,包头域的 Accept 域中,不包含多部分的标记,那么发送方必须发送一个非多部分的 session description。1
SIP 消息可以包含二进制的包体或者部分包体。如果发送方没有其他显示的字符集参数指出,媒体的文本”text”子类型会是缺省的字符集”UTF-8”。
// SIP返回状态码信息
type StatusCodeItem struct {
Code int
Reason string
}
// SIP返回状态码
var (
// 1xx - 临时应答
StatusTrying = StatusCodeItem{100, "Trying"}
StatusRinging = StatusCodeItem{180, "Ringing"}
StatusCallIsBeingForwarded = StatusCodeItem{181, "Call Is Being Forwarded"}
StatusQueued = StatusCodeItem{182, "Queued"}
StatusSessionProgress = StatusCodeItem{183, "Session Progress"}
// 2xx - 成功处理
StatusOK = StatusCodeItem{200, "OK"}
// 3xx - 重定向
StatusMultipleChoices = StatusCodeItem{300, "Multiple Choices"}
StatusMovedPermanently = StatusCodeItem{301, "Moved Permanently"}
StatusMovedTemporarily = StatusCodeItem{302, "Moved Temporarily"}
StatusUseProxy = StatusCodeItem{305, "Use Proxy"}
StatusAlternativeService = StatusCodeItem{380, "Alternative Service"}
// 4xx - 客户端错误
StatusBadRequest = StatusCodeItem{400, "Bad Request"}
StatusUnauthorized = StatusCodeItem{401, "Unauthorized"}
StatusPaymentRequired = StatusCodeItem{402, "Payment Required"}
StatusForbidden = StatusCodeItem{403, "Forbidden"}
StatusNotFound = StatusCodeItem{404, "Not Found"}
StatusMethodNotAllowed = StatusCodeItem{405, "Method Not Allowed"}
StatusNotAcceptable = StatusCodeItem{406, "Not Acceptable"}
StatusProxyAuthenticationRequired = StatusCodeItem{407, "Proxy Authentication Required"}
StatusRequestTimeout = StatusCodeItem{408, "Request Timeout"}
StatusGone = StatusCodeItem{410, "Gone"}
StatusRequestEntityTooLarge = StatusCodeItem{413, "Request Entity Too Large"}
StatusRequestURITooLong = StatusCodeItem{414, "Request-URI Too Long"}
StatusUnsupportedMediaType = StatusCodeItem{415, "Unsupported Media Type"}
StatusUnsupportedURIScheme = StatusCodeItem{416, "Unsupported URI Scheme"}
StatusBadExtension = StatusCodeItem{420, "Bad Extension"}
StatusExtensionRequired = StatusCodeItem{421, "Extension Required"}
StatusIntervalTooBrief = StatusCodeItem{423, "Interval Too Brief"}
StatusNoResponse = StatusCodeItem{480, "No Response"}
StatusCallTransactionDoesNotExist = StatusCodeItem{481, "Call/Transaction Does Not Exist"}
StatusLoopDetected = StatusCodeItem{482, "Loop Detected"}
StatusTooManyHops = StatusCodeItem{483, "Too Many Hops"}
StatusAddressIncomplete = StatusCodeItem{484, "Address Incomplete"}
StatusAmbigious = StatusCodeItem{485, "Ambiguous"}
StatusBusyHere = StatusCodeItem{486, "Busy Here"}
StatusRequestTerminated = StatusCodeItem{487, "Request Terminated"}
StatusNotAcceptableHere = StatusCodeItem{488, "Not Acceptable Here"}
StatusRequestPending = StatusCodeItem{491, "Request Pending"}
StatusUndecipherable = StatusCodeItem{493, "Undecipherable"}
// 5xx - 服务器错误
StatusServerInternalError = StatusCodeItem{500, "Server Internal Error"}
StatusNotImplemented = StatusCodeItem{501, "Not Implemented"}
StatusBadGateway = StatusCodeItem{502, "Bad Gateway"}
StatusServiceUnavailable = StatusCodeItem{503, "Service Unavailable"}
StatusServerTimeout = StatusCodeItem{504, "Server Timeout"}
StatusVersionNotSupported = StatusCodeItem{505, "Version Not Supported"}
StatusMessageTooLarge = StatusCodeItem{513, "Message Too Large"}
// 6xx - 全局错误
StatusBusyEverywhere = StatusCodeItem{600, "Busy Everywhere"}
StatusDecline = StatusCodeItem{603, "Decline"}
StatusDoesNotExistAnywhere = StatusCodeItem{604, "Does Not Exist Anywhere"}
StatusUnacceptable = StatusCodeItem{606, "Not Acceptable"}
)