仿微信 即时聊天工具 - SignalR (一)

话不多说,先上图

 

 

 

 

 

 

背景:

微信聊天,经常会遇见视频发不了,嗯,还有聊天不方便的问题,于是我就自己买了服务器,部署了一套可以直接在微信打开的网页进行聊天,这样只需要发送个url给朋友,就能聊天了!

由于自己无聊弄着玩的,代码比较粗糙,各位多指正!

1、首先安装SignalR,这步我就不做过多说明了

安装好以后在根目录新建一个Hubs文件夹,做用户的注册和通知

MessageHub.cs 文件

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

using Microsoft.AspNet.SignalR;

using Microsoft.AspNet.SignalR.Hubs;

using System;

using System.Collections;

using System.Collections.Generic;

using System.Linq;

using System.Threading;

using System.Threading.Tasks;

using System.Web;

 

namespace SignalR.Hubs

{

    [HubName("MessageHub")]

    public class MessageHub : Hub

        {

            private readonly ChatTicker ticker;

            public MessageHub()

            {

                ticker = ChatTicker.Instance;

            }

 

            public void register(string username, string group "default")

            {

                var list = (List<SiginalRModel>)HttpRuntime.Cache.Get("msg_hs");

                if (list == null)

                {

                    list = new List<SiginalRModel>();

                }

                

 

                if (list.Any(x => x.connectionId == Context.ConnectionId))

                {

                    Clients.Client(Context.ConnectionId).broadcastMessage("已经注册,无需再次注册");

                }

            else if (list.Any(x => x.name == username))

            {

                var model = list.Where(x => x.name == username && x.group == group).FirstOrDefault();

                if (model != null)

                {

                    //注册到全局 

                    ticker.GlobalContext.Groups.Add(Context.ConnectionId, group);

                    Clients.Client(model.connectionId).exit();

                    ticker.GlobalContext.Groups.Remove(model.connectionId, group);

                    list.Remove(model);

                    model.connectionId = Context.ConnectionId;

                    list.Add(model);

                    Clients.Group(group).removeUserList(model.connectionId);

                    Thread.Sleep(200);

                    var gourpList = list.Where(x => x.group == group).ToList();

                    Clients.Group(group).appendUserList(Context.ConnectionId, gourpList);

                    HttpRuntime.Cache.Insert("msg_hs", list);

                    // Clients.Client(model.connectionId).broadcastMessage("名称重复,只能注册一个");

                }

                //Clients.Client(Context.ConnectionId).broadcastMessage("名称重复,只能注册一个");

            }

            else

                {

                    list.Add(new SiginalRModel() { name = username, group group, connectionId = Context.ConnectionId });

 

                    //注册到全局 

                    ticker.GlobalContext.Groups.Add(Context.ConnectionId, group);

                    Thread.Sleep(200);

 

                    var gourpList = list.Where(x => x.group == group).ToList();

                    Clients.Group(group).appendUserList(Context.ConnectionId, gourpList);

                    HttpRuntime.Cache.Insert("msg_hs", list);

                }

 

            }

 

            public void Say(string msg)

            {

                var list = (List<SiginalRModel>)HttpRuntime.Cache.Get("msg_hs");

                if (list == null)

                {

                    list = new List<SiginalRModel>();

                }

                var userModel = list.Where(x => x.connectionId == Context.ConnectionId).FirstOrDefault();

                if (userModel != null )

                {

                    Clients.Group(userModel.group).Say(userModel.name, msg);

                }

            }

 

        public void Exit()

        {

            OnDisconnected(true);

        }

 

        public override Task OnDisconnected(bool s)

                {

                    var list = (List<SiginalRModel>)HttpRuntime.Cache.Get("msg_hs");

                    if (list == null)

                    {

                        list = new List<SiginalRModel>();

                    }

                    var closeModel = list.Where(x => x.connectionId == Context.ConnectionId).FirstOrDefault();

 

                    if (closeModel != null)

                    {

                        list.Remove(closeModel);

 

                        Clients.Group(closeModel.group).removeUserList(Context.ConnectionId);

 

                     }

                    HttpRuntime.Cache.Insert("msg_hs", list);

                 

                    return base.OnDisconnected(s);

                }

            }

         

 

    public class ChatTicker

        {

            #region 实现一个单例

 

            private static readonly ChatTicker _instance =

                new ChatTicker(GlobalHost.ConnectionManager.GetHubContext<MessageHub>());

 

