烦人的同步问题!!我没招了~~急急急!!!!(50分)

L

logpie

Unregistered / Unconfirmed
GUEST, unregistred user!
我的CLIENT发送代码:
procedure TForm1.Button2Click(Sender: TObject);
var i,x,y:integer;
str:string;
l1:pBytearray;
begin
i:=50;
l1:=image1.Picture.Bitmap.ScanLine ;
wsk.Socket.SendBuf(i,sizeof(integer));
wsk.Socket.SendBuf(l1^,image1.Picture.Bitmap.Width );
end;
-------------------
我的SERVER接收代码:
procedure TForm1.wskClientRead(Sender: TObject; Socket: TCustomWinSocket);
var i,x,y:integer;
str:string;
l1,l2:pByteArray;
begin
getmem(l1,image1.Picture.Bitmap.Width);
socket.ReceiveBuf(i,sizeof(integer));
socket.ReceiveBuf(l1^,image1.Picture.Bitmap.Width);
l2:=image1.Picture.Bitmap.ScanLine;
move(l1^,l2^,image1.Picture.Bitmap.Width);
freemem(l1);
end;
---------------------------------------------
调试时,发现SERVER接受到的i不是CLIENT发来的50
但气人的是有时候接受到的又是50,且SERVER接受到的SCANLINE数据也正确(只有偶然几次)
我把SOCKET设为BLOCKING也不行,发送时用SLEEP延时也不行,把发送i和l1的顺序颠倒也不行
我真的是没招了~~

哪位大哥能把我的程序该一该让我SERVER的i和l1都正确能接受到应有的数据~
谢谢谢谢了!!!!!!!!!
 
不至于吧,连个回复的都没有,
帮你顶一下吧
 
把原码发给我,我帮你一下吧!
我的Email:feifan731@163.net
 
最好是一问一答
 
Socket发送的是数据流,
而不是数据包,
最好是打包,并在数据流中插入分界符,
收到一个完整的数据包时,再读取包中的数据
 
to LiChaoHui:
能给出代码吗?
 
TO 张无忌:
我给出的是测试代码,整个程序是不能用一问一答来发送的,因为i是在一个循环中
不断的送出,而CLIENT又要同步更新i和L1
我到LAN上做过测试,行不通的.
 
你可以做个缓冲区队列,存放接收到的数据,然后在每个数据包中加入分隔字符作为包界限,
每次从队列头指针向前扫描一个包分隔字符,读取一个数据包。
 
Server的i和l1是两个不同类型的变量,怎么一起发出去?SERVER又怎么分别读出i,l1?
能否给出具体代码?谢谢!
 
i要取地址发送吧?
 
socket.ReceiveBuf(@i,sizeof(integer));
 
> 整个程序是不能用一问一答来发送的,因为i是在一个循环中
> 不断的送出,而CLIENT又要同步更新i和L1
?????
 
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1557107
也许有点参考价值
 
用非阻塞!

