pb使用winsock控件来进行文件传送
请问哪位大侠有这个方面的资料或者是例子,请各位大侠不吝赐教!!
[解决办法]
直接传文件的没有,传blob 数据的有.
估计你得自己完善一下.
PB用的什么版本的?
[解决办法]
转载
一个用Winsock控件传送文件的VB程序写完了
blog出现在CSDN,也就blog将不blog也。你会问为什么吧?无论你心里有没有这个问号,但在我心里这是个句号!你又会问为什么吗?好,不管你问不问。我决定,现在作答。但那种长篇大论的前因后果,请恕我无法一一罗列。我只能直接而又间接地说明,blog出现在CSDN,也就blog将不blog也。
使用VB,写过网络程序,没试过Winsock这个控件实在是遗憾(API高手除外)。我想没有朋友有这种遗憾的...呵呵!因为,通过Winsock控件,你可以把网络通信简化简化再简化。那是什么程度?可能就是10行代码以内就行了那种(IDE生成的随外)!因为那只是网络通信,而通信,仅仅就发送一条信息,对方收到了,显示出来。可以算了吧?来看看:
首先,窗口加载过程,我们写上:
'设置了第一个Winsock控件进入等待
Winsock1.LocalPort = 5052
Winsock1.Listen
'再来把第二个Winsock控件连向第一个
Winsock2.Connect "127.0.0.1", 5052
好了,这时Winsock1控件的ConnectionRequest事件触发。我们写上:
If Winsock1.State <> sckClosed Then Winsock1.Close
Winsock1.Accept requestID '接受连接
就这样就连上啦!简单得很。再来:
Winsock1.SendData Text1.Text '把Text1中的文本传给对方
当然了,你传了数据给Winsock2,那它的DataArrival事件也触发了。
Dim strDat As String
Winsock2.GetData strDat '取得数据
Text2.Text = strDat '在Text2中显示出来
至此,一个使用Winsock控件,实现网络通信程序就完成了!哈哈哈,是不是觉得上当受骗了?这种点子,MSDN里早有出卖了!盖兹可真不是盖的,竟然早早想到用MSDN断我财路!唉.难怪他比我富!
我完成任务了?没有,我要作的是《使用Winsock控件,实现网络点对点通信》。通信实现了,也是一个点对另一个点。可是,在普遍认识里,点对点并不是这么解释的。怎么解?就是双向通信吧!呵呵,但盖兹始终比我富。这点子在MSDN里又已经出卖了!
不耐烦了吧?但要写程序,就不能怕烦。如果你决定走这条路,那还有更烦更烦的等着你。喂,烦人,你能看得下去吗?哈哈~!既然好点子全给盖兹拿去卖了,那还有什么好说的?就如,比尔卖你一个空的数据表,可是不填上数据。它还是一个空的数据表,而不是数据表!但比尔是不能帮你填上适合你的数据的,所以,你要照着你现有的数据表,向空的数据表填上数据,再把源数据表删了,那它就成了一个独一无二的数据表了!
我看你真的不耐烦了...你饿吗?把鼠标蒸熟吃了顶一会吧!
好,现在就让我说一说,点对点通信应用里的一个重要程序...但这次,不会再像上面那样,列出一行一行的代码,然后告诉你添加到那里,然后怎样...现在我要就“授人以渔”的政策,坚持“编程重思想”的方针;向“由浅入深”的路线,从“理解到认识”的哲学角度出发。解释一下,通过Winsock控件,在网络上传送文件的VB程序是怎么写的!
正如前面的例子一样,用Winsock控件通信,得分开客户端与服务器,即:Client、Server(C\S)。但大家都知道,这仅是使用TCP协议必需要上演的一幕。而在UDP里,并没有明确的把C\S分开!但这样概念要变模糊了。这在以后的话题里,我会再作解释。今天我们的主角是TCP。
上例中,那个循序渐进的步骤,大家体会到吗?没关系,现在来回顾一下。
服务器监听5052端口。
客端连接到服务器的5052端口,就是服务器程序监听的端口了!
由于有客户连接,服务器程序那的Winsock控件就触发ConnectionRequest事件(说明见MSDN)。
服务器接受了连接,客端程序的Winsock控件会触发Connect事件,以表示连接上(例省略)。
连接上后,由服务器程序向客户端程序发送数据。
数据到达客端,Winsock控件触发起客端程序的DataArrival事件提示,取得数据,显示!
整个流程就这样,显然很简单了,不对吗?呵呵!然后,我们得到这么一个流程表后。就要开始填空了。打开我们想发送的文件读取数据给服务器发送。对方收到数据后,再写回到文件!过程就这么个样了!具体实现起来,还得要思考思考。好,现在我们就从一个用户的角度出发去想吧!
两个Winsock怎么连上我就不说了,反正盖兹都已经比我富!那我们要传文件,先得让用户选择一个文件吧。然后就开始传!好传,慢着!客端怎么知道服务器要传的是什么类型的文件呢?好!有了这个想法,我们在用户点击服务器程序传送文件按扭的时候,获取刚才用户选择的文件的名字吧。要传过去了?还是不行,如果我们这个程序是聊天+传文件的话,那不是很混乱了?嗯,给这个信息一个名字吧!就叫:“Send”怎样?好,是个很有霸气的名字~大家用过QQ之类的聊天软件传文件都知道,QQ会先询问接收方是否接收XXXX.XXX的文件!那我们就模仿一下吧,把传送命令和文件名设置成:"Send 文件名",这样的一个格式。然后用Winsock的SendData方法传给对方。
那就去了客户端编写代码:
好,当收到这条命令,我们从前4个字得出,对方要传文件过来了!也没什么好写的,就弹个Msgbox出来说,对方要传XXXX.XXX给你,你愿意接收吗?好,当用户把保存路径确定下来了,然后在这样的路径里,创建一个这样的文件,以备接收到数据时写入。我们现在就得返回一条信息给服务器那,就叫:"ILiefly "吧!表示确认接收。
又转回来服务器那写代码了:
收到接收确认信息,我们开始进行读文件操作了!把文件打开,声明一个Byte数组。把打开的文件数据全读入到这个数组里。哈哈~不就成了吗?那传过去试试...
客户端开始接收到数据了:
嘿,糟糕,怎么会有数据类型错误呢?原来每当有数据到达客户端,我们在DataArrival事件写的代码都执行了。还以为是命令或聊天内容呢!不行,要给它设置一个标记,用作记录什么时候传过来的数据是文件的的数据吧。用一个Static的语句(见MSDN)声明一个静态布尔的变量,当False的时候是我们命令或聊天内容,而True的时候就是文件数据吧!再改一改上一次在客户端那写的源码,在答应接收之后,把这个布尔变量设为True!啊?问题又出来了!这时我想到,那什么时候才能把布尔变量设回False呢?倒,还是有问题!目光集中到服务器那里去...
再回到服务器的代码编辑框:
有什么办法让客户端知道什么时候传完呢?发送命令?不行,那里会把命令写入到文件的。连接关闭?更加不行,我还要聊天...嗯,要是让客户端知道传过去的文件长度,不就可以确定什么时候是接收完了吗?哈哈!行动,改一改传送文件的命令为:"Send 文件名 文件长度"。哦?太多空格啦,那样对方解释起来相当麻烦嘛,用InStr?不行,要是文件名里有空格怎办?不就又出错了...还好,我们有Split嘛(见MSDN)。好,那得把命令用个特别的字符分开了!就vbNullChar吧(见MSDN)!把命令改成:["SEND" & vbNullChar & 文件名 & vbNullChar & 文件长度]。传给对方。(注:FileLen函数)
客户端要修改代码了:
声明一个动态字串数组,把Split函数的返回值附给它,哈哈!下标0是命令,下标1是文件名,下标2是文件长度!爽了~把长度用模块域变量保存着,以备计算什么时候收完吧!(注:Val函数)
继续在客户端那写我们没完的代码:
布尔变量为True,有数据到达。那就取得它的数据吧!可要注意接收到的数据类型哦,对面传过来的不是字符串了,我们要指定类型为"vbArray + vbByte"(见MSND关于GetData的说明),意思就是字节数组啦。当然,我们要定义一个字节数组才能接收嘛 Dim ByteArray() As Byte。好,那刚才保存下来的长度就有用啦,总长度-当次收到的字节数=剩余字节数。当剩余字节数为0的时候,不就接收完了吗?哈哈!不急,先来组织一下思路:数据到达,取得,保存到字节数组。用UBound函数(见MSDN)得到字节数组的最大下标,但由于数组下标0也存着数据,但文件不是这么算啊,所以要算就得+1算!总长度 = 总长度 - UBound(ByteArray) + 1 嘿,行了!
赶快运行试试,找个50多MB的传。呵呵,突然,铛!“内存不足”晕!搞什么鬼啊,再看一下进程列表,哇!我的程序占用了60MB左右的内存!晕,什么回事啊?是不是那里出错了?再重组一下思路:选择文件,发送命令,当收到返回确认信息,打开文件,读...读???不会吧,我把文件整个读入内存了,难怪难怪(其实不会发生这个错误的,因为Windows有虚拟内存嘛!但的确,要把整个文件读入内存再发送出去,有点让系统难为啊)!得优化优化了。
回到服务器代码里进行优化:
不能一次读入这么多,那...分开来吧!一次传送:8192个字节如何?好!就这样吧。但不是每个文件的字节数都为8192的倍数,怎办呢?嗯,作个计算。之前,我们不是得到了要传的文件的长度?既然对方都可以计算什么时候收完,那我也可以计算什么时候传完嘛!首先,确定一下,要传的文件长度是不是比8192大。如果是,那我们就把文件长度-8192。然后定义个下标是8191的字节数组(因为数组下标0也可以存数据嘛,所以要-1),用Get方法,读取打开的文件数据来填充这个字节数组。然后传过去...嗯?对方好像会收了啊,未达到文件总长度时,客户端不会把布尔数组设回来的。但,这边怎么知道什么时候可以传下一笔数据呢?嗯~SendComplete 事件——在完成一个发送操作时出现。好,就是这个了!我们在这边也用文件长度为0时作为结束传送的条件。如果不为0,那就再看看还没传的文件剩下多少,因为上面的计算已经-去了上次传送的字节了,那现在的文件长度变量就是剩下字节的数量啦,再比较一下,是否比8192大。哈哈~不是就定义一个这么大小的字节数组,读文件,送出!完成~[MsgBox "传输完毕!", vbInformation, "⊙_⌒γ - Server"]...
但突然发现,原来SendComplete事件,在我们把传文件命令发出去之后也会触发。那没法子了,只好再定义一个布尔变量来确定什么时候是传文件吧...
至此,一个用Winsock控件传送文件的VB程序写完了!呵呵,没睡着吧?不过我快睡着了!好困,上一次睡觉的时间只有3小时!现在-_-#了...但得说明一下,上面提到的8192,并不是我随便安上去的。这个在以后再作详细说明吧!
嗯,要做的事还没完!好像我没说为什么:“blog出现在CSDN,也就blog将不blog也”哈哈~其实这篇东东已经说得很清楚了!
[解决办法]
文章不错,有时间按着做一下
[解决办法]
http://download.csdn.net/source/499124
使用WinSock.pbl进行通讯的源代码
[解决办法]
pslib21.dll + winsock.pbl 传的就是 blob 数据.
[解决办法]
传送文件,或比较大量的数据,或都通讯过程中需要适应多种指令的,都需要制定一个通讯协议。
一般来说,通讯协议包括消息头和消息体,消息头通常为固定长度,消息体不定长。查查关于通讯协议方面的资料就了解了。
消息头和消息体,简单点来说就是消息头包含了指令,消息体是该指令的参数。
[解决办法]
To:cxlzl2005
WINSOCK.PBL可以传送二进制的文件.简单思路如下:
Client:FileOpen>>FileRead>>sent(blob)>>FileClose
Server:recv(blob)>>FileWrite>>FileClose
To:lzp_lrp
PowerBuilder版能不能把比较懂VC++的热心朋友组织一下?
PowerBuilder直接调用API有时会有困难,简单封装一下就能解决问题。比如上面winsocket recv api
Function integer recv (int socket, ref blob buf, int len, int flags) Library "wsock32.dll" alias for "recv;Ansi"
由于接收时PB要预先分配内存space(len),但是winsocket api并没有方法获取buf的len,如果把winsocket api简单封装,提供一个方法获取buf的len,如GetDataLen(int socket).那么PB就能更好地调用API.
[解决办法]
pslib21.dll 是封装了 winsock2.0 ,但也没有方法获取buf的len.
实际上可以直接调用sockets api
forward
global type n_cst_wsock32 from nonvisualobject
end type
end forward
global type n_cst_wsock32 from nonvisualobject
end type
global n_cst_wsock32 n_cst_wsock32
type prototypes
Function uint accept (uint s,str_sockaddr addr, ref int addrlen) Library "wsock32.dll"
Function integer bind (uint s, str_sockaddr name, int namelen) Library "wsock32.dll"
Function integer closesocket (uint socket) Library "wsock32.dll"
Function integer getsockname (uint s,ref str_sockaddr name, ref int namelen) Library "wsock32.dll"
Function integer getsockopt (uint socket, int level, int optname,str_linger optval, int optlen) Library "wsock32.dll" alias for "getsockopt;Ansi"
Function integer htons (int hostshort) Library "wsock32.dll"
Function integer ntohs (int netshort) Library "wsock32.dll"
Function integer listen (uint s, int backlog) Library "wsock32.dll"
Function integer shutdown (uint s, int how) Library "wsock32.dll"
Function integer send (int socket, ref blob buf, int len, int flags) Library "wsock32.dll" alias for "send;Ansi"
Function integer setsockopt (uint socket, int level, int optname,str_linger optval, int optlen) Library "wsock32.dll" alias for "setsockopt;Ansi"
Function uint socket (int af, int ttype, int protocol) Library "wsock32.dll"
Function integer recv (int socket, ref blob buf, int len, int flags) Library "wsock32.dll" alias for "recv;Ansi"
Function integer wsconnect (uint socket, str_sockaddr name, int namelen) Library "wsock32.dll" alias for "connect"
Function integer WSACleanup () Library "wsock32.dll"
Function integer WSAAsyncSelect (uint socket, uint Wnd, uint wMsg, long lEvent) Library "wsock32.dll"
Function integer WSACancelBlockingCall () Library "wsock32.dll"
Function integer WSAGetLastError () Library "wsock32.dll"
Function integer WSAStartup (uint wVersionRequested, ref str_wsadata lpWSAData) Library "wsock32.dll" alias for "WSAStartup;Ansi"
Function uint gethostname (ref String szHost, long dwHostLen) Library "wsock32.dll" alias for "gethostname;Ansi"
Function ulong gethostbyname (String szHost) Library "wsock32.dll" alias for "gethostbyname;Ansi"
Subroutine CopyMemoryIP (ref str_hostent Destination , ulong Source, long Length) Library "KERNEL32.DLL" Alias for RtlMoveMemory
Subroutine CopyMemoryIP (ref blob Destination , ulong Source, long Length) Library "KERNEL32.DLL" Alias for RtlMoveMemory
Subroutine CopyMemoryIP (ref character Destination[4] , ulong Source, long Length) Library "KERNEL32.DLL" Alias for RtlMoveMemory
Subroutine CopyMemoryIP (ref ulong Destination , ulong Source, long Length) Library "KERNEL32.DLL" Alias for RtlMoveMemory
Function ulong LocalFree(ulong MemHandle) library "kernel32"
Function ulong LocalFree(ref str_hostent MemHandle) library "kernel32"
function long CopyBlobGet(long szBuf1, ref blob szBuf2, long nDataLen) library "memcopy.dll" alias for "CopyBlobGet;Ansi"
function long CopyBlobSet(ref blob szBuf1, long szBuf2, long nDataLen) library "memcopy.dll" alias for "CopyBlobSet;Ansi"
FUNCTION Long Getsockopt ( Long s, Long level, Long optname, REF String optval, Long optlen) LIBRARY "ws2_32.dll"
//FUNCTION Long recv ( Long s, String buf, Long lLen, Long flags) LIBRARY "ws2_32.dll"
end prototypes
type variables
public constant int MAXGETSOCKADDRSTRUCT = 16
public constant int WM_USER = 1024
public constant int FD_READ = 1
public constant int FD_OOB = 4
public constant int FD_ACCEPT = 8
public constant int FD_CONNECT = 16
public constant int FD_CLOSE = 32
public constant int SOCK_ERROR = -1
public constant int AF_INET = 2// For UDP, TCP etc
public constant int AF_IPX = 6// For IPX/SX
public constant int SOCK_STREAM = 1 /* stream socket */
public constant int SOCK_DGRAM = 2 /* datagram socket */
public constant int SOCK_RAW = 3 /* raw-protocol interface */
public constant int SOCK_RDM = 4 /* reliably-delivered message */
public constant int SOCK_SEQPACKET = 5 /* sequenced packet stream */
str_wsadatai_WSAData
boolean ib_initialized
end variables
forward prototypes
public function integer uf_initialize ()
public function integer uf_geterror ()
public function string uf_getwinsockversion ()
public function string uf_gethostname ()
public subroutine uf_closesocket (integer asocket)
public function string uf_getipfromblob (blob lb_hostaddress)
public function integer uf_startup (string ahostname, unsignedinteger aport, unsignedlong ahandle, integer aevent)
public function integer uf_connect (string ahostname, integer aport)
public function integer uf_senddata (unsignedinteger asocket, blob ab_data)
public function integer uf_accept (integer asocket, ref str_sockaddr aclient)
public function blob uf_getrequestdata (integer asocket)
public function long of_getdata (long al_data, blob ablb_data, long al_datalen)
public function long of_setdata (long al_data, ref blob ablb_data, long al_datalen)
public function integer of_recv (integer ai_socket, ref blob ablb_data, ref integer ai_datalen)
public function integer uf_initialize ()
public function integer uf_geterror ()
public function string uf_getwinsockversion ()
public function string uf_gethostname ()
public subroutine uf_closesocket (integer asocket)
public function string uf_getipfromblob (blob lb_hostaddress)
public function integer uf_startup (string ahostname, unsignedinteger aport, unsignedlong ahandle, integer aevent)
public function integer uf_connect (string ahostname, integer aport)
public function integer uf_senddata (unsignedinteger asocket, blob ab_data)
public function integer uf_accept (integer asocket, ref str_sockaddr aclient)
public function blob uf_getrequestdata (integer asocket)
public function long of_getdata (long al_data, blob ablb_data, long al_datalen)
public function long of_setdata (long al_data, ref blob ablb_data, long al_datalen)
public function integer of_recv (integer ai_socket, ref blob ablb_data, ref integer ai_datalen)
end prototypes
public function integer uf_initialize ();
if not ib_initialized then
intli_version
//Initialize Winsock Version variables
li_Version = 257
//Initialize Winsock Library...WSAStartup needs to be called first time
IF wsastartup ( li_version, i_WSAData ) = 0 THEN
ib_initialized = true
return 0
End if
return SOCK_ERROR
end if
return 0
end function
public function integer uf_geterror ();
return WSAGetLastError()
end function
public function string uf_getwinsockversion ();
/* the wsadata structure contains several information.
The description element tells us the winsock version */
if not ib_initialized then
if uf_initialize() = SOCK_ERROR then
return string(SOCK_ERROR)
end if
end if
return i_WSAData.description
end function
public function string uf_gethostname ();
boolean lb_error
lb_error = false
if not ib_initialized then
if uf_initialize() = SOCK_ERROR then
lb_error = true
end if
end if
if not lb_error then
string ls_Hostname
//initialize the hostname variable
ls_Hostname = space(128)
/* Now, let's find out what the hostname is of the current machine
we're working on */
IF gethostname ( ls_HostName, len(ls_HostName) ) < 0 THEN
/* Oops an Error occured. Get ErrorText from the Winsock Library */
lb_error = true
ELSE
return ls_HostName
End if
end if
If lb_error then
messagebox("uf_gethostname",uf_geterror())
return string(SOCK_ERROR)
end if
end function