            private readonly IHubContext m_context;

 

            private ChatTicker(IHubContext context)

            {

 

                m_context = context;

                //这里不能直接调用Sender,因为Sender是一个不退出的“死循环”,否则这个构造函数将不会退出。 

                //其他的流程也将不会再执行下去了。所以要采用异步的方式。 

                //Task.Run(() => Sender());

            }

 

            public IHubContext GlobalContext

            {

                get return m_context; }

            }

 

            public static ChatTicker Instance

            {

                get return _instance; }

            }

 

            #endregion

        }

 

    public class SiginalRModel {

        public string connectionId { getset; }

 

        public string group getset; }

        public string name { getset; }

    }

}<br data-filtered="filtered"><br data-filtered="filtered">我把类和方法都写到一块了,大家最好是分开!

 

接下来是控制器

HomeController.cs

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

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

using Microsoft.AspNet.SignalR;

using Microsoft.AspNet.SignalR.Client;

using SignalR.Hubs;

using SignalR.ViewModels;

using System;

using System.Collections;

using System.Collections.Generic;

using System.IO;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using Newtonsoft.Json;

using System.Diagnostics;

using System.Text.RegularExpressions;

 

namespace SignalR.Controllers

{

    public class HomeController : Controller

    {

        public ActionResult Index()

        {

     

            return View();

        }

 

 

        public ActionResult GetV(string v)

        {

            if (!string.IsNullOrEmpty(v))

            {

                string url = RedisHelper.Get(v)?.ToString();

                if (!string.IsNullOrEmpty(url))

                {

                    return Json(new { isOk = true, m = url }, JsonRequestBehavior.AllowGet);

                }

                return Json(new { isOk = false}, JsonRequestBehavior.AllowGet);

            }

            return Json(new { isOk = false }, JsonRequestBehavior.AllowGet);

        }

 

        public ActionResult getkey(string url)

        {

            if (!string.IsNullOrEmpty(url))

            {

                var s = "v" + Util.GetRandomLetterAndNumberString(new Random(), 5).ToLower();

                var dt = Convert.ToDateTime(DateTime.Now.AddDays(1).ToString("yyyy-MM-dd 04:00:00"));

                int min = Convert.ToInt16((dt - DateTime.Now).TotalMinutes);

                RedisHelper.Set(s, url, min);

                return Json(new { isOk = true, m = s }, JsonRequestBehavior.AllowGet);

            }

            return Json(new { isOk = false }, JsonRequestBehavior.AllowGet);

        }

 

        public ActionResult upfile()

        {

            try

            {

                if (Request.Files.Count > 0)

                {

                    var file = Request.Files[0];

                    if (file != null)

                    {

                        var imgList = new List<string>() { ".gif"".jpg"".bmp"".png" };

                        var videoList = new List<string>() { ".mp4" };

                        FileModel fmodel = new FileModel();

 

                        string name = Guid.NewGuid().ToString();

                        string fileExt = Path.GetExtension(file.FileName).ToLower();//上传文件扩展名

                        string path = Server.MapPath("~/files/") + name + fileExt;

                        file.SaveAs(path);

 

                        string extension = new FileInfo(path).Extension;

 

                        if (extension == ".mp4")

                        {

                            fmodel.t = 2;

                        }

                        else if (imgList.Contains(extension))

                        {

                            fmodel.t = 1;

                        }

                        else

                        {

                            fmodel.t = 0;

                        }

                        string url = Guid.NewGuid().ToString();

                        fmodel.url = "http://" + Request.Url.Host;

                        if (Request.Url.Port != 80)

                        {

                            fmodel.url += ":" + Request.Url.Port;

                        }

                        fmodel.url += "/files/" + name + fileExt;

                        GetImageThumb(Server.MapPath("~") + "files\\" + name + fileExt, name);

                        return Json(new { isOk = true, m = "file:" + JsonConvert.SerializeObject(fmodel) }, JsonRequestBehavior.AllowGet);

                    }

                }

            }

            catch(Exception ex)

            {

                Log.Info(ex);

            }

            

            

            return Content("");

        }

 

        public string GetImageThumb(string localVideo,string name)

