1. 程式人生 > 程式設計 >C# Socket程式設計實現簡單的區域網聊天器的示例程式碼

C# Socket程式設計實現簡單的區域網聊天器的示例程式碼

前言

最近在學習C# Socket相關的知識,學習之餘,動手做了一個簡單的區域網聊天器。有萌生做這個的想法,主要是由於之前家裡兩臺電腦之間想要傳輸檔案十分麻煩,需要藉助QQ,微信或者其他第三方應用,基本都要登入,而且可能傳輸的檔案還有大小限制,壓縮問題。所以本聊天器的首要目標就是解決這兩個問題,做到使用方便(雙擊啟動即用),傳檔案無限制。

廢話不多說,先上圖。S-Chat是服務端,C-Chat是客戶端,兩者除了客戶端首次啟動後需要設定一下連線的IP地址外,無其他區別。操作與介面都完全相同,對於使用者來說,基本不用在意誰是服務端誰是客戶端。

C# Socket程式設計實現簡單的區域網聊天器的示例程式碼

C# Socket程式設計實現簡單的區域網聊天器的示例程式碼

編碼

服務端監聽介面

服務端主要負責開啟監聽執行緒,等待客戶端接入

public void StartListen()
{
 // 建立Socket物件 new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp)
 Socket socket = GetSocket();
 // 將套接字與IPEndPoint繫結
 socket.Bind(this.GetIPEndPoint());
 // 開啟監聽 僅支援一個連線
 socket.Listen(1);
 // 開啟執行緒等待客戶端接入,避免堵塞
 Thread acceptThread = new Thread(new ThreadStart(TryAccept));
 acceptThread.IsBackground = true;
 acceptThread.Start();
}

public void TryAccept()
{
 Socket socket = GetSocket();
 while (true)
 {
  try
  {
   Socket connectedSocket = socket.Accept()
   this.ConnectedSocket = connectedSocket;
   OnConnect(); // 連線成功回撥
   this.StartReceive(); // 開始接收執行緒
   break;
  }
  catch (Exception e)
  {
  }
 }
}

客戶端連線介面

客戶端主要負責開啟連線執行緒,每隔2秒,自動嘗試連線服務端

public void StartConnect()
{
 Thread connectThread = new Thread(new ThreadStart(TryConnect));
 connectThread.IsBackground = true;
 connectThread.Start();
}

public void TryConnect()
{
 Socket socket = GetSocket();
 while (true)
 {
  try
  {
   socket.Connect(this.GetIPEndPoint());
   this.ConnectedSocket = socket;
   OnConnect(); // 連線成功回撥
   this.StartReceive();
   break;
  }
  catch (Exception e)
  {
   Thread.Sleep(TryConnectInterval); // 指定間隔後重新嘗試連線
  }
 }
}

文字傳送,檔案傳送,接收文字,接收檔案等通用介面主要實現在 ChatBase 類中,是服務端與客戶端的共同父類。

文字傳送介面

傳送資料的第一位表示傳送資訊的型別,0表示字串文字,1表示檔案

然後獲取待發送字串的長度,使用long型別表示,佔用8個位元組

共傳送的位元組資料可以表示為頭部(型別 + 字串位元組長度,共9個位元組)+ 實際字串位元組資料

public bool Send(string msg)
{
 if (ConnectedSocket != null && ConnectedSocket.Connected)
 {
  byte[] buffer = UTF8.GetBytes(msg); 
  byte[] len = BitConverter.GetBytes((long)buffer.Length); 
  byte[] content = new byte[1 + len.Length + buffer.Length]; 
  content[0] = (byte)ChatType.Str; // 傳送資訊型別,字串
  Array.Copy(len,content,1,len.Length); // 字串位元組長度
  Array.Copy(buffer,1 + len.Length,buffer.Length); // 實際字串位元組資料
  try
  {
   ConnectedSocket.Send(content);
   return true;
  }
  catch (Exception e)
  {
  }
 }
 return false;
}

檔案傳送介面

與字串傳送相同的頭部可以表示為(型別 + 檔案長度,共9個位元組)

還需要再加上待發送的檔名的長度,與檔名位元組資料

共傳送的位元組資料可以表示為頭部(型別 + 檔案長度,共9個位元組)+ 檔名頭部(檔名長度 + 檔名位元組資料)+ 實際檔案資料

