欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

使用Net.Sockets.TcpListener和Net.Sockets.TcpClient进行图片传输时如何精确控制接收缓存数组大小

程序员文章站 2023-01-29 16:52:10
在dotnet平台net.sockets.tcplistener和net.sockets.tcpclient已经为我们封装了所有socket关于tcp部分,操作也更为简单,面向数据流。使用tcpc...

在dotnet平台net.sockets.tcplistener和net.sockets.tcpclient已经为我们封装了所有socket关于tcp部分,操作也更为简单,面向数据流。使用tcpclient的getstream方法获取数据流后可以方便的对数据流进行读写操作,就如同本地磁盘的文件读写一样,使得程序员在设计程序时更为便捷简单。

但如果你使用过这两个对象进行数据传输的时候,你会发现问题也随之而来——getstream获取的数据流是一个永无止境的stream,你无法获取它的具体长度。

来看一下微软msdn关于这两个对象的例程:

shared sub connect(server as [string], message as [string])
   try
      ' create a tcpclient.
      ' note, for this client to work you need to have a tcpserver 
      ' connected to the same address as specified by the server, port
      ' combination.
      dim port as int32 = 13000
      dim client as new tcpclient(server, port)

      ' translate the passed message into ascii and store it as a byte array.
      dim data as [byte]() = system.text.encoding.ascii.getbytes(message)

      ' get a client stream for reading and writing.
      '  stream stream = client.getstream();
      dim stream as networkstream = client.getstream()

      ' send the message to the connected tcpserver. 
      stream.write(data, 0, data.length)

      console.writeline("sent: {0}", message)

      ' receive the tcpserver.response.
      ' buffer to store the response bytes.
      data = new [byte](256) {}

      ' string to store the response ascii representation.
      dim responsedata as [string] = [string].empty

      ' read the first batch of the tcpserver response bytes.
      dim bytes as int32 = stream.read(data, 0, data.length)
      responsedata = system.text.encoding.ascii.getstring(data, 0, bytes)
      console.writeline("received: {0}", responsedata)

      ' close everything.
      stream.close()
      client.close()
   catch e as argumentnullexception
      console.writeline("argumentnullexception: {0}", e)
   catch e as socketexception
      console.writeline("socketexception: {0}", e)
   end try

   console.writeline(controlchars.cr + " press enter to continue...")
   console.read()
end sub 'connect
你不得不去指定一个固定尺寸的缓冲区来接收数据,如果实际发送的数据超出了这个长度,你可能无法接收到全部完整的数据,而如果发送的数据少于缓冲区的大小,那么很显然你的内存会比别人消耗的更快。更为严重的时,如果你要发送一副图片,图片容量可能根据内容的不同而各有千秋,容量也不止256byte这么小,该如何精确控制缓冲区呢?


其实我们可以很容易的解决这个问题,就像很多文件格式所做的事情一样,我们可以在stream的前几个字节写入实际有效数据的长度,然后根据这个长度来分配内存,再读取内容,我所做的客户端与服务器之间传递屏幕的源代码如下:

使用Net.Sockets.TcpListener和Net.Sockets.TcpClient进行图片传输时如何精确控制接收缓存数组大小

"----------------客户端----------------

public class form1


    private sub timer1_tick(byval sender as system.object, byval e as system.eventargs) handles timer1.tick
        dim tcpc as new net.sockets.tcpclient
        dim slens(7) as byte
        try
            tcpc.connect("127.0.0.1", 2099)
            if tcpc.connected then
                for i = 0 to 7
                    slens(i) = tcpc.getstream.readbyte
                next

                dim buf(bitconverter.touint64(slens, 0)) as byte
                me.text = buf.length
                tcpc.getstream.read(buf, 0, buf.length)
                dim mem as new io.memorystream(buf)
                dim bmp as new bitmap(mem)
                me.picturebox1.image = bmp
                tcpc.close()
            end if
        catch ex as exception

        finally
            tcpc.close()
        end try
    end sub

    private sub form1_load(byval sender as system.object, byval e as system.eventargs) handles mybase.load

    end sub
end class





'------------服务器----------------

public class form1

    private sub backgroundworker1_dowork(byval sender as system.object, byval e as system.componentmodel.doworkeventargs) handles backgroundworker1.dowork
        dim tcps as new net.sockets.tcplistener(2099)
        tcps.start()
        while true
            dim slen as uint64
            dim slens(7) as byte
            dim tcpc as net.sockets.tcpclient
            tcpc = tcps.accepttcpclient

            '创建图片
            dim bmp as new bitmap(screen.primaryscreen.bounds.width, screen.primaryscreen.bounds.height)
            bmp.setresolution(1, 1)



            dim gph as graphics = graphics.fromimage(bmp)
            gph.copyfromscreen(new point(0, 0), new point(0, 0), bmp.size)
            gph.flush()

            '存入内存
            dim mem as new io.memorystream
            bmp.save(mem, drawing.imaging.imageformat.tiff)


            '文件长度
            slen = mem.length
            slens = bitconverter.getbytes(slen)

            '发送长度
            for i = 0 to 7
                tcpc.getstream.writebyte(slens(i))
            next
            '发送内容
            tcpc.getstream.write(mem.toarray, 0, mem.length)
            tcpc.close()
        end while
    end sub

    private sub form1_load(byval sender as system.object, byval e as system.eventargs) handles mybase.load
        me.backgroundworker1.runworkerasync()
    end sub
end class