3. Socket
首先,客户socket 必须描述希望连接的服务socket。客户socket查找服务socket,当定位服务时请求连接。服务socket不一定马上完成连接。服务socket维护一个客户请求队列,当有时间时完成连接。当服务socket 接受客户连接时,向客户socket 改善完整的服务socket 的描述,然后由客户完成连接。
服务sockets不定位客户。相反,被动地侦听客户请求。服务socket的侦听连接和一个队列相关。队列记录了进入的客户连接请求。当服务接受请求时,形成一个新的socket 来连接客户端,这样侦听连接可以继续接受其它客户请求。
当侦听接受客户请求时,由服务socket形成连接。当客户收到描述信息并完成连接时才建立连接。
地址包括“系统、类型和端口”。一个完整的socket连接包括连接两端的地址。IP/主机名、端口。连接前一般不需要指定客户的地址。
必须提供的信息依赖socket 的类型。客户socket必须描述要连接的服务。侦听服务socket必须描述提供服务的端口。
主机可以用IP描述,如123.197.1.2,一个系统可能包含多个IP地址。IP比较难记,可以使用主机名,如http://www.wSite.Com
大多数企业网提供“运行于internet ”的主机名。在95/NT 机上,如果,没有主机名,可以在HOSTS文件中建立。服务socket不需要指定主机。本地IP地址可以从系统中读取。如果有多个IP,服务sockets 会在所有地址上同时侦听。接受连接时,客户socket提供远程IP地址。
客户socket必须通过host name或IP 指定远程主机。
大多数应用使用主机名。主机名易记,另外服务器可以发迹系统或相关的IP地址。如果主机名未知,客户socket必须用IP 。
IP地址提供了查找主机的足够信息,还需要指定端口号。端口号是使一个系统维护多个连接的唯一标识。服务端指定端口以便客户端查找,客户端无须指定端口。
设置客户socket
指定主机:Host/Address,两个都指定时使用Host;
指定端口:直接设置 port,或简接地通过设置Service,若两个都指定将使用Service;
设置好后可以在运行时执行Open 方法来形成连接,也可在设计时设置Active:=true使程序一执行就形成连接。
完成连接后,可以使用客户Windows socket 对象来获得连接的信息。使用Socket 属性来获得客户Windows socket 对象。这个Windows socket 对象有属性,让你得到连接两端的地址和端口。SocketHandle 属性获得连接句柄,可用于 Windows socket API 调用。Handle 属性可用来访问从socket 连接接受消息的窗口。ASyncStyles 属性用来判断接收消息的类型。
通过socket连接完成通信后,可通过调用Close 方法关闭连接。连接也可以从服务端关闭,这样会产生OnDisconnect 事件。
设置服务Socket
必须设置端口:直接用port ,简接用 Service;
进入侦听:Open/Active:=true
接受请求后触发OnClientConnect 事件。
Socket 属性表示服务Windows socket 对象,它包含一些属性,可用来查找接受的活动连接。SocketHandle 属性获得句柄,Handle 可用来访问窗口。
每个活动连接是由server client Windows socket object (TServerClientWinSocket)封装的,可通过服务Windows socket 对象的Connections属性来访问。这样服务客户Windows socket 对象有一些属性,用来获得连接两端的地址和端口。
调用Close 关闭侦听连接。这样关闭所有已打开的连接,取消所有未接受的连接,并关闭侦听连接,所以不会接受任何新的连接。
当客户关闭单独的连接时,会产生OnClientDisconnect 事件。
错误事件:客户会产生OnError 事件,服务会产生OnClientError事件。在错误处理事件中可以设置错误号为0以阻止产生异常。
客户事件:
1 OnLookup 在定位服务前产生,这里,不可以改变Host, Address, Port或Service 属性。可使用Socket 属性访问客户Windows socket对象,使用SocketHandle属性来调用影响客户属性的API 。例如,若想设置客户应用的端口号。
2 设置并初始化Windows socket事件。
3 定位主机后产生OnConnecting 事件。这时Socket 属性可提供服务socket的属性。这是第一次获得用于连接的实际端口和IP的机会。这会和侦听连接的socket 的port和IP不同。
4 连接由服务接受并由客户完成。
5 建立连接后产生OnConnect 事件,如果通过连接马上进行读写,写这个事件。
服务事件:
服务socket组件形成两种连接:侦听连接和应用连接。
侦听连接事件:形成侦听连接前,出现OnListen 事件。这里通过Socket 属性获得Windows socket对象。使用SocketHandle 来改变开听前的 socket。如,限制侦听的IP地址。
应用连接事件:接收请求时发生以下事件。
1 OnGetSocket 事件,为形成服务连接端的socket传递Windows socket 句柄。如果想提供自己的 TserverClientWinSocket派生类,可以在OnGetSocket中建立,会用来替代TserverClientWinSocket。
2 OnAccept ,向事件传递新的TServerClientWinSocket object to the event handler。这里第一次可以使用TServerClientWinSocket 的属性获得服务端信息。
3 如果ServerType=stThreadBlocking ,则出现OnGetThread 事件。如果想提供自定义的TserverClientThread派生类,可以在OnGetThread 中建立,来替代TserverClientThread。
4 如果ServerType=stNonBlocking, 线程执行时发生OnThreadStart。可以进行线程的初始化,并开始读写前进行API调用。
5 客户完成连接生触发OnClientConnect 。在非阻塞服务中,应在这时开始读写。
建立非阻塞连接,设置客户:ClientType=ctNonBlocking,
服务:ServerType=stNonBlocking;
非阻塞会产生读写事件,客户OnRead / OnWrite,服务OnClientRead/OnClientWrite。
和连接有关的Windows socket 对象将作为读写的参数提供。这个Windows socket对象提供了读写方法。读使用ReceiveBuf / ReceiveText 。使用ReceiveBuf 前,使用ReceiveLength 来获得长度。使用SendBuf/SendStream/SendText 写,如果写后不再需要连接可使用SendStreamThenDrop 方法。SendStreamThenDrop在写完所有信息后关闭连接。如果使用SendStream / SendStreamThenDrop ,不要释放流对象。关闭连接时会自动释放。SendStreamThenDrop 将关闭单独的应用连接而不是侦听连接。