        {

            string path = AppDomain.CurrentDomain.BaseDirectory;

            string ffmpegPath = path + "/ffmpeg.exe";

            string oriVideoPath = localVideo;

            int frameIndex = 5;

            int _thubWidth;

            int _thubHeight;

            GetMovWidthAndHeight(localVideo, out _thubWidth, out _thubHeight);

            int thubWidth = 200;

            int thubHeight = _thubWidth == 0 ? 200 : (thubWidth * _thubHeight / _thubWidth ); 

             

            string thubImagePath = path +  "files\\" + name + ".jpg";

            string command = string.Format("\"{0}\" -i \"{1}\" -ss {2} -vframes 1 -r 1 -ac 1 -ab 2 -s {3}*{4} -f image2 \"{5}\"", ffmpegPath, oriVideoPath, frameIndex, thubWidth, thubHeight, thubImagePath);

            Cmd.RunCmd(command);

            return name;

        }

 

        /// <summary>

        /// 获取视频的帧宽度和帧高度

        /// </summary>

        /// <param name="videoFilePath">mov文件的路径</param>

        /// <returns>null表示获取宽度或高度失败</returns>

        public static void GetMovWidthAndHeight(string videoFilePath, out int width, out int height)

        {

            try

            {

 

                //执行命令获取该文件的一些信息

                string ffmpegPath = AppDomain.CurrentDomain.BaseDirectory +  "/ffmpeg.exe";

                string output;

                string error;

                ExecuteCommand("\"" + ffmpegPath + "\"" " -i " "\"" + videoFilePath + "\""out output, out error);

                if (string.IsNullOrEmpty(error))

                {

                    width = 0;

                    height = 0;

                }

 

                //通过正则表达式获取信息里面的宽度信息

                Regex regex = new Regex("(\\d{2,4})x(\\d{2,4})", RegexOptions.Compiled);

                Match m = regex.Match(error);

                if (m.Success)

                {

                    width = int.Parse(m.Groups[1].Value);

                    height = int.Parse(m.Groups[2].Value);

                }

                else

                {

                    width = 0;

                    height = 0;

                }

            }

            catch (Exception)

            {

                width = 0;

                height = 0;

            }

        }

 

        public static void ExecuteCommand(string command, out string output, out string error)

        {

            try

            {

                //创建一个进程

                Process pc = new Process();

                pc.StartInfo.FileName = command;

                pc.StartInfo.UseShellExecute = false;

                pc.StartInfo.RedirectStandardOutput = true;

                pc.StartInfo.RedirectStandardError = true;

                pc.StartInfo.CreateNoWindow = true;

 

                //启动进程

                pc.Start();

 

                //准备读出输出流和错误流

                string outputData = string.Empty;

                string errorData = string.Empty;

                pc.BeginOutputReadLine();

                pc.BeginErrorReadLine();

 

                pc.OutputDataReceived += (ss, ee) =>

                {

                    outputData += ee.Data;

                };

 

                pc.ErrorDataReceived += (ss, ee) =>

                {

                    errorData += ee.Data;

                };

 

                //等待退出

                pc.WaitForExit();

 

                //关闭进程

                pc.Close();

 

                //返回流结果

                output = outputData;

                error = errorData;

            }

            catch (Exception)

            {

                output = null;

                error = null;

            }

        }

 

    }

 

    public class Util

    {

        public static string GetRandomLetterAndNumberString(Random random, int length)

        {

            if (length < 0)

            {

                throw new ArgumentOutOfRangeException("length");

            }

            char[] pattern = new char[] { '0''1''2''3''4''5''6''7''8''9',

        'A''B''C''D''E''F''G''H''I''J''K''L''M''N''O''P',

        'Q''R''S''T''U''V''W''X''Y''Z' };

            string result = "";

            int n = pattern.Length;

            for (int i = 0; i < length; i++)

            {

                int rnd = random.Next(0, n);

                result += pattern[rnd];

            }

            return result;

        }

    }

 

    class Cmd

    {

        private static string CmdPath = @"C:\Windows\System32\cmd.exe";

        /// <summary>

        /// 执行cmd命令 返回cmd窗口显示的信息

        /// 多命令请使用批处理命令连接符:

        /// <![CDATA[

        /// &:同时执行两个命令

        /// |:将上一个命令的输出,作为下一个命令的输入

        /// &&:当&&前的命令成功时,才执行&&后的命令

        /// ||:当||前的命令失败时,才执行||后的命令]]>

        /// </summary>

        /// <param name="cmd">执行的命令</param>

        public static string RunCmd(string cmd)

