none
VB2008 建立Socket的應用疑問 RRS feed

  • 問題

  • 當小弟將原本直接由UDPCilent建構的程式改以Socket的方式來製作!載看了MSDN的資料後做了一個SAMPLE如下:
    本機位址:192.168.0.2 ,9600
    PLC位址:192.168.0.3  ,9600
     Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
            Dim myIPEndPoint As New IPEndPoint(IPAddress.Any, 0)
            Dim mySocket As New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
            Dim readerstr As String = ""
            Dim SXD As String = "800002000300000200000101820065000001"
            Dim sendBytes As [Byte]()
            ReDim sendBytes(SXD.Length \ 2 - 1)
            For ix As Integer = 0 To sendBytes.Length - 1
                sendBytes(ix) = CByte(Val("&H" & SXD.Substring(ix * 2, 2)))
            Next
            Try
                mySocket.Connect("192.168.0.3", 9600)
                mySocket.Send(sendBytes)
                Dim mybytes(15) As Byte
                mySocket.ReceiveFrom(mybytes, myIPEndPoint)
                Dim byteCount As Integer = mybytes.Length
                If byteCount > 0 Then
                    Label4.Text = "連線成功"
                End If
                Dim xx As Integer = mybytes.Length
                For j As Integer = 1 To xx - 1
                    Dim PQ As Integer = mybytes(j)
                    Dim s As String = Hex(PQ)
                    If Len(s) < 2 Then
                        s = "0" + s
                    End If
                    readerstr = readerstr + s
                Next
                Label3.Text = readerstr
                mySocket.Close()
            Catch ex As Exception
            End Try
        End Sub
    這樣的確能上PLC並做傳送&接收的功能!但收到的碼卻是不對的!請問大大在這個程式中!我目前還轉不出來的是為何收碼的值是錯的?
    mySocket.ReceiveFrom(mybytes, myIPEndPoint) 這邊的指定我目前還不太清楚
    麻煩大大指點一下


    新手上路
    • 已編輯 eblue 2009年8月26日 上午 03:26
    2009年8月26日 上午 02:44

解答

  •    你完全亂了.
       本機位址:192.168.0.2 ,9600
       PLC位址:192.168.0.3  ,9600

        '先設定電腦的
       Dim myIpaddress As IPAddress = (IPAddress.Parse("192.168.0.2"))
       Dim myIpEndPoint As New IPEndPoint(myIpaddress, 9600)
       mySocket.Bind(myIpEndPoint)

       '設定PLC的
       Dim PLCIpAddress As IPAddress = (IPAddress.Parse("192.168.0.3"))
       Dim PLCIpEndPoint As New IPEndPoint(PLCIpAddress, 9600)
      
       'Send
        mySocket.SendTo(sendBytes, PLCendPoint)

        'Receive
        mySocket.ReceiveFrom(mybytes, PLCendPoint)

       
      
      
    請關心自己的問題,不要問了就放空;這是對別人與自己的尊重。如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    • 已標示為解答 eblue 2009年8月27日 上午 06:27
    2009年8月26日 下午 01:26
    版主

