標籤

顯示具有 VB 標籤的文章。 顯示所有文章
顯示具有 VB 標籤的文章。 顯示所有文章

2011年9月18日 星期日

VB TCP 對外通訊與接收

.Net FrameWork 在 TCP/IP 通訊上支援的十分好. 幾乎只要 GOOGLE 之後就可以找出答案. 真正的問題在於, 接收和發送, 如果要顯示在 FORM 上, 仍然必須用 "委派" 的方式. 天下文章一大抄, 只要抄對了就好.
如果你考慮使用多工, 要小心通訊埠多次開啟的錯誤. 而主要是採用 TcpListener 為主. 重點要注意的是, 對外通訊必須使用 "192.168.XX.XX" 或 "10.X.X.X" 的 IP, 而不能使用 "127.0.0.1". 經實驗, 127.0.0.1 在 VB 對 VB 連線是沒問題的, 但是ANDROID 對 PC 連, 就會連不到.
另外, 收到的字元長度, 緩衝區定義為 1024, 長度就是 1024, 即使只傳了1個字, 其長度依然是 1024, 其它的字元都是 CHR(0). 所以你在判斷ANDROID傳回什麼答案時, 必須將CHR(0) 去掉才能進行判斷.
中文部份, 使用 UNICODE ENCODING 是可以接收到. 但是顯示時會有亂碼, (理論上 ANDROID 應該也是 UNICODE). 所以在轉換時, 請特別小心.
以下程式為支援 andriod 與 PC 通訊的 PC 端程式.
Imports System
Imports System.ComponentModel
Imports System.Threading
Imports System.Windows.Forms
Imports System.Net.Sockets
Imports System.Net
Imports System.Text
Private Class CSState
Public myTcpListener As TcpListener
Public ClientSocket As Socket
Public mystring As String
End Class
Private myDatatable As New DataTable
Private myTcpListener As TcpListener
Delegate Sub SetMsgCallBack(ByVal state As Object)
Private Sub DisplayMsg1(ByVal state As Object)
Dim myObj As New CSState
myObj = CType(state, CSState)
If Me.DataGridView1.InvokeRequired Then
Dim d As New SetMsgCallBack(AddressOf DisplayMsg1)
Me.Invoke(d, New Object() {myObj})
Else
Dim xRow As DataRow = myDatatable.NewRow()
xRow.Item(0) = CType(myObj.ClientSocket.RemoteEndPoint, IPEndPoint).Address.ToString()
xRow.Item(1) = CType(myObj.ClientSocket.RemoteEndPoint, IPEndPoint).Port.ToString()
xRow.Item(2) = myObj.mystring
myDatatable.Rows.Add(xRow)
End If
End Sub
Private Sub Form1_Load_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Activated
myDatatable.Columns.Add("IP")
myDatatable.Columns.Add("Port")
myDatatable.Columns.Add("Data")
DataGridView1.DataSource = myDatatable
DataGridView1.Columns(2).Width = 150
Dim iPort As Integer
iPort = "1024"
Dim ListenThread As New Thread(AddressOf StartListen)
ListenThread.IsBackground = True
ListenThread.Start(iPort)

End Sub
Private Sub StartListen(ByVal state As Object)
Dim iPort As Integer
iPort = CType(state, Integer)
myTcpListener = New TcpListener(IPAddress.Any, iPort)
Try
Dim ClientSocket As Socket
myTcpListener.Start()
Dim iCount As Integer = 0
Do
ClientSocket = myTcpListener.AcceptSocket()
If ClientSocket.Connected = True Then
Dim myObj As New CSState
myObj.myTcpListener = myTcpListener
myObj.ClientSocket = ClientSocket
myObj.mystring = Now.ToString("yyyy/MM/dd HH:mm:ss") & "已連線"
DisplayMsg1(myObj)
Dim ReceiveThread As New Thread(AddressOf ReceiveData)
ReceiveThread.IsBackground = True
ReceiveThread.Start(myObj)
iCount += 1
End If
Loop
Catch ex As Exception
MessageBox.Show(ex.ToString())
End Try
End Sub
Private Sub ReceiveData(ByVal state As Object)
Dim myObj As New CSState
myObj.ClientSocket = CType(state, CSState).ClientSocket
myObj.myTcpListener = CType(state, CSState).myTcpListener
myObj.mystring = ""
Dim myNetworkStream As New NetworkStream(myObj.ClientSocket)
Dim InBytesCount As Integer = 0
Dim myReceiveBytes(1023) As Byte
Dim i As Integer = 0
While True
Try
InBytesCount = myNetworkStream.Read(myReceiveBytes, 0, myReceiveBytes.Length)
System.Threading.Thread.Sleep(100)
If InBytesCount = 0 Then
Exit While
End If
myObj.mystring = Replace(Encoding.GetEncoding(950).GetString(myReceiveBytes).TrimEnd().TrimStart(), Chr(0), "")
DisplayMsg1(myObj)
'If (myObj.mystring.Equals("xxx")) Then
Dim myWriteBuffer As Byte() = Encoding.GetEncoding(950).GetBytes("Received: " + myObj.mystring)
myNetworkStream.Write(myWriteBuffer, 0, myWriteBuffer.Length)
System.Threading.Thread.Sleep(100)
'End If
Catch ex As Exception
MessageBox.Show(ex.ToString)
Exit Sub
End Try
End While
End Sub

RS-232 通訊讀取