        {

            cmd = cmd.Trim().TrimEnd('&') + "&exit";//说明:不管命令是否成功均执行exit命令,否则当调用ReadToEnd()方法时,会处于假死状态

            using (Process p = new Process())

            {

                p.StartInfo.FileName = CmdPath;

                p.StartInfo.UseShellExecute = false;        //是否使用操作系统shell启动

                p.StartInfo.RedirectStandardInput = true;   //接受来自调用程序的输入信息

                p.StartInfo.RedirectStandardOutput = true;  //由调用程序获取输出信息

                p.StartInfo.RedirectStandardError = true;   //重定向标准错误输出

                p.StartInfo.CreateNoWindow = true;          //不显示程序窗口

                p.Start();//启动程序

 

                //向cmd窗口写入命令

                p.StandardInput.WriteLine(cmd);

                p.StandardInput.AutoFlush = true;

 

                //获取cmd窗口的输出信息

                string output = p.StandardOutput.ReadToEnd();

                p.WaitForExit();//等待程序执行完退出进程

                p.Close();

 

                return output;

            }

        }

    }

}<br data-filtered="filtered"><br data-filtered="filtered">我还是都写到一块了,大家记得分开!

SController.cs  这个是针对手机端单独拎出来的,里面不需要什么内容

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

 

namespace SignalR.Controllers

{

    public class SController : Controller

    {

        // GET: S

        public ActionResult Index()

        {

            return View();

        }

    }

}

 根目录新建一个ViewModels文件夹,里面新建FileModel.cs文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

 

namespace SignalR.ViewModels

{

    public class FileModel

    {

        /// <summary>

        /// 1 : 图片  2:视频

        /// </summary>

        public int t { getset; }

 

        public string url { getset; }

    }

} 

RedisHelper.cs

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

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

using Microsoft.AspNet.SignalR.Messaging;

using StackExchange.Redis;

using System;

using System.Collections.Generic;

using System.IO;

using System.Linq;

using System.Net;

using System.Runtime.Serialization.Formatters.Binary;

using System.Threading.Tasks;

using System.Web;

 

namespace SignalR

{

    public class RedisHelper

    {

        private static string Constr = "xxxx.cn:6379";

 

        private static object _locker = new Object();

        private static ConnectionMultiplexer _instance = null;

 

        /// <summary>

        /// 使用一个静态属性来返回已连接的实例,如下列中所示。这样,一旦 ConnectionMultiplexer 断开连接,便可以初始化新的连接实例。

        /// </summary>

        public static ConnectionMultiplexer Instance

        {

            get

            {

                if (Constr.Length == 0)

                {

                    throw new Exception("连接字符串未设置!");

                }

                if (_instance == null)

                {

                    lock (_locker)

                    {

                        if (_instance == null || !_instance.IsConnected)

                        {

                            _instance = ConnectionMultiplexer.Connect(Constr);

                        }

                    }

                }

                //注册如下事件

                _instance.ConnectionFailed += MuxerConnectionFailed;

                _instance.ConnectionRestored += MuxerConnectionRestored;

                _instance.ErrorMessage += MuxerErrorMessage;

                _instance.ConfigurationChanged += MuxerConfigurationChanged;

                _instance.HashSlotMoved += MuxerHashSlotMoved;

                _instance.InternalError += MuxerInternalError;

                return _instance;

            }

        }

 

        static RedisHelper()

        {

        }

 

 

        /// <summary>

        ///

        /// </summary>

        /// <returns></returns>

        public static IDatabase GetDatabase()

        {

            return Instance.GetDatabase();

        }

 

        /// <summary>

        /// 这里的 MergeKey 用来拼接 Key 的前缀,具体不同的业务模块使用不同的前缀。

        /// </summary>

        /// <param name="key"></param>

        /// <returns></returns>

        private static string MergeKey(string key)

        {

            return "SignalR:"+ key;

            //return BaseSystemInfo.SystemCode + key;

        }

 

        /// <summary>

        /// 根据key获取缓存对象

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <param name="key"></param>

        /// <returns></returns>

        public static T Get<T>(string key)

        {

            key = MergeKey(key);

            return Deserialize<T>(GetDatabase().StringGet(key));

        }

 

        /// <summary>

        /// 根据key获取缓存对象

        /// </summary>

        /// <param name="key"></param>

        /// <returns></returns>

        public static object Get(string key)

        {

            key = MergeKey(key);

            return Deserialize<object>(GetDatabase().StringGet(key));

        }

 

        /// <summary>

        /// 设置缓存

        /// </summary>

        /// <param name="key"></param>

        /// <param name="value"></param>