使用阻塞连接时,必须初始化读写,而不是被动地等待连接通知。当由连接端负责何时读写时使用阻塞。
客户设置ClientType=ctBlocking形成阻塞连接。基于客户应用,可能想建立一个新的读写线程。这样应用可以在其它线程中继续执行代码。而在这里会等待读写完成。
服务设置ServerType=stThreadBlocking ,因为阻塞连接会阻止其它代码的执行,服务socket组件总是为每个ServerType=stThreadBlocking的连接产生新的执行线程。
大多数使用阻塞连接的应用使用线程。即使不用线程,可以使用TwinSocketStream读写。
使用阻塞连接客户不会自动产生新的线程。如果客户应用除了读写无的事事,可以使用这种类型。但应用若包含必须响应的用户界面,你会想产生单独的线程进行读写。
当服务形成阻塞连接时,总是为每个客户连接产生单独的线程,所以没有客户必须等待其它客户完成读写。默认时,服务使用TServerClientThread 对象来实现线程执行。
TServerClientThread 对象模拟非阻塞连接中的OnClientRead/OnClientWrite事件。但是,这些事件出现在侦听socket上,而不是线程本地。如果客户请求频繁,你会建立自己的 TserverClientThread派生类来提供线程安全读写。
写客户或服务线程时,可使用TWinSocketStream 来进行实际的读写。
实现阻塞连接线程时,必须判定何时连接的另一端准备读写。阻塞连接不通知socket何时读写。查看连接是否准备好,使用TwinSocketStream对象。TWinSocketStream 提供了对应读写时间的方法。调用WaitForData 来等待另一端写。
使用TwinSocketStream读写,如果过了指定时间,读写尚未完成,流超时。结果是socket应用不会无休止地挂起来偿试读写。
非阻塞连接中不能使用TWinSocketStream 。
写客户线程:用向导定义线程对象。线程的Execute 方法处理线程连接的读写细节。建立一个TWinSocketStream 对象,用来读写。
procedure TMyClientThread.Execute;
var
TheStream: TWinSocketStream;
buffer: string;
begin
{ create a TWinSocketStream for reading and writing }
TheStream := TWinSocketStream.Create(ClientSocket1.Socket, 60000);
try
{ fetch and process commands until the connection or thread is terminated }
while (not Terminated) and (ClientSocket1.Active) do
begin
try
GetNextRequest(buffer); { GetNextRequest must be a thread-safe method }

{ write the request to the server }
TheStream.Write(buffer, Length(buffer) + 1);
{ continue the communication (e.g. read a response from the server) }
...
except
if not(ExceptObject is EAbort) then
Synchronize(HandleThreadException); { you must write HandleThreadException }
end;
end;
finally
TheStream.free;
end;
end;
使用线程,在OnConnect 中建立。

写服务线程:服务连接的线程是TserverClientThread的派生。因此,不能用线程向导。按如下方式声明:
TMyServerThread = class(TServerClientThread);
要实现这个线线程,重载ClientExecute 方法,而不是Execute,实现和Execute类似。但服务客户线程必须使用TserverClientWinSocket对象,是由侦听服务socket接受客户连接时建立的。是可用的公开属性ClientSocket 。可以使用保护方法HandleException ,这样不需要写自己的线程安全异常处理。
procedure TMyServerThread.ClientExecute;
var
Stream : TWinSocketStream;
Buffer : array[0 .. 9] of Char;
begin
{ make sure connection is active }
while (not Terminated) and ClientSocket.Connected do
begin
try
Stream := TWinSocketStream.Create(ClientSocket, 60000);
try
FillChar(Buffer, 10, 0); { initialize the buffer }
{ give the client 60 seconds to start writing }
if Stream.WaitForData(60000) then

begin
if Stream.Read(Buffer, 10) = 0 then { if can't read in 60 seconds }
ClientSocket.Close; { close the connection }
{ now process the request }
...
end
else
ClientSocket.Close; { if client doesn't start, close }
finally
Stream.Free;
end;
except
HandleException;
end;

end;
end;

服务socket缓存使用的线程,确保ClientExecute方法进行必要的初始化。使用线程,在OnGetThread 中建立,建立线程时,设置参数CreateSuspended= False。
 
TTmpSheet= Record
sId: integer;
sbuf: variant;
rbuf: variant;
rId: integer;
end;

内存映射方法。
我定义的只是一个假设,如果你觉得有必要还要做两者之间关系处理的话,那么就要定义成类。
然后写两者间的关系。
 
顶部