服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C# WebApi+Webrtc局域网音视频通话实例

C# WebApi+Webrtc局域网音视频通话实例

2022-11-27 14:58qq53716684 C#

这篇文章主要为大家详细介绍了C# WebApi+Webrtc局域网音视频通话实例,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

C# WebApi+Webrtc 局域网音视频通话示例,供大家参考,具体内容如下

本示例通过IIS部署webapi,利用websocket进行webrtc消息交换,通过Chrome浏览器访问,可实现局域网内webrtc 音视频通话。

通过Chrome浏览器打开localhost/live.html本地网址,打开两个本地网,点击任意页面连接按钮即联通。

本示例未实现NAT穿透处理,互联网无法联通,如需NAT穿透请自行查阅相关资料。

关于webrtc、webapi相关技术说明请自行查阅相关资料,本文不做赘述说明。

运行效果如下图:

C# WebApi+Webrtc局域网音视频通话实例

webapi端Handler1.ashx代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.WebSockets;
 
namespace webrtclan
{
    /// <summary>
    /// 离线消息
    /// </summary>
    public class MessageInfo
    {
        public MessageInfo(DateTime _MsgTime, ArraySegment<byte> _MsgContent)
        {
            MsgTime = _MsgTime;
            MsgContent = _MsgContent;
        }
        public DateTime MsgTime { get; set; }
        public ArraySegment<byte> MsgContent { get; set; }
        
    }
 
 
    /// <summary>
    /// Handler1 的摘要说明
    /// </summary>
    public class Handler1 : IHttpHandler
    {
        private static Dictionary<string, WebSocket> CONNECT_POOL = new Dictionary<string, WebSocket>();//用户连接池
        private static Dictionary<string, List<MessageInfo>> MESSAGE_POOL = new Dictionary<string, List<MessageInfo>>();//离线消息池
       
 
        public void ProcessRequest(HttpContext context)
        {
            
            if (context.IsWebSocketRequest)
            {
                context.Response.ContentType = "application/json";
                context.Response.Charset = "utf-8";
                context.AcceptWebSocketRequest(ProcessMsg);
            }
        }
       
 
        private async Task ProcessMsg(AspNetWebSocketContext context)
        {
            WebSocket socket = context.WebSocket;
            string user = context.QueryString["user"].ToString();
            
            try
            {
                #region 用户添加连接池
                //第一次open时,添加到连接池中
                if (!CONNECT_POOL.ContainsKey(user))
                {
                    CONNECT_POOL.Add(user, socket);//不存在,添加
                }
                else
                {
                    if (socket != CONNECT_POOL[user])//当前对象不一致,更新
                    {
                        CONNECT_POOL[user] = socket;
                    }
                }
                #endregion
 
                //#region 连线成功
                //for (int cp = 0; cp < CONNECT_POOL.Count; cp++)
                //{
                //    if (CONNECT_POOL.ElementAt(cp).Key != user)
                //    {
                //        string joinedmsg = "{\"FROM\":\"" + user + "\",\"event\":\"joined\"}";
                //        ArraySegment<byte> joinedmsgbuffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(joinedmsg));
                //        WebSocket destSocket = CONNECT_POOL.ElementAt(cp).Value;//目的客户端
                //        await destSocket.SendAsync(joinedmsgbuffer, WebSocketMessageType.Text, true, CancellationToken.None);
                //    }
                //}
                //#endregion
 
                #region 离线消息处理
                if (MESSAGE_POOL.ContainsKey(user))
                {
                    List<MessageInfo> msgs = MESSAGE_POOL[user];
                    foreach (MessageInfo item in msgs)
                    {
                        await socket.SendAsync(item.MsgContent, WebSocketMessageType.Text, true, CancellationToken.None);
                    }
                    MESSAGE_POOL.Remove(user);//移除离线消息
                }
                #endregion
                
                while (true)
                {
                    if (socket.State == WebSocketState.Open)
                    {
                        ArraySegment<byte> wholemessage= new ArraySegment<byte>(new byte[10240]);
 
                        
                        int i = 0;
 
                       
                        WebSocketReceiveResult dresult;
                        do
                        {
                            //因为websocket每一次发送的数据会被tcp分包
                            //所以必须判断接收到的消息是否完整
                            //不完整就要继续接收并拼接数据包
                            ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[2048]);
                            dresult = await socket.ReceiveAsync(buffer, CancellationToken.None);
                            string message1 = Encoding.UTF8.GetString(buffer.Array);                           
                            buffer.Array.CopyTo(wholemessage.Array,i);
                            i += 2048;
                        } while (false == dresult.EndOfMessage);                      
 
                        //string message = Encoding.UTF8.GetString(wholemessage.Array);
                        //message = message.Replace("\0", "").Trim();
                        //JavaScriptSerializer serializer = new JavaScriptSerializer();
                        //Dictionary<string, object> json = (Dictionary<string, object>)serializer.DeserializeObject(message);
                        //string target = (string)json.ElementAt(1).Value;
 
                        #region 消息处理(字符截取、消息转发)
                        try
                        {
                            #region 关闭Socket处理,删除连接池
                            if (socket.State != WebSocketState.Open)//连接关闭
                            {
                                if (CONNECT_POOL.ContainsKey(user)) CONNECT_POOL.Remove(user);//删除连接池
                                break;
                            }
                            #endregion
                            
                            for (int cp = 0; cp < CONNECT_POOL.Count; cp++)
                            {
                                 //if (CONNECT_POOL.ElementAt(cp).Key!=target)
                                 //   {
                                        WebSocket destSocket = CONNECT_POOL.ElementAt(cp).Value;//目的客户端
                                        await destSocket.SendAsync(wholemessage, WebSocketMessageType.Text, true, CancellationToken.None);
                                 //  }
                             }
                           
 
                            //if (CONNECT_POOL.ContainsKey(descUser))//判断客户端是否在线
                            //{
                            //    WebSocket destSocket = CONNECT_POOL[descUser];//目的客户端
                            //    if (destSocket != null && destSocket.State == WebSocketState.Open)
                            //        await destSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                            //}
                            //else
                            //{
                            //    _ = Task.Run(() =>
                            //      {
                            //          if (!MESSAGE_POOL.ContainsKey(descUser))//将用户添加至离线消息池中
                            //            MESSAGE_POOL.Add(descUser, new List<MessageInfo>());
                            //          MESSAGE_POOL[descUser].Add(new MessageInfo(DateTime.Now, buffer));//添加离线消息
                            //    });
                            //}
                        }
                        catch (Exception exs)
                        {
                            //消息转发异常处理,本次消息忽略 继续监听接下来的消息
                        }
                        #endregion
                    }
                    else
                    {
                        if (CONNECT_POOL.ContainsKey(user)) CONNECT_POOL.Remove(user);//删除连接池                       
                        break;
                    }
                }//while end
            }
            catch (Exception ex)
            {
                //整体异常处理
                if (CONNECT_POOL.ContainsKey(user)) CONNECT_POOL.Remove(user);
                
            }
        }
 
    
 
        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
 
 
    }
}

