С сервером немного сложнее. Требуются определённые навыки в написании мультипоточных приложений.
Кроме того, тот класс которым я пользуюсь, содержит код, специфичный для моего приложения и
сходу разобраться в нём будет достаточно тяжело.
Поэтому не смогу дать конечную реализацию, просто приведу здесь куски кода, которые в конечном итоге
реализуют функционал TCP сервера.
Итак, по шагам:
0. В uses прописываем примерно следующее:
- Код: Выделить всё
uses
{$IFNDEF UNIX}
WinSock,
Windows,
{$ELSE}
Sockets;
{$ENDIF}
1. Инициализируем WinSock (нужно для Win и WinCE, не нужно для Linux)
- Код: Выделить всё
WSAStartup(MakeWord(2,0),Data);
2. Создаем отдельный поток, в котором запускаем примерно такой код:
- Код: Выделить всё
var
newSocket : TSocket;
serv : sockaddr_in;
addr_len : Integer;
addr : sockaddr_in;
.......................................................................
newSocket := socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (newSocket <> INVALID_SOCKET) then
begin
serv.sin_family := AF_INET;
serv.sin_port := htons(Port); // <-- TCP порт, который мы слушаем
serv.sin_addr.S_addr := INADDR_ANY;
if (bind(newSocket,serv,SizeOf(sockaddr_in)) = {$IFNDEF UNIX} SOCKET_ERROR {$ELSE} False {$ENDIF}) then
begin
closesocket(newSocket);
newSocket := INVALID_SOCKET;
end;
if (newSocket <> INVALID_SOCKET) then
begin
if (listen(newSocket,SOMAXCONN) = {$IFNDEF UNIX} SOCKET_ERROR {$ELSE} False {$ENDIF}) then
begin
closesocket(newSocket);
newSocket := INVALID_SOCKET;
end;
// Цикл приема подключений
repeat
addr_len := SizeOf(sockaddr_in);
try
clientSocket := accept(newSocket ,{$IFNDEF UNIX}@{$ENDIF}Addr,{$IFNDEF FPC}@{$ENDIF}Addr_len);
except
clientSocket := INVALID_SOCKET;
end;
if (clientSocket <> INVALID_SOCKET) then
begin
// Здесь мы приняли подключение.
// Нужно создать новый поток, который будет взаимодействовать с клиентом и передать ему значение clientSocket
end;
until ((clientSocket = INVALID_SOCKET) or Thread.Terminated);
end;
end;
3. В клиентском потоке запускаем цикл приёма и обработки данных:
- Код: Выделить всё
const
cnStaticBufferSize = 4096;
var
clientSocket : TSocket;
staticBuf : array [0 .. cnStaticBufferSize - 1] of Char;
nRet : Integer;
.......................................................................................
repeat
nRet := recv(clientSocket,staticBuf[0],cnStaticBufferSize,0);
if (nRet > 0) then
begin
// staticBuf содержит nRet байт пришедших данных
// Можно их обработать
end;
until ((nRet <= 0) or Thread.Terminated);
4. При завершении работы приложения не забываем про WSACleanUp (если мы под Win,WinCE).
В принципе, все. Хотя, мог упустить какие-то мелочи.
Хотел бы еще заметить, что в приведенном коде используются прямые обращения к сетевым функциям. Если не ошибаюсь, в fpc есть соответствующие аналоги с префиксом
fp (например, fpsocket, fpbind и т.д.), которые по сути являются обертками. Цель их присутствия в rtl фрипаскаля мне до конца не ясна. Возможно, с их помощью можно было написать приведенный код более лаконично (например, без лишних директив IFDEF).
Я намеренно не стал использовать обёртки из rtl fpc, т.к. основной средой разработки для меня является Delphi. Там эти обёртки отсутсвуют.