Я пишу простое приложение для Windows TCP / IP-сервера, которое одновременно должно взаимодействовать только с одним клиентом.Мое приложение имеет четыре потока:
- Основная программа, которая также обрабатывает передачу данных по мере необходимости.
- Получение входящего потока данных.
- Поток прослушивания для приема запросов на подключение отклиент.
- Поток ping, который отслеживает все остальное и при необходимости передает сообщения пульса.Я понимаю, что последнее не должно быть действительно необходимым с TCP / IP, но клиентское приложение (над которым я не имею никакого контроля) требует этого.
Я подтвердил в диспетчере задач, что мое приложениедействительно работает четыре потока.
Я использую блокировку сокетов TCP / IP, но, насколько я понимаю, они блокируют только вызывающий поток - другим потокам все равно следует разрешить выполнение без блокировки.Однако я столкнулся со следующими проблемами:
Если поток проверки связи считает, что соединение прервано, он вызывает closesocket ().Однако, похоже, что он заблокирован вызовом recv () в получающем потоке.
Основное приложение не может передавать данные, в то время как принимающий поток имеет вызов recv () выполняется.
Сокет создается с помощью функции accept ().На данном этапе я не устанавливаю никаких опций сокетов.
Я сейчас создал простую двухпоточную программу, которая иллюстрирует проблему.Без флага WSA_FLAG_OVERLAPPED второй поток блокируется первым потоком, даже если это будет противоречить тому, что должно произойти.Если установлен флаг WSA_FLAG_OVERLAPPED, то все работает так, как я и ожидал.
PROJECT SOURCE FILE:
====================
program Blocking;
uses
Forms,
Blocking_Test in 'Blocking_Test.pas' {Form1},
Close_Test in 'Close_Test.pas';
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end. { Blocking }
UNIT 1 SOURCE FILE:
===================
unit Blocking_Test;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, WinSock2;
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Test_Socket: TSocket;
Test_Addr: TSockAddr;
wsda: TWSADATA; { used to store info returned from WSAStartup }
implementation
{$R *.dfm}
uses
Debugger, Close_Test;
procedure TForm1.FormShow(Sender: TObject);
const
Test_Port: word = 3804;
var
Buffer: array [0..127] of byte;
Bytes_Read: integer;
begin { TForm1.FormShow }
Debug('Main thread started');
assert(WSAStartup(MAKEWORD(2,2), wsda) = 0); { WinSock load version 2.2 }
Test_Socket := WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, nil, 0, 0{WSA_FLAG_OVERLAPPED});
assert(Test_Socket <> INVALID_SOCKET);
with Test_Addr do
begin
sin_family := AF_INET;
sin_port := htons(Test_Port);
sin_addr.s_addr := 0; { this will be filled in by bind }
end; { with This_PC_Address }
assert(bind(Test_Socket, @Test_Addr, SizeOf(Test_Addr)) = 0);
Close_Thread := TClose_Thread.Create(false); { start thread immediately }
Debug('B4 Rx');
Bytes_Read := recv(Test_Socket, Buffer, SizeOf(Buffer), 0);
Debug('After Rx');
end; { TForm1.FormShow }
end. { Blocking_Test }
UNIT 2 SOURCE FILE:
===================
unit Close_Test;
interface
uses
Classes;
type
TClose_Thread = class(TThread)
protected
procedure Execute; override;
end; { TClose_Thread }
var
Close_Thread: TClose_Thread;
implementation
uses
Blocking_Test, Debugger, Windows, WinSock2;
type
TThreadNameInfo = record
FType: LongWord; // must be 0x1000
FName: PChar; // pointer to name (in user address space)
FThreadID: LongWord; // thread ID (-1 indicates caller thread)
FFlags: LongWord; // reserved for future use, must be zero
end; { TThreadNameInfo }
var
ThreadNameInfo: TThreadNameInfo;
procedure TClose_Thread.Execute;
procedure SetName;
begin { SetName }
ThreadNameInfo.FType := $1000;
ThreadNameInfo.FName := 'Ping_Thread';
ThreadNameInfo.FThreadID := $FFFFFFFF;
ThreadNameInfo.FFlags := 0;
try
RaiseException( $406D1388, 0, sizeof(ThreadNameInfo) div sizeof(LongWord), @ThreadNameInfo );
except
end; { try }
end; { SetName }
begin { TClose_Thread.Execute }
Debug('Close thread started');
SetName;
sleep(10000); { wait 10 seconds }
Debug('B4 Close');
closesocket(Test_Socket);
Debug('After Close');
end; { TClose_Thread.Execute }
end. { Close_Test }
PS Поскольку установка атрибута WSA_FLAG_OVERLAPPED устранила проблему, я опубликовал приведенное выше для академического интереса.