软件工程 2016.7.5日报

软件工程 2016.7.5日报


 

  今天我的主要工作是晚场了客户端功能的搭建、连接了客户端UI与客户端Socket部分的功能,为服务端增加了文件锁避免多个线程对同一文件同时操作。

  具体实现的工作有:

  客户端功能搭建:

    在客户端完成了通信功能的实现:

  补全了昨天空缺的代码,在收到消息时进行相应的处理:

 1                     if (arrMsg[0] == SEND_MSG)
 2                     {
 3                         ReceiveMsgFromServer(msgReceive);
 4                     }
 5                     else if (arrMsg[0] == IS_RECEIVE_MSG)
 6                     {
 7                         Application.Current.Dispatcher.Invoke(new Action(delegate
 8                         {
 9                             MessageBox.Show("发送消息成功");
10                         }));
11                     }
12                     else if (arrMsg[0] == IS_NOT_RECEIVE_MSG)
13                     {
14                         Application.Current.Dispatcher.Invoke(new Action(delegate
15                         {
16                             MessageBox.Show("[Error]发送消息失败");
17                         }));
18                     }
19                     else if (arrMsg[0] == INVALID_MESSAGE)
20                     {
21                         Application.Current.Dispatcher.Invoke(new Action(delegate
22                         {
23                             MessageBox.Show("[Error]通信过程出错");
24                         }));
25                     }
26                     else
27                     {
28                         Application.Current.Dispatcher.Invoke(new Action(delegate
29                         {
30                             MessageBox.Show("[Error]通信过程出错");
31                         }));
32                     }

  其中,ReceiveMsgFromServer(string str);的具体实实现如下:

 1         #region --- Receive Room History Message ---
 2         /// <summary>
 3         ///     Receive Message
 4         /// </summary>
 5         /// <param name="msgReceive"></param>
 6         private void ReceiveMsgFromServer(string msgReceive)
 7         {
 8             MsgHandler msgHandler = (MsgHandler)JsonConvert.DeserializeObject(msgReceive, typeof(MsgHandler));
 9             string roomId = msgHandler.roomId;
10             List<string> msgList = msgHandler.msgList;
11 
12             Application.Current.Dispatcher.Invoke(new Action(delegate
13             {
14                 tucaoWall.Document.Blocks.Clear();
15                 string room = (string)courseList.SelectedItem;
16                  if (room.Equals(roomId))
17                     foreach (string msg in msgList)
18                     {
19                         // TODO : 将消息逐一添加到显示框中
20                         Paragraph newParagraph = new Paragraph();
21 
22                         InlineUIContainer inlineUIContainer = new InlineUIContainer()
23                         {
24                             Child = new TextBlock()
25                             {
26                                 Foreground = new SolidColorBrush(Colors.Black),
27                                 TextWrapping = TextWrapping.Wrap,
28                                 Text = msg + "
"
29                             }
30                         };
31                         newParagraph.Inlines.Add(inlineUIContainer);
32 
33                         tucaoWall.Document.Blocks.Add(newParagraph);
34                     }
35                  
36             }));
37         }
38         #endregion

  客户端UI与客户端Socket的连接

   添加了响应控件的响应,用户点击不同的按钮会调用不同的方法与服务器进行交互。由于代码过于琐碎,在此不做列举。

  

  服务器端修改了部分逻辑,同时对文件增加了读写锁。

   由于原来在服务端对每个用户都新建了一个线程,保证了在接受消息端是同步的。但是却没有保证发送消息、处理文件等的并发性(可以同时操纵多个文件)。所以将相关的方法封装为一个类,对于每一个新链接的用户新建一个该类对象,这样每个用户就可以通过自己保存的类对象进行相关的操作,而互不影响。此部分功能实现代码如下:

   添加从用户ip索引处理对象的字典

        public static Dictionary<string, Handler> dictHandler = new Dictionary<string, Handler>();

   处理对象Handler封装的方法包括

        public void CheckRoomList(string s_roomList);
        public void AddMsgToFile(string clientIP, string msg);
        public void InvalidMsg(string clientIP);
        public void SendMessage(string clientIP, byte flag, string msg);

   使用特定ip对应的Handler对象的方法为

 1 if (msgReceiver[0] == CHECK_ROOM_LIST)
 2     dictHandler[socketKey].CheckRoomList(msg);
 3 else if (msgReceiver[0] == REQUEST_ROOM_MSG)
 4     dictHandler[socketKey].GetRoomMsg(socketKey, msg);
 5 else if (msgReceiver[0] == SEND_MSG)
 6     dictHandler[socketKey].AddMsgToFile(socketKey, msg);
 7 else if (msgReceiver[0] == DISCONNECT)
 8      RemoveOfflineUser(socketKey);
 9 else
10      dictHandler[socketKey].InvalidMsg(socketKey);

   实现上述功能之后,还需要处理文件读写同步的问题,同一文件在同一时间不能被多个线程操作,所以为解决此问题需要为每个文件添加一个读写锁,来控制文件读写同步的问题。

   在程序中创建由文件名索引到读写锁的字典

public static Dictionary<string, Object> dictLocker = new Dictionary<string, object>();

   在每次操作文件时都需要进行并发控制,检测相应读写锁的使用情况,使用例子如下:

 1 lock(Server.dictLocker[room])
 2 {
 3     FileStream fs = new FileStream(roomFile, FileMode.Create);
 4     fs.Close();
 5 
 6     lock (Server.dictLocker["room"])
 7     {
 8         string romFile = "room.txt";
 9 
10         FileStream f = File.OpenWrite(romFile);
11 
12         f.Position = f.Length;
13 
14         byte[] writeMsg = Encoding.UTF8.GetBytes(room+"
");
15 
16         f.Write(writeMsg, 0, writeMsg.Length);
17 
18         f.Close();
19     }
20 }

  通过上述方式,实现了文件的读写控制。

原文地址:https://www.cnblogs.com/fanfan-blogs/p/5645531.html