live.html客户端代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>webrtc</title>
    <style>
        #yours {
            width: 200px;
            position: absolute;
            top: 50px;
            left: 100px;
        }
        #theirs {
            width: 600px;
            position: absolute;
            top: 50px;
            left: 400px;
        }
    </style>
</head>
<body>
    <button onclick="createOffer()">建立连接</button>
    <video id="yours" autoplay controls="controls" ></video>
    <video id="theirs" autoplay controls="controls"></video>
 
</body>
 
<script src="webrtc.js"></script>
 
</html>

webrtc.js脚本代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
var websocket;
 
function randomNum(minNum, maxNum) {
    switch (arguments.length) {
        case 1:
            return parseInt(Math.random() * minNum + 1, 10);
            break;
        case 2:
            return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
            break;
        default:
            return 0;
            break;
    }
}
const userid = 'user' + randomNum(0, 100000);
 
function hasUserMedia() {
    navigator.getUserMedia = navigator.getUserMedia || navigator.msGetUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
    return !!navigator.getUserMedia;
}
function hasRTCPeerConnection() {
    window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.msRTCPeerConnection;
    return !!window.RTCPeerConnection;
}
 
var yourVideo = document.getElementById("yours");
var theirVideo = document.getElementById("theirs");
var Connection;
 
 
function startPeerConnection() {
    //return;
    var config = {
        'iceServers': [
            //{ 'urls': 'stun:stun.xten.com:3478' },
            //{ 'urls': 'stun:stun.voxgratia.org:3478' },
 
            //{ 'url': 'stun:stun.l.google.com:19302' }
        ]
    };
    config = {
        iceServers: [
            //{ urls: 'stun:stun.l.google.com:19302' },
            //{ urls: 'stun:global.stun.twilio.com:3478?transport=udp' }
        ]
        //sdpSemantics: 'unified-plan'
    };
    // {
    //     "iceServers": [{
    //         "url": "stun:stun.1.google.com:19302"
    //     }]
    // };
    Connection = new RTCPeerConnection(config);
    Connection.onicecandidate = function (e) {
        console.log('onicecandidate');
        if (e.candidate) {
            websocket.send(JSON.stringify({
                "userid": userid,
                "event": "_ice_candidate",
                "data": {
                    "candidate": e.candidate
                }
            }));
        }
    };
    Connection.onaddstream = function (e) {
        console.log('onaddstream');
        //theirVideo.src = window.URL.createObjectURL(e.stream);
        theirVideo.srcObject = e.stream;
    };
    Connection.onclose = function (e) {
        console.log('RTCPeerConnection close'+e);
    };
}
 