所有回覆

  • eblue:
       1. 有個建議, 你在測試時期Catch到的execption, 最好是不要做無處理的動作, 不然發生錯誤你也不曉得
       2. MSDN文件庫中有提到一件事,  在呼叫 ReceiveFrom 之前,您必須使用 Bind 方法,將 Socket 明確繫結至本機端點。如果不這樣做, ReceiveFrom 將會擲回 SocketException
           [
    Socket. ReceiveFrom 方法 (Byte[] , EndPoint ) ]
      
        先修正這兩項, 再測看看
    請關心自己的問題,不要問了就放空;這是對別人與自己的尊重。如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    2009年8月26日 上午 03:42
    版主
  • 感謝大大的幫助
    我在查詢MSDN時也有看到我必須先做Bind的動作.但是Bind這卻是try迴圈執行時跳出的地方
    請問EndPoint的宣告方式是這樣嗎??
     Dim myIpaddress As IPAddress = (IPAddress.Parse("192.168.0.2")) 'PC的位址
     Dim myendPoint As New IPEndPoint(myIpaddress, 9600)
     Dim senderRemote As EndPoint = CType(myendPoint, EndPoint)

    將這行Bind(senderRemote)
    放置於  mySocket.ReceiveFrom(mybytes, myIPEndPoint) 前面
    會出現異常訊息"提供一個不正確的引數"

    我想目前我的問題是卡在不知該如何給EndPoint



    新手上路
    2009年8月26日 上午 04:23
  • Bind (這邊是本機的EndPoint)
    ReceiveFrom(byte[], 這邊是遠端的EndPoint)
    你的寫法變成自己收自己..
    所以要宣告兩個EndPoint , 一個是本機,給Bind用, 一個是遠端 也就是 (IpAddress.Any,0) 給ReceiveFrom用


    請關心自己的問題,不要問了就放空;這是對別人與自己的尊重。如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    2009年8月26日 上午 05:37
    版主
  • 了解!小弟將程式修改了一下:
       Dim mySocket As New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
            Dim myIpaddress As IPAddress = (IPAddress.Parse("192.168.0.3"))
            Dim PLCendPoint As New IPEndPoint(myIpaddress, 9600)
            Dim PCendPoint As New IPEndPoint(IPAddress.Any, 0)
            Dim readerstr As String = ""
            Dim SXD As String = "800002000300000200000101B00001000001"
            Dim sendBytes As [Byte]()
            ReDim sendBytes(SXD.Length \ 2 - 1)
            For ix As Integer = 0 To sendBytes.Length - 1
                sendBytes(ix) = CByte(Val("&H" & SXD.Substring(ix * 2, 2)))
            Next
            mySocket.Bind(PLCendPoint)
            mySocket.Connect("192.168.0.3", 9600)
            mySocket.Send(sendBytes, sendBytes.Length, SocketFlags.None)
            Try
                Dim mybytes() As Byte = New [Byte](15) {}
                mySocket.ReceiveFrom(mybytes, PCendPoint)
                Dim byteCount As Integer = mybytes.Length
                If byteCount > 0 Then
                    Label4.Text = "連線成功"
                End If
                mySocket.Close()
            Catch ex As Exception
                Label1.Text = "錯誤"
            End Try
    所得到的回傳資料與一開始時相同!是否在接收的部分小弟上有遺漏的呢?
    新手上路
    • 已編輯 eblue 2009年8月26日 上午 06:13
    2009年8月26日 上午 06:03
  • eblue:
       正確和後來不正確的比較你是用字串看的嗎? 還是比較個別Byte值?
       如果個別Byte值是相同, 而字串值不同, 問題就出在你把 Byte陣列轉成字串的時候發生的了.
       原則上不可能會因為你用Socket類別或UdpClient類別而接到不一樣的資料.
     
    請關心自己的問題,不要問了就放空;這是對別人與自己的尊重。如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    2009年8月26日 上午 09:31
    版主
  • 我是看byte的值!這個會給出16個byte
    用原本UDPCLient 取得的byte值分別為:192,0,2,0,2,0,0,3,0,0,1,1,0,0,0,2
    用Socket取得值為                            :192,0,2,0,2,0,0,3,0,0,1,1,33,8,0,0
    進行別的連線語法socket收到的值一樣是沒改變!這樣來看可能是我收碼的問題了

    我想在跟大大們確認一下
    1.建立連線
    mySocket.Connect("192.168.0.3", 9600) -->這是我開啟PC->PLC的連線 PLC位址:192.168.0.3 , 9600
    2.Bind的連結
    Dim PCendPoint As New IPEndPoint(IPAddress.Any, 0) <----這邊的宣告對嗎?
    Dim senderRemote As EndPoint = CType(PCendPoint, EndPoint)
    mySocket.Bind(senderRemote)<----將socket連機至PC端點
    3.收碼
    Dim myIpaddress As IPAddress = (IPAddress.Parse("192.168.0.2"))
    Dim PLCendPoint As New IPEndPoint(myIpaddress, 9600)
    Dim Remote As EndPoint = CType(PLCendPoint, EndPoint)
    Dim mybytes() As Byte = New [Byte](15) {}
    mySocket.ReceiveFrom(mybytes, Remote)

    是否我有誤解的地方呢


    新手上路
    2009年8月26日 上午 10:10
  • 1. UDP不需要Connect, 不過你如果不Connect , 就必須把Send 改成SendTo
    2. 你的EndPoint設定有點怪怪的
        (1)一般是電腦端才會固定IP和Port
        (2)遠端反而習慣都是用 IPAddress.Any, 0
        (3)不過我喜歡用偷吃步, 就是電腦端用 IPAddress,固定port 或 IpAddress,0 . 遠端一律用 Ipaddress.any , 0
    3. 我檢查了你兩次發文的程式, 你兩次發送給 PLC的命令不一樣啊
        上次: 8000020003000002000001018200640000C8
        這次: 800002000300000200000101B00001000001
        所以收回來不一樣, 應該是正常的吧?

    請關心自己的問題,不要問了就放空;這是對別人與自己的尊重。如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    2009年8月26日 上午 10:31
    版主
  • 就是因為給的連線指令不同!而收到的都一樣才覺得有問題!
    大大的做法小弟在試試!感謝


    新手上路
    2009年8月26日 下午 12:47
  • 通常寫這類程式我會用個監控軟體看封包的傳送是否正確. 因為搞不好是PLC真的都送一樣的東西回來
    http://toget.pchome.com.tw/intro/network_tool/network_tool_monitor/24306.html
    你可以用這個工具看看

    請關心自己的問題,不要問了就放空;這是對別人與自己的尊重。如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    2009年8月26日 下午 01:09
    版主
  • 1. UDP不需要Connect, 不過你如果不Connect , 就必須把Send 改成SendTo
    2. 你的EndPoint設定有點怪怪的
        (1)一般是電腦端才會固定IP和Port
        (2)遠端反而習慣都是用 IPAddress.Any, 0
        (3)不過我喜歡用偷吃步, 就是電腦端用 IPAddress,固定port 或 IpAddress,0 . 遠端一律用 Ipaddress.any , 0
    3. 我檢查了你兩次發文的程式, 你兩次發送給 PLC的命令不一樣啊
        上次: 8000020003000002000001018200640000C8
        這次: 800002000300000200000101B00001000001
        所以收回來不一樣, 應該是正常的吧?

    請關心自己的問題,不要問了就放空;這是對別人與自己的尊重。如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事

    不好意思這個連線問題真麻煩大大了..我重整了一下!我列出執行的重點請大大在指導我
    1.將資料送至PLC
            Dim myIpaddress As IPAddress = (IPAddress.Parse("192.168.0.3"))
            Dim PLCendPoint As New IPEndPoint(myIpaddress, 9600) /PLC內部開啟的通訊埠為9600
            mySocket.SendTo(sendBytes, PLCendPoint)    
    2.Bind連結
           Dim PCendPoint As New IPEndPoint(IPAddress.Any, 0)
           Dim senderRemote As EndPoint = CType(PCendPoint, EndPoint)
           mySocket.Bind(senderRemote)   ---->放在這邊則OK
            mySocket.SendTo(sendBytes, PLCendPoint)
            mySocket.Bind(senderRemote)   ---->放在這會出現異常訊息:提供一個不正確的引數
    3.收碼
            Dim mybytes() As Byte = New [Byte](15) {}
            mySocket.ReceiveFrom(mybytes, PCendPoint) --->所收到的還是跟之前收到的byte相同(錯的回應碼)
            mySocket.ReceiveFrom(mybytes, PLCendPoint)--->改成這樣也是收到一樣那組byte (錯的回應碼)

    新手上路
    2009年8月26日 下午 01:19
  •    你完全亂了.
       本機位址:192.168.0.2 ,9600
       PLC位址:192.168.0.3  ,9600

        '先設定電腦的
       Dim myIpaddress As IPAddress = (IPAddress.Parse("192.168.0.2"))
       Dim myIpEndPoint As New IPEndPoint(myIpaddress, 9600)
       mySocket.Bind(myIpEndPoint)

       '設定PLC的
       Dim PLCIpAddress As IPAddress = (IPAddress.Parse("192.168.0.3"))
       Dim PLCIpEndPoint As New IPEndPoint(PLCIpAddress, 9600)
      
       'Send
        mySocket.SendTo(sendBytes, PLCendPoint)

        'Receive
        mySocket.ReceiveFrom(mybytes, PLCendPoint)

       
      
      
    請關心自己的問題,不要問了就放空;這是對別人與自己的尊重。如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    • 已標示為解答 eblue 2009年8月27日 上午 06:27
    2009年8月26日 下午 01:26
    版主
  • 還真的是讓我很亂..試了好多次..發現問題在於我搞亂了該連哪個.還有一個就是..Public udpClient As New UdpClient(9600)宣告完,我在load事件中做了udpClient.Connect("192.168.0.3", 9600)這件事,讓收回來的值變得很怪...拿掉就OK了..
    這樣就回到原始的位置了!避免網路沒有回應造成的成是死當
    在收碼前加上一個判斷式,我試了2個做法:
    1.
    If mySocket.Connected = True
    收碼程式
    End if 
    2.
    If mySocket.Available > 0 Then
    收碼程式
    End if 

    第1種做法 失敗!!不知為何它會是一直是false

    第2種做法 前面加一個延遲時間System.Threading.Thread.Sleep(100) 這樣就成功做出避免網路斷線或是沒有回應造成的死當
    第2種做法我將它套用於UDPCilent的舊程式中,也是可行的..不過大大既然說UDP的通訊是無法解決斷線的問題
    那麼表示這個做法!前人用過有它的風險在
    請問大大還有其他比較可靠的做法嗎??
    新手上路
    2009年8月27日 上午 12:50
  • eblue:
       通常使用Udp的專案是這樣控制的.
       1. 我習於用Socket 類別寫
       2. 我會搭配 receiveTimeout 和 SendTimeout 去判斷在時間內是否有完成通訊的程序
       3. 一般的設備回傳碼通常會帶有啟始碼, 長度碼與檢查碼, 我會程序中建立檢查這三樣的判讀式, 藉以判斷回傳的碼是否正常
       4. 至於你第一個做法會有問題, 因為你可能誤解這個屬性的意思, 這個屬性是個取得值, 並且指出的是上一次.
           MSDN這麼說的:取得值,指出上一個 SendReceive 作業是否將 Socket 連接至遠端主機。      
       

    Connected 屬性會取得最近一次 I/O 作業的 Socket 連接狀態。傳回 false 時,即表示 Socket 不是從未連接過,就是不再連接了。

    Connected 屬性的值會反映最近一次作業的連接狀態。如果您需要判斷連接的目前狀態,請執行非封鎖、零位元組的 Send 呼叫。如果該呼叫成功傳回或擲回 WAEWOULDBLOCK 錯誤碼 (10035),則表示通訊端仍在連接中,否則,就表示通訊端已不再連接。

    如果您在使用者資料包通訊協定 (User Datagram Protocol,UDP) 通訊端上呼叫 Connect ,則 Connected 屬性永遠會傳回 true ,不過,這個動作不會變更 UDP 的固有無連線性質。

     意思就是說, 這個屬性對於UDP是沒有用的

    5.你加sleep是正確的, 因為這種設備不像電腦能夠快速的處理與回傳, 我遇過有些複雜的命令甚至要等待1.5秒.


    請關心自己的問題,不要問了就放空;這是對別人與自己的尊重。如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    2009年8月27日 上午 01:08
    版主
  • 成功了..這段程式碼現在看來真的很簡單~~不過讓我轉了很久..接下來我試著用執行緒去接資料不過卻是空空的收不到資料!這是我的方式!
       Private Class NET
            Public mySocket As New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
            Dim myIpaddress As IPAddress = (IPAddress.Parse("192.168.0.2"))
            Public myIpEndPoint As New IPEndPoint(myIpaddress, 9600)
            Dim PLCIpAddress As IPAddress = (IPAddress.Parse("192.168.0.3"))
            Public PLCIpEndPoint As New IPEndPoint(PLCIpAddress, 9600)
        End Class

    發送資料:
            Dim myObj As New NET
            -發送資料的byte運算-
            myObj.mySocket.SendTo(sendBytes, myObj.PLCIpEndPoint)
            System.Threading.Thread.Sleep(100)
            FactorialThread = New System.Threading.Thread(AddressOf REC)
            FactorialThread.Start()
    接收資料:
     Private Sub REC()
            Dim readerstr As String
            Dim myObj As New NET
            myObj.mySocket.Bind(myObj.myIpEndPoint)
            myObj.mySocket.ReceiveTimeout = 1000
            Try
                Dim mybytes() As Byte = New [Byte](17) {}
                myObj.mySocket.ReceiveFrom(mybytes, myObj.PLCIpEndPoint)
                      - byte的運算-
                 Catch ex As Exception
                Label4.Text = "連線失敗"
            End Try
           myobj.mySocket.Close()
        End Sub

    執行的結果為:ReceiveTimeout
    這麼做不可以嗎??
    順道一提..昨天大大介紹的那個軟體還真不錯用!感謝 

    用監視軟體看資料發送的情況
    單一執行緒:PC PORT:9600  PLC PORT:9600 (收發正常)
    以多執行緒方式:PC PORT="隨機" PLC PORT:9600 (收碼異常)
    新手上路
    2009年8月27日 上午 02:21
  • 你要用執行緒做同步Socket的話, 最好把收送都放在一個執行緒中. 否則就要考慮Socket執行個體傳遞的狀況.
    你可以看一下我部落格上的[回呼的秘密花園《State Object》 ]
    多緒的狀態下, 有幾個問題是要注意的
    1. 使用全域物件, 有可能在執行緒未結束前, 全域物件的內容就被改變. 導致到後面整個全域物件已經不知道是什麼東西了.
    2. 沒有正確傳遞物件參考或是值, 導致其實不同執行緒中應該要一致的卻沒有一致. (你的問題可能就是這個)
    3. Bind這個方法最好是放在SendTo or ReceviceFrom的前方, 我發現你很喜歡把他放在兩個的中間.
    請關心自己的問題,不要問了就放空;這是對別人與自己的尊重。如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    2009年8月27日 上午 03:15
    版主
  • 從socket建立到丟出執行緒!目前進入測試階段了~~多謝大大的幫忙


    新手上路
    2009年8月27日 上午 06:30
  •  ReDim sendBytes(SXD.Length \ 2 - 1)

    請教一下各位上面這段

    ReDim sendBytes(SXD.Length \ 2 - 1) 這個除是除嗎 在vb中這個反方向是代表什麼意思?? 

    正常不都是 (SXD.Length / 2 - 1) 這樣嗎??

     

     

    2011年7月20日 上午 03:09
  • 原來這是整數除法 ~ 不過這好像在C# 沒見過 ~ 

     

    http://msdn.microsoft.com/zh-tw/library/b6ex274z(v=vs.80).aspx#Y637

     

    恩恩........

    2011年7月20日 上午 03:24