title: Store multiple TcpClient Connections - VB.NET
body: <p>I'm building an application that can communicate and send commands to game servers. In the gaming world we call this “rcon” for remote console, but in reality it's just a telnet session. After successful authentication, a text command can be issued to the server and a text response is sent back to the client.</p>
  
  <p>Often times server admins/owners run multiple game servers and every server has its own distinct ip address, port &amp; password. I would like my application to loop through a list/array/datatable/dictionary (or whatever method is best ) of servers, connect and authenticate to each server, then somehow store that connection so commands can be sent to it in the future (Without having to connect &amp; authenticate again)</p>
  
  <p>Below is the code I have that works for a single server. I store the TcpClient(client) &amp; NetworkStream(stm) as global objects which keeps the connection/session open to the server. What is the best method for me to do this with multiple servers since each server will have it's own TcpClient &amp; NetworkStream?</p>
  
  <p>Is there some way for me to store each TcpClient(client) &amp; NetworkStream(stm) in an array, dictionary or something? I'm completely lost with how to approach this.</p>
  
  <pre><code>Imports System.Net.Sockets
  Imports System.Text
  Imports System.Threading
  Imports System.Security.Cryptography
  Imports System.IO
  
  Public Class Form1
  
      Public client As TcpClient
      Public stm As NetworkStream
  
      Sub SendCmd(cmd As String)
          Dim data
          If cmd.Trim = "" Then
              data = Encoding.GetEncoding(1252).GetBytes(cmd)
          Else
              data = Encoding.GetEncoding(1252).GetBytes(cmd &amp; vbCrLf)
          End If
  
          stm.Write(data, 0, data.Length)
          Dim resp As Byte() = New Byte(512) {}
          Dim memStream = New MemoryStream()
          Thread.Sleep(500)
          Dim bytes As Integer = 0
          If stm.DataAvailable Then
              Do
                  Thread.Sleep(10)
                  bytes = stm.Read(resp, 0, resp.Length)
  
                  memStream.Write(resp, 0, bytes)
              Loop While stm.DataAvailable
              Dim responsedata As String = Encoding.GetEncoding(1252).GetString(memStream.ToArray())
  
              If responsedata.Contains("### Digest seed: ") Then
                  'The server is asking for authentication: Login to the server now
                  Authenticate(responsedata)
              End If
              OutputRTB.Text &amp;= responsedata
          End If
          memStream.Close()
      End Sub
  
      Sub GetPlayerList()
          If stm.CanRead Then
              Dim data = Encoding.GetEncoding(1252).GetBytes("bf2cc pl" &amp; vbCrLf)
  
  
              Dim PlayerDT As New DataTable
              PlayerDT.Columns.Add("PlayerSlot", GetType(Integer))
              PlayerDT.Columns.Add("HeroName", GetType(String))
              PlayerDT.Columns.Add("Score", GetType(String))
              PlayerDT.Columns.Add("HeroID", GetType(String))
              PlayerDT.Columns.Add("PlayerID", GetType(String))
              PlayerDT.Columns.Add("Level", GetType(Integer))
              PlayerDT.Columns.Add("Class", GetType(String))
              PlayerDT.Columns.Add("Ping", GetType(Integer))
  
  
              stm.Write(data, 0, data.Length)
              Dim resp As Byte() = New Byte(512) {}
              Dim memStream = New MemoryStream()
              Thread.Sleep(500)
              Dim bytes As Integer = 0
              If stm.DataAvailable Then
                  Do
                      Thread.Sleep(10)
                      bytes = stm.Read(resp, 0, resp.Length)
  
                      memStream.Write(resp, 0, bytes)
                  Loop While stm.DataAvailable
                  Dim responsedata As String = Encoding.GetEncoding(1252).GetString(memStream.ToArray())
  
                  If responsedata.Contains("### Digest seed: ") Then
                      'Login to the server
                      Authenticate(responsedata)
                  End If
                  OutputRTB.Text = responsedata.Replace(vbTab, "^")
  
  
                  Dim Rows() As String = responsedata.Split(Environment.NewLine)
                  For i = 0 To Rows.Length - 1
                      Dim Cols() As String = Rows(i).Split(vbTab)
                      If Cols.Length &gt; 40 Then
                          PlayerDT.Rows.Add(Cols(0).ToString(), Cols(1).ToString(), Cols(37).ToString(), Cols(10).ToString(), Cols(47).ToString(), Cols(39).ToString(), Cols(34).ToString(), Cols(3).ToString)
                      End If
                  Next
  
  
              End If
              DataGridView1.DataSource = PlayerDT
              DataGridView1.Update()
  
              memStream.Close()
          End If
      End Sub
  
      Sub Authenticate(ByVal rdata As String)
          Dim DigestKeyStart As Integer = rdata.LastIndexOf(":") + 2
          Dim DigestKeyLen As Integer = 16
  
          Dim PWResponse As String = rdata.Substring(DigestKeyStart, DigestKeyLen) &amp; PassTXT.Text
          PWResponse = "login " &amp; GenerateHash(PWResponse)
          SendCmd(PWResponse)
      End Sub
  
      Private Function GenerateHash(ByVal SourceText As String) As String
          Dim objMD5 As New System.Security.Cryptography.MD5CryptoServiceProvider
          Dim arrData() As Byte
          Dim arrHash() As Byte
  
          ' first convert the string to bytes (using UTF8 encoding for unicode characters)
          arrData = System.Text.Encoding.UTF8.GetBytes(SourceText)
  
          ' hash contents of this byte array
          arrHash = objMD5.ComputeHash(arrData)
  
          ' thanks objects
          objMD5 = Nothing
  
          ' return formatted hash
          Return ByteArrayToString(arrHash)
      End Function
  
      Private Function ByteArrayToString(ByVal arrInput() As Byte) As String
  
          Dim strOutput As New System.Text.StringBuilder(arrInput.Length)
  
          For i As Integer = 0 To arrInput.Length - 1
              strOutput.Append(arrInput(i).ToString("X2"))
          Next
  
          Return strOutput.ToString().ToLower
  
      End Function
  
      Private Sub Form1_FormClosing(sender As System.Object, e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
          stm.Close()
          client.Close()
      End Sub
  
      Private Sub ConnectBTN_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ConnectBTN.Click
          client = New TcpClient(ServerIPTXT.Text, PortTXT.Text)
          stm = client.GetStream()
          SendCmd(CommandTXT.Text)
      End Sub
  
      Private Sub SendCommandBTN_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SendCommandBTN.Click
          SendCmd(CommandTXT.Text)
      End Sub
  
      Private Sub GetPlayerListBTN_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GetPlayerListBTN.Click
          GetPlayerList()
      End Sub
  End Class
  </code></pre>
  
  <p>BTW This is my first post on stackoverflow, but I have learned so much from this site over the years =)</p>