createSocket();
startPeerConnection();
 
if (hasUserMedia()) {
    navigator.getUserMedia({ video: true, audio: true },
        stream => {
            yourVideo.srcObject = stream;
            window.stream = stream;
            yourVideo.muted = true;
            Connection.addStream(stream)
        },
        err => {
            console.log(err);
        })
}
 
 
function createOffer() {
    //发送offer和answer的函数,发送本地session描述
    Connection.createOffer().then(offer => {
        Connection.setLocalDescription(offer);
        websocket.send(JSON.stringify({           
            "userid": userid,
            "event": "offer",
            "data": {
                "sdp": offer
            }
        }));
    });
}
 
 
function createSocket() {
    //websocket = null;
    websocket = new WebSocket('ws://localhost:80/Handler1.ashx?user='+userid);//('wss://www.ecoblog.online/wss');
    eventBind();
};
 
function eventBind() {
    //连接成功
    websocket.onopen = function (e) {
        console.log('open:' + e);
    };
    //server端请求关闭
    websocket.onclose = function (e) {
        console.log('close:' + e);
    };
    //error
    websocket.onerror = function (e) {
        console.log('error:' + e.data);
    };
    //收到消息
    websocket.onmessage = (event) => {
        if (event.data == "new user") {
            location.reload();
        } else {
            var js = event.data.replace(/[\u0000-\u0019]+/g, "");
            var json = JSON.parse(js);
            
            if (json.userid != userid) {
                //如果是一个ICE的候选,则将其加入到PeerConnection中,否则设定对方的session描述为传递过来的描述
                if (json.event === "_ice_candidate" && json.data.candidate) {
                    Connection.addIceCandidate(new RTCIceCandidate(json.data.candidate));
                }
                else if (json.event === 'offer') {
                    Connection.setRemoteDescription(json.data.sdp);
                    Connection.createAnswer().then(answer => {
                        Connection.setLocalDescription(answer);
                        //console.log(window.stream)
                        websocket.send(JSON.stringify({                           
                            "userid": userid,
                            "event": "answer",
                            "data": {
                                "sdp": answer
                            }
                        }));
                    })
                }
                else if (json.event === 'answer') {
                    Connection.setRemoteDescription(json.data.sdp);
                    //console.log(window.stream)
 
                }
            }
        }
    };
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/yx1234321/article/details/106850997

延伸 · 阅读

精彩推荐