public bool SendFile(string path)
{
 if (ConnectedSocket != null && ConnectedSocket.Connected)
 {
  try
  {
   FileInfo fi = new FileInfo(path);
   byte[] len = BitConverter.GetBytes(fi.Length); 
   byte[] name = UTF8.GetBytes(fi.Name); 
   byte[] nameLen = BitConverter.GetBytes(name.Length); 
   byte[] head = new byte[1 + len.Length + nameLen.Length + name.Length];
   head[0] = (byte)ChatType.File; // 加上資訊傳送型別
   Array.Copy(len,head,len.Length); // 加上檔案長度
   Array.Copy(nameLen,nameLen.Length); // 加上檔名長度
   Array.Copy(name,1 + len.Length + nameLen.Length,name.Length); // 加上檔名位元組資料
   ConnectedSocket.SendFile(
    path,null,TransmitFileOptions.UseDefaultWorkerThread
   );
   return true;
  }
  catch(Exception e)
  {
  }
 }
 return false;
}

資訊接收介面(文字與檔案)

主要是解析接收到的位元組資料,根據字串或檔案的型別進行處理

public void Receive()
{
 if (ConnectedSocket != null)
 {
  while (true)
  {
   try
   {
    // 讀取公共頭部
    byte[] head = new byte[9];
    ConnectedSocket.Receive(head,head.Length,SocketFlags.None);
    int len = BitConverter.ToInt32(head,1);
    if (head[0] == (byte) ChatType.Str)
    {
     // 接收字串
     byte[] buffer = new byte[len];
     ConnectedSocket.Receive(buffer,len,SocketFlags.None);
     OnReceive(ChatType.Str,UTF8.GetString(buffer));
    }
    else if(head[0] == (byte)ChatType.File)
    {
     // 接收檔案
     if (!Directory.Exists(dirName))
     {
      Directory.CreateDirectory(dirName);
     }
     // 讀取檔名資訊
     byte[] nameLen = new byte[4];
     ConnectedSocket.Receive(nameLen,nameLen.Length,SocketFlags.None);
     byte[] name = new byte[BitConverter.ToInt32(nameLen,0)];
     ConnectedSocket.Receive(name,name.Length,SocketFlags.None);
     string fileName = UTF8.GetString(name);
     // 讀取檔案內容並寫入
     int readByte = 0;
     int count = 0;
     byte[] buffer = new byte[1024 * 8];
     string filePath = Path.Combine(dirName,fileName);
     if (File.Exists(filePath))
     {
      File.Delete(filePath);
     }
     using (FileStream fs = new FileStream(filePath,FileMode.Append,FileAccess.Write))
     {
      while (count != len)
      {
       int readLength = buffer.Length;
       if(len - count < readLength)
       {
        readLength = len - count;
       }
       readByte = ConnectedSocket.Receive(buffer,readLength,SocketFlags.None);
       fs.Write(buffer,readByte);
       count += readByte;
      }
     }
     OnReceive(ChatType.File,fileName); 
    }
    else
    {
     // 未知型別
    }
   }
   catch (Exception e)
   {
   }
  }
 }
}

使用

第一次使用,客戶端需要設定待連線的IP地址。之後再啟動會自動連線

雙擊服務端exe啟動,點選 設定 ,檢視IP地址項

C# Socket程式設計實現簡單的區域網聊天器的示例程式碼

雙擊客戶端exe啟動,點選 設定 ,在 IP地址 項,輸入服務端檢視到的IP地址

C# Socket程式設計實現簡單的區域網聊天器的示例程式碼

  • 設定成功後,等待大約一兩秒,應用cion變成綠色,即表示連線成功,可以正常傳送文字和檔案了
  • 可以點選選擇檔案(支援選擇多個檔案),傳送檔案
  • 支援直接拖拽檔案到輸入框,傳送檔案
  • 支援Ctrl+Enter快捷鍵傳送
  • 接收到的檔案自動存放在exe所在目錄的ChatFiles資料夾下

注意事項

客戶端服務端需要在同一個區域網下才能實現連線
服務端IP地址是不支援修改的,自動讀取本機的IP地址

原始碼
完整程式碼放在GitHub上,點選檢視
預編譯好的可執行exe程式,在倉庫的Release目錄,也可以直接本地下載:下載地址

到此這篇關於C# Socket程式設計實現簡單的區域網聊天器的示例程式碼的文章就介紹到這了,更多相關C# Socket 區域網聊天器內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!