0 sockets delphi multithreading android delphi-xe5
当我关闭与某个客户端的连接时,会发生此错误.错误发生在服务器的此代码行中:
Received := SocketStrm.Read(Data, SizeOf(Data));
Run Code Online (Sandbox Code Playgroud)
而且,当智能手机重新启动时(或者,例如,当我关闭客户端应用程序时),丢失的客户端的数据不会从ListView服务器应用程序中删除.
有人可以帮我解决这2个错误吗?
以下是我发送数据的代码:
客户端(Android):
public class MainActivity extends AppCompatActivity {
private Socket xclientSocket;
class ClientThread implements Runnable {
@Override
public void run() {
try {
InetAddress serverAddr = InetAddress.getByName("192.168.15.12");
xclientSocket = new Socket(serverAddr, 101);
new Thread(new CMDThread()).start();
} catch (Exception e1) {
System.out.println(e1.toString());
}
}
}
class CMDThread implements Runnable {
@Override
public void run() {
try {
while(xclientSocket.isConnected()){
BufferedReader xreader = new BufferedReader(new InputStreamReader(xclientSocket.getInputStream()));
DataOutputStream dOut = new DataOutputStream(xclientSocket.getOutputStream());
String xline;
if (xreader.ready()) {
while ((xline = xreader.readLine()) != null) {
System.out.println(xline);
if (xline != null && !xline.trim().isEmpty()) {
if (xline.equalsIgnoreCase("info")) {
DataOutputStream dOut = new DataOutputStream(xclientSocket.getOutputStream());
dOut.writeChars("<|data|>" + myDeviceProduct.toUpperCase() + "<|>" + myDeviceModel + "<|>" + myVersion + "<|>" + SIM_OPNAME + "<|>" + SIM_NUMBER + "<<|");
dOut.flush();
}
if (xline.equalsIgnoreCase("disconnect-client")) {
break;
}
}
}
}
}
System.out.println("Shutting down Socket!!");
xclientSocket.close();
}
catch (Exception e1) {
System.out.println(e1.toString());
}
}
}
///////////////////////// USAGE /////////////////////////////
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new ClientThread()).start();
}
}
Run Code Online (Sandbox Code Playgroud)
服务器(Delphi):
type
TCMDSock_Thread = class(TServerClientThread)
private
function ArrayToString(const a: array of Char): string;
procedure addClientToListView;
protected
procedure ClientExecute; override;
end;
var
FMain: TFMain;
Manufacturer, Model, OsVersion, SIMOpName, SIMNumber: string;
implementation
{$R *.dfm}
//==============================================================================================================
function TCMDSock_Thread.ArrayToString(const a: array of Char): string;
begin
if Length(a)>0 then
SetString(Result, PChar(@a[0]), Length(a))
else
Result := '';
end;
procedure TCMDSock_Thread.addClientToListView;
var
Item: TListItem;
begin
Item := FMain.ListView1.Items.Add;
Item.Caption := IntToStr(ClientSocket.Handle);
Item.SubItems.Add(ClientSocket.RemoteAddress);
Item.SubItems.Add(ClientSocket.RemoteHost);
Item.SubItems.Add(Manufacturer +' - '+Model+' - '+OsVersion);
Item.SubItems.Add(SIMOpName+' - '+SIMNumber);
Item.Data := ClientSocket.Data;
FMain.ServerStatus.Panels.Items[1].Text := 'Connected';
end;
procedure TCMDSock_Thread.ClientExecute;
var
Data: array [0 .. 1023] Of Char;
SocketStrm: TWinSocketStream;
ReceivedText: string;
Received: Integer;
begin
ClientSocket.SendText('info' + #13#10);
SocketStrm := TWinSocketStream.Create(ClientSocket, 5000);
try
while ClientSocket.Connected do
begin
if not SocketStrm.WaitForData(100) then
Continue;
FillChar(Data, SizeOf(Data), #0);
SocketStrm.Read(Data, SizeOf(Data));
repeat
try
Received := SocketStrm.Read(Data, SizeOf(Data));
except
Break;
end;
until Received = 0;
ReceivedText := ArrayToString(Data);
if Pos('<|data|>', RecText) > 0 then
begin
Delete(ReceivedText, 1, Pos('<|data|>', ReceivedText) + 7);
Manufacturer := Copy(ReceivedText, 1, Pos('<|>', ReceivedText) - 1);
Delete(ReceivedText, 1, Pos('<|>', ReceivedText) + 2);
Model := Copy(ReceivedText, 1, Pos('<|>', ReceivedText) - 1);
Delete(ReceivedText, 1, Pos('<|>', ReceivedText) + 2);
OsVersion := Copy(ReceivedText, 1, Pos('<|>', ReceivedText) - 1);
Delete(ReceivedText, 1, Pos('<|>', ReceivedText) + 2);
SIMOpName := Copy(ReceivedText, 1, Pos('<|>', ReceivedText) - 1);
Delete(ReceivedText, 1, Pos('<|>', ReceivedText) + 2);
SIMNumber := Copy(ReceivedText, 1, Pos('<<|', ReceivedText) - 1);
Synchronize(addClientToListView);
end;
end;
finally
SocketStrm.Free;
end;
end;
//================================================================================================================
procedure TFMain.ServerSocket1GetThread(Sender: TObject;
ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread);
begin
SocketThread := TCMDSock_Thread.Create(False, ClientSocket);
end;
procedure TFMain.Button2Click(Sender: TObject); // <= PopupMenu
var
Index: Integer;
begin
Index := ListView1.Selected.Index;
if Index = -1 then
Exit;
ServerSocket1.Socket.Connections[Index].SendText('disconnect-client' + #13#10);
ServerSocket1.Active := False;
end;
procedure TFMain.ServerSocket1ClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
var
Item: TListItem;
begin
Item := FMain.ListView1.FindCaption(0, IntToStr(Socket.Handle), False, True, False);
if Item <> nil then
Item.Delete;
FMain.StatusBar1.Panels.Items[1].Text := 'Desconnected';
end;
Run Code Online (Sandbox Code Playgroud)
Rem*_*eau 13
如果在ClientSocket释放TWinSocketStream使用它的套接字句柄之前关闭了" 句柄无效"错误.例如,当您停用服务器时会发生这种情况,因为它会循环关闭其套接字的活动线程的内部列表.这是正常行为.只需忽略线程中的错误并退出ClientExecute().
话虽这么说,你没有TWinSocketStream.Read()正确使用.在这种情况下,您需要调用Read()一个循环(在循环错误之前进行第一次调用,将其删除),将接收到的数据附加到不断增长的缓冲区,并在处理之前扫描缓冲区以获取完整的消息.重复直到断开连接
Received在客户端断开连接之前,不会将其设置为0,但是客户端会设置为(可能)在断开连接之前向服务器发送许多消息.你没有考虑到这种可能性,所以在断开连接之前你不应该无休止地阅读.当您收到完整的消息,处理它,然后返回阅读时停止阅读.
至于你的ListView,正如我之前已经告诉你的那样,OnClientDisconnect事件不是在线程阻塞模式下触发的,所以你需要在ClientExecute()退出之前删除ListView项.就此而言,由于客户端设置为(可能)发送许多消息,因此您的代码会将每个消息添加到ListView,因此您需要确保删除所有消息,而不仅仅是找到的第一个消息.否则,请确保每个客户端不添加多个ListView项.
此外,您的服务器可以(可能)接受多个客户端,但您假设一次只连接一个客户端.您的工作线程正在访问真正应该是线程本地的全局变量,因此多个客户端不会覆盖彼此的数据.
此外,你有一个小的竞争条件.您有工作线程向info客户端发送命令,以及发送disconnect-client命令的主线程.您没有同步命令,因此有一个小的机会窗口可能会重叠,从而破坏您的通信.
最后,在Java中,DataOutputStream.writeChars()以UTF-16格式写出Unicode字符串.在Delphi中,Char是AnsiChar在D2007及更早版本,并且WideChar在D2009及更高版本中.网络通信应该使用UTF-8作为文本,因为它可以在平台之间移植(无端序问题),并且通常占用较少的带宽,特别是对于基于拉丁语的语言.您应该编写Android和Delphi代码,以强制通过连接强制UTF-8.
话虽如此,尝试更像这样的东西:
public class MainActivity extends AppCompatActivity {
private Socket xclientSocket;
class ClientThread implements Runnable {
@Override
public void run() {
try {
xclientSocket = new Socket("192.168.15.12", 101);
new Thread(new CMDThread()).start();
}
catch (Exception e1) {
System.out.println(e1.toString());
}
}
}
class CMDThread implements Runnable {
@Override
public void run() {
try {
BufferedReader xreader = new BufferedReader(new InputStreamReader(xclientSocket.getInputStream(), StandardCharsets.UTF_8));
DataOutputStream dOut = new DataOutputStream(new BufferedOutputStream(xclientSocket.getOutputStream()));
while ((xline = xreader.readLine()) != null) {
System.out.println(xline);
xline = xline.trim();
if (xline.equalsIgnoreCase("info")) {
byte[] data = ("<|data|>" + myDeviceProduct.toUpperCase() + "<|>" + myDeviceModel + "<|>" + myVersion + "<|>" + SIM_OPNAME + "<|>" + SIM_NUMBER + "<<|").getBytes(StandardCharsets.UTF_8);
dOut.writeInt(data.length);
dOut.write(data, 0, data.length);
dOut.flush();
}
if (xline.equalsIgnoreCase("disconnect")) {
break;
}
}
System.out.println("Shutting down Socket!!");
xclientSocket.close();
}
catch (Exception e1) {
System.out.println(e1.toString());
}
}
}
///////////////////////// USAGE /////////////////////////////
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new ClientThread()).start();
}
}
Run Code Online (Sandbox Code Playgroud)
var
FMain: TFMain;
implementation
{$R *.dfm}
//================================================================================================================
type
TCMDSock_Thread = class(TServerClientThread)
private
Manufacturer, Model, OsVersion, SIMOpName, SIMNumber: string;
procedure addClientToListView;
procedure removeClientFromListView;
protected
procedure ClientExecute; override;
end;
procedure TCMDSock_Thread.addClientToListView;
var
Item: TListItem;
begin
Item := FMain.ListView1.FindData(0, ClientSocket, True, False);
if Item = nil then
begin
Item := FMain.ListView1.Items.Add;
Item.Data := ClientSocket;
end;
Item.Caption := IntToStr(ClientSocket.Handle);
Item.SubItems.Add(ClientSocket.RemoteAddress);
Item.SubItems.Add(ClientSocket.RemoteHost);
Item.SubItems.Add(Manufacturer + ' - ' + Model + ' - ' + OsVersion);
Item.SubItems.Add(SIMOpName + ' - ' + SIMNumber);
FMain.ServerStatus.Panels.Items[1].Text := 'Connected';
end;
procedure TCMDSock_Thread.removeClientFromListView;
var
Item: TListItem;
begin
Item := FMain.ListView1.FindData(0, ClientSocket, True, False);
if Item <> nil then
Item.Delete;
FMain.StatusBar1.Panels.Items[1].Text := 'Disconnected';
end;
procedure TCMDSock_Thread.ClientExecute;
var
SocketStrm: TWinSocketStream;
ReceivedText: string;
procedure readRaw(var Data; Size: Integer);
var
P: PByte;
Received: Integer;
begin
P := PByte(@Data);
while Size > 0 do
begin
Received := SocketStrm.Read(P^, Size);
if Received <= 0 then SysUtils.Abort;
Inc(P, Received);
Dec(Size, Received);
end;
end;
procedure writeRaw(const Data; Size: Integer);
var
P: PByte;
Sent: Integer;
begin
P := PByte(@Data);
while Size > 0 do
begin
Sent := SocketStrm.Write(P^, Size);
if Sent <= 0 then SysUtils.Abort;
Inc(P, Sent);
Dec(Size, Sent);
end;
end;
function readMessage: boolean;
var
Tmp: UTF8String;
Len: Integer;
begin
Result := SocketStrm.WaitForData(100);
if not Result then Exit;
readRaw(Len, sizeof(Len));
Len := ntohl(Len);
SetLength(Tmp, Len);
readRaw(PAnsiChar(Tmp)^, Len);
ReceivedText := string(Tmp);
end;
procedure writeString(const S: string);
var
Tmp: UTF8String;
begin
Tmp := UTF8String(S);
writeRaw(PAnsiChar(Tmp)^, Length(Tmp));
end;
procedure writeLine(const S: string);
begin
writeString(S + #13#10);
end;
function splitData: boolean;
var
Idx: Integer;
begin
Result := StartsText('<|data|>', ReceivedText);
if not Result then Exit;
Delete(ReceivedText, 1, 8);
Idx := Pos('<|>', ReceivedText);
Manufacturer := Copy(ReceivedText, 1, Idx-1);
Delete(ReceivedText, 1, Idx+2);
Idx := Pos('<|>', ReceivedText);
Model := Copy(ReceivedText, 1, Idx-1);
Delete(ReceivedText, 1, Idx+2);
Idx := Pos('<|>', ReceivedText);
OsVersion := Copy(ReceivedText, 1, Idx-1);
Delete(ReceivedText, 1, Idx+2);
Idx := Pos('<|>', ReceivedText);
SIMOpName := Copy(ReceivedText, 1, Idx-1);
Delete(ReceivedText, 1, Idx+2);
Idx := Pos('<<|', ReceivedText);
SIMNumber := Copy(ReceivedText, 1, Idx-1);
end;
begin
try
SocketStrm := TWinSocketStream.Create(ClientSocket, 5000);
try
writeLine('info');
try
while (not Terminated) and ClientSocket.Connected do
begin
if readMessage then
begin
if splitData then
Synchronize(addClientToListView);
end;
end;
finally
writeLine('disconnect');
end;
finally
SocketStrm.Free;
end;
finally
Synchronize(removeClientFromListView);
end;
end;
//================================================================================================================
procedure TFMain.ServerSocket1GetThread(Sender: TObject;
ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread);
begin
SocketThread := TCMDSock_Thread.Create(False, ClientSocket);
end;
procedure TFMain.Button2Click(Sender: TObject);
begin
ServerSocket1.Active := False;
end;
Run Code Online (Sandbox Code Playgroud)