對一個初學者來說, RS-232 通訊是一件苦差事. 從最早的 MSCOMM 到 .Net FrameWork 之後, 語法變更十分巨大. 因此如果您也是從組合語言起家的. 那進入 .Net FrameWork 之後, 你會完全不知道該怎麼辦.
下面的程式. 為基本 RS-232 通訊, 並與台灣 TCBUS 連接的範例. 程式碼如下.
其實, 最大的分別點只有. "當COM PORT 傳回資料時" 進行讀取, 使用"委派" 讓程式得以在多工下執行. 並沒有什麼其他的困難點, 而 TCBUS 最後的結尾字元為 0x13h 所以只需要大方的.
serialPort.WriteLine(cmd)
就完事了. 如果通訊方式非以換行字元為主, 那請參考前部落格的方式. 以陣列方式填入即可.

Imports System
Imports System.ComponentModel
Imports System.Threading
Imports System.Windows.Forms
Imports System.Net.Sockets
Imports System.Net
Imports System.Text
Public Class Form1
Public Shared ports As String() = IO.Ports.SerialPort.GetPortNames()
Public Shared myport As String
Public read_flag As Boolean
Public mystring As String
Public m_length As Integer
Public m_arRxBfx(25) As Byte
Public buffer_string As String
Public m_arTxBfx(25) As Byte
Delegate Sub PrintToText(ByVal InputString As String)
Private Sub ShowText(ByVal strReceive As String)
If Me.response.InvokeRequired Then
Dim d As New PrintToText(AddressOf ShowText)
Me.Invoke(d, New Object() {strReceive})
Else
Me.response.Text += mystring
End If
End Sub
Delegate Sub findport(ByVal InputString As String)
Public Shared Sub showport(ByVal strReceive As String)
If Form1.response.InvokeRequired Then
Dim d As New findport(AddressOf showport)
Form1.Invoke(d, New Object() {strReceive})
Else

For Each port In ports
' listBox1.Items.Add("Item " & x.ToString())
Form1.PortUsed.Items.Add(port)
myport = port
Next
Form1.PortUsed.SelectedIndex = 0
End If
'Form1.PortUsed.SetSelected(1, True)
End Sub
Private WithEvents serialPort As New IO.Ports.SerialPort
Dim sent_string(24) As Array
Dim register As Byte
Dim command As Byte
Dim station As Byte

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Call P_learn()
End Sub
Public Sub Connect(ByVal PortNo As String)
If PortNo = "" Then
PortNo = myport
End If
If serialPort.IsOpen Then
serialPort.Close()
End If
Try
With serialPort
.PortName = PortNo
.BaudRate = 38400
.Parity = IO.Ports.Parity.None
.DataBits = 8
.StopBits = IO.Ports.StopBits.One
.Handshake = IO.Ports.Handshake.None
End With
serialPort.Open()
Catch ex As Exception
End Try
End Sub
Private Sub mySerialPort_DataReceived(ByVal sender As Object, _
ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
Handles serialPort.DataReceived
read_flag = True
mystring = ""
m_length = serialPort.BytesToRead
If m_length Mod 25 = 0 Then
mystring = serialPort.ReadExisting + Chr(10)
ShowText("OK")
Else
Thread.Sleep(1000)
End If
read_flag = False
End Sub
Private Sub P_learn(ByVal cmd As String)
Connect(myport)
serialPort.WriteLine(cmd)
End Sub
Private Sub P_send(ByVal cmd As String)
Connect(myport)
serialPort.WriteLine(cmd)
End Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
P_send()
End Sub

Private Sub CircuitName_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CircuitName.HandleCreated
Dim count As String
For i = 0 To 99
If i < 10 Then
count = "0" + i.ToString
Else
count = i.ToString
End If
CircuitName.Items.Add(count)
Next
End Sub
Protected Overrides Sub OnLoad(ByVal e As EventArgs)
showport("OK")
End Sub

Private Sub P_learn()
MsgBox("沒有字串")
End Sub
Private Sub P_send()
MsgBox("沒有字串")
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ReadAll.Click
Dim cmd As String = "*C0009FEFF00FF0200000000"
response.Text = ""
RSCommand.Text = cmd
P_send(cmd)
End Sub
Private Sub SendCommand_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SendCommand.Click
Dim cmd As String
cmd = RSCommand.Text
P_send(cmd)
End Sub
Private Sub Attrib_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Attrib.HandleCreated
If Attrib.Text = "" Then
Attrib.Text = "0"
End If
End Sub
Private Sub PlusAttrib_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PlusAttrib.Click
Dim a As Integer
a = Attrib.Text
a = a + 1
Attrib.Text = a
End Sub
Private Sub MinusAttrib_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MinusAttrib.Click
Dim a As Integer
a = Attrib.Text
a = a - 1
Attrib.Text = a
End Sub
Private Sub OnCmd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OnCmd.Click
Dim st As Integer
Dim c As Integer
Dim precmd As String = "*C00190E0100"
Dim midcmd As String = "01"
Dim endcmd As String = "000000A"
Dim st_string As String
st = StationName.SelectedItem
c = Attrib.Text
st_string = Hex(st)
If st_string.Length = 1 Then
st_string = "0" + st_string
End If
st_string = precmd + st_string + midcmd + c.ToString + endcmd
RSCommand.Text = st_string
End Sub
Private Sub StationName_init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StationName.HandleCreated
For i = 10 To 99
StationName.Items.Add(i)
Next
StationName.SelectedIndex = 0
End Sub

End Class