        /// <param name="expireMinutes"></param>

        public static void Set(string key, object value, int expireMinutes = 0)

        {

            key = MergeKey(key);

            if (expireMinutes > 0)

            {

                GetDatabase().StringSet(key, Serialize(value), TimeSpan.FromMinutes(expireMinutes));

            }

            else

            {

                GetDatabase().StringSet(key, Serialize(value));

            }

 

        }

 

 

        /// <summary>

        /// 判断在缓存中是否存在该key的缓存数据

        /// </summary>

        /// <param name="key"></param>

        /// <returns></returns>

        public static bool Exists(string key)

        {

            key = MergeKey(key);

            return GetDatabase().KeyExists(key); //可直接调用

        }

 

        /// <summary>

        /// 移除指定key的缓存

        /// </summary>

        /// <param name="key"></param>

        /// <returns></returns>

        public static bool Remove(string key)

        {

            key = MergeKey(key);

            return GetDatabase().KeyDelete(key);

        }

 

        /// <summary>

        /// 异步设置

        /// </summary>

        /// <param name="key"></param>

        /// <param name="value"></param>

        public static async Task SetAsync(string key, object value)

        {

            key = MergeKey(key);

            await GetDatabase().StringSetAsync(key, Serialize(value));

        }

 

        /// <summary>

        /// 根据key获取缓存对象

        /// </summary>

        /// <param name="key"></param>

        /// <returns></returns>

        public static async Task<object> GetAsync(string key)

        {

            key = MergeKey(key);

            object value = await GetDatabase().StringGetAsync(key);

            return value;

        }

 

        /// <summary>

        /// 实现递增

        /// </summary>

        /// <param name="key"></param>

        /// <returns></returns>

        public static long Increment(string key)

        {

            key = MergeKey(key);

            //三种命令模式

            //Sync,同步模式会直接阻塞调用者,但是显然不会阻塞其他线程。

            //Async,异步模式直接走的是Task模型。

            //Fire - and - Forget,就是发送命令,然后完全不关心最终什么时候完成命令操作。

            //即发即弃:通过配置 CommandFlags 来实现即发即弃功能,在该实例中该方法会立即返回,如果是string则返回null 如果是int则返回0.这个操作将会继续在后台运行,一个典型的用法页面计数器的实现:

            return GetDatabase().StringIncrement(key, flags: CommandFlags.FireAndForget);

        }

 

        /// <summary>

        /// 实现递减

        /// </summary>

        /// <param name="key"></param>

        /// <param name="value"></param>

        /// <returns></returns>

        public static long Decrement(string key, string value)

        {

            key = MergeKey(key);

            return GetDatabase().HashDecrement(key, value, flags: CommandFlags.FireAndForget);

        }

 

        /// <summary>

        /// 序列化对象

        /// </summary>

        /// <param name="o"></param>

        /// <returns></returns>

        private static byte[] Serialize(object o)

        {

            if (o == null)

            {

                return null;

            }

            BinaryFormatter binaryFormatter = new BinaryFormatter();

            using (MemoryStream memoryStream = new MemoryStream())

            {

                binaryFormatter.Serialize(memoryStream, o);

                byte[] objectDataAsStream = memoryStream.ToArray();

                return objectDataAsStream;

            }

        }

 

        /// <summary>

        /// 反序列化对象

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <param name="stream"></param>

        /// <returns></returns>

        private static T Deserialize<T>(byte[] stream)

        {

            if (stream == null)

            {

                return default(T);

            }

            BinaryFormatter binaryFormatter = new BinaryFormatter();

            using (MemoryStream memoryStream = new MemoryStream(stream))

            {

                T result = (T)binaryFormatter.Deserialize(memoryStream);

                return result;

            }

        }

 

        /// <summary>

        /// 配置更改时

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private static void MuxerConfigurationChanged(object sender, EndPointEventArgs e)

        {

            //LogHelper.SafeLogMessage("Configuration changed: " + e.EndPoint);

        }

 

        /// <summary>

        /// 发生错误时

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private static void MuxerErrorMessage(object sender, RedisErrorEventArgs e)

        {

            //LogHelper.SafeLogMessage("ErrorMessage: " + e.Message);

        }

 

        /// <summary>

        /// 重新建立连接之前的错误

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private static void MuxerConnectionRestored(object sender, ConnectionFailedEventArgs e)

        {

            //LogHelper.SafeLogMessage("ConnectionRestored: " + e.EndPoint);

        }

 

        /// <summary>

        /// 连接失败 , 如果重新连接成功你将不会收到这个通知

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private static void MuxerConnectionFailed(object sender, ConnectionFailedEventArgs e)

        {

            //LogHelper.SafeLogMessage("重新连接:Endpoint failed: " + e.EndPoint + ", " + e.FailureType +(e.Exception == null ? "" : (", " + e.Exception.Message)));

        }

 

        /// <summary>

        /// 更改集群

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private static void MuxerHashSlotMoved(object sender, HashSlotMovedEventArgs e)

        {

            //LogHelper.SafeLogMessage("HashSlotMoved:NewEndPoint" + e.NewEndPoint + ", OldEndPoint" + e.OldEndPoint);

        }

 

        /// <summary>

        /// redis类库错误

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private static void MuxerInternalError(object sender, InternalErrorEventArgs e)

        {

            //LogHelper.SafeLogMessage("InternalError:Message" + e.Exception.Message);

        }

 

        //场景不一样,选择的模式便会不一样,大家可以按照自己系统架构情况合理选择长连接还是Lazy。

        //建立连接后,通过调用ConnectionMultiplexer.GetDatabase 方法返回对 Redis Cache 数据库的引用。从 GetDatabase 方法返回的对象是一个轻量级直通对象,不需要进行存储。

 

        /// <summary>

        /// 使用的是Lazy,在真正需要连接时创建连接。

        /// 延迟加载技术

        /// 微软azure中的配置 连接模板

        /// </summary>

        //private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>

        //{

        //    //var options = ConfigurationOptions.Parse(constr);

        //    options.ClientName = GetAppName(); // only known at runtime

        //    //options.AllowAdmin = true;

        //    //return ConnectionMultiplexer.Connect(options);

        //    ConnectionMultiplexer muxer = ConnectionMultiplexer.Connect(Coonstr);

        //    muxer.ConnectionFailed += MuxerConnectionFailed;

        //    muxer.ConnectionRestored += MuxerConnectionRestored;

        //    muxer.ErrorMessage += MuxerErrorMessage;

        //    muxer.ConfigurationChanged += MuxerConfigurationChanged;

        //    muxer.HashSlotMoved += MuxerHashSlotMoved;

        //    muxer.InternalError += MuxerInternalError;

        //    return muxer;

        //});

 

 

        #region  当作消息代理中间件使用 一般使用更专业的消息队列来处理这种业务场景

 

        /// <summary>

        /// 当作消息代理中间件使用

        /// 消息组建中,重要的概念便是生产者,消费者,消息中间件。

        /// </summary>

        /// <param name="channel"></param>

        /// <param name="message"></param>

        /// <returns></returns>

        public static long Publish(string channel, string message)

        {

            StackExchange.Redis.ISubscriber sub = Instance.GetSubscriber();

            //return sub.Publish("messages", "hello");

            return sub.Publish(channel, message);

        }

 

        /// <summary>

        /// 在消费者端得到该消息并输出

        /// </summary>

        /// <param name="channelFrom"></param>

        /// <returns></returns>

        public static void Subscribe(string channelFrom)

        {

            StackExchange.Redis.ISubscriber sub = Instance.GetSubscriber();

            sub.Subscribe(channelFrom, (channel, message) =>

            {

                Console.WriteLine((string)message);

            });

        }

 

        #endregion

 

        /// <summary>

        /// GetServer方法会接收一个EndPoint类或者一个唯一标识一台服务器的键值对

        /// 有时候需要为单个服务器指定特定的命令

        /// 使用IServer可以使用所有的shell命令,比如:

        /// DateTime lastSave = server.LastSave();

        /// ClientInfo[] clients = server.ClientList();

        /// 如果报错在连接字符串后加 ,allowAdmin=true;

        /// </summary>

        /// <returns></returns>

        public static IServer GetServer(string host, int port)

        {

            IServer server = Instance.GetServer(host, port);

            return server;

        }

 

        /// <summary>

        /// 获取全部终结点

        /// </summary>

        /// <returns></returns>

        public static EndPoint[] GetEndPoints()

        {

            EndPoint[] endpoints = Instance.GetEndPoints();

            return endpoints;

        }

    }

}

  

 总体项目结构是这样的

 

 

下期我将把前端代码列出来,这个我只是为了实现功能,大神勿喷

 

转自https://www.cnblogs.com/colyn/p/11976006.html

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值