Почему несколько блоков при передаче по сокету могут объединяться в один

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Скорость передачи между двумя и более пользователями Intеrnеtа при обрыве связи внезапно возрастает до 2 мегаматов в секунду.

Итак, во-первых, надо заметить, что посылаемые через сокет данные могут не только объединяться в один блок, но и разъединяться по нескольким блокам. Дело в том, что сокет — обычный поток, но в отличие, скажем, от файлового (TFileStream), он передает данные медленнее (сами понимаете — сеть, ограниченный трафик, и т.д.). Именно поэтому две команды:

ServerSocket1.Socket.Connections[0].SendText(‘Hello, ‘);

ServerSocket1.Socket.Connections[0].SendText(‘world!’);

совершенно идентичны одной команде:

ServerSocket1.Socket.Connections[0].SendText(‘Hello, world!’);

И именно поэтому, если Вы отправите через сокет файл, скажем, в 100 Кб, то тому, кому Вы посылали этот блок, придет несколько блоков с размерами, которые зависят от трафика и загруженности линии. Причем, размеры не обязательно будут одинаковыми. Отсюда следует, что для того, чтобы принять файл или любые другие данные большого размера, Вам следует принимать блоки данных, а затем объединять их в одно целое (и сохранять, например, в файл). Отличным решением данной задачи является тот же файловый поток — TFileStream (либо поток в памяти — TMemoryStream). Принимать частички данных из сокета можно через событие OnRead (OnClientRead), используя универсальный метод ReceiveBuf. Определить размер полученного блока можно методом ReceiveLength. Также можно воспользоваться сокетным потоком (см. статью про TClientSocket). А вот и небольшой примерчик (приблизительный):

{Прием файла через сокет}

procedure TForm1.ClientSocket1Read(Sender: TObject;

Socket: TCustomWinSocket);

var l: Integer;

buf: PChar;

src: TFileStream;

begin

{Записываем в l размер полученного блока}

l := Socket.ReceiveLength;

{Заказываем память для буфера}

GetMem(buf,l 1);

{Записываем в буфер полученный блок}

Socket.ReceiveBuf(buf,l);

{Открываем временный файл для записи}

src := TFileStream.Create(‘myfile.tmp’,fmOpenReadWrite);

{Ставим позицию в конец файла}

src.Seek(0,soFromEnd);

{Записываем буфер в файл}

src.WriteBuffer(buf,l);

{Закрываем файл}

src.Free;

{Освобождаем память}

FreeMem(buf);

end;

{/codecitation}

Посылка и прием сообщений через сокеты

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Интеpнетчик возвpащается поздно домой. Его встpечают двое гpомил. Один из них достает нож и говоpит:

— Гони деньги!

«Пpовайдеpы»,- подумал интеpнетчик.

{… Здесь идет заголовок файла и определение

формы TForm1 и ее экземпляра Form1}

procedure TForm1.Button1Click(Sender: TObject);

begin

{Определяем порт и запускаем сервер}

ServerSocket1.Port := 1025;

{Метод Insert вставляет строку в массив в указанную позицию}

Memo2.Lines.Insert(0,’Server starting’);

ServerSocket1.Open;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

{Останавливаем сервер}

ServerSocket1.Active := False;

Memo2.Lines.Insert(0,’Server stopped’);

end;

procedure TForm1.ServerSocket1Listen(Sender: TObject;

Socket: TCustomWinSocket);

begin

{Здесь сервер «прослушивает» сокет на наличие клиентов}

Memo2.Lines.Insert(0,’Listening on port ‘ IntToStr(ServerSocket1.Port));

end;

procedure TForm1.ServerSocket1Accept(Sender: TObject;

Socket: TCustomWinSocket);

begin

{Здесь сервер принимает клиента}

Memo2.Lines.Insert(0,’Client connection accepted’);

end;

procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;

Socket: TCustomWinSocket);

begin

{Здесь клиент подсоединяется}

Memo2.Lines.Insert(0,’Client connected’);

end;

procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject;

Socket: TCustomWinSocket);

begin

{Здесь клиент отсоединяется}

Memo2.Lines.Insert(0,’Client disconnected’);

end;

procedure TForm1.ServerSocket1ClientError(Sender: TObject;

Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;

var ErrorCode: Integer);

begin

{Произошла ошибка — выводим ее код}

Memo2.Lines.Insert(0,’Client error. Code = ‘ IntToStr(ErrorCode));

end;

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;

Socket: TCustomWinSocket);

begin

{От клиента получено сообщение — выводим его в Memo1}

Memo2.Lines.Insert(0,’Message received from client’);

Memo1.Lines.Insert(0,’> ‘ Socket.ReceiveText);

end;

procedure TForm1.ServerSocket1ClientWrite(Sender: TObject;

Socket: TCustomWinSocket);

begin

{Теперь можно слать данные в сокет}

Memo2.Lines.Insert(0,’Now can write to socket’);

end;

procedure TForm1.ServerSocket1GetSocket(Sender: TObject; Socket: Integer;

var ClientSocket: TServerClientWinSocket);

begin

Memo2.Lines.Insert(0,’Get socket’);

end;

procedure TForm1.ServerSocket1GetThread(Sender: TObject;

ClientSocket: TServerClientWinSocket;

var SocketThread: TServerClientThread);

begin

Memo2.Lines.Insert(0,’Get Thread’);

end;

procedure TForm1.ServerSocket1ThreadEnd(Sender: TObject;

Thread: TServerClientThread);

begin

Memo2.Lines.Insert(0,’Thread end’);

end;

procedure TForm1.ServerSocket1ThreadStart(Sender: TObject;

Thread: TServerClientThread);

begin

Memo2.Lines.Insert(0,’Thread start’);

end;

procedure TForm1.Button3Click(Sender: TObject);

var i: Integer;

begin

{Посылаем ВСЕМ клиентам сообщение из Edit1}

for i := 0 to ServerSocket1.Socket.ActiveConnections-1 do begin

ServerSocket1.Socket.Connections[i].SendText(Edit1.Text);

end;

Memo1.Lines.Insert(0,’< ' Edit1.Text);

end;

{/codecitation}

Определить, занят ли порт сокета

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

— Смайлик видишь?

— Нет.

— И я не вижу. А он есть.

var SockAddrIn : TSockAddrIn;

FSocket : TSocket;

If bind(FSocket, SockAddrIn, SizeOf(SockAddrIn)) 0 Then

begin

обрабатываем WSAGetLastError

end;

{/codecitation}

Как послать широковещательный UDP пакет

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Смотрит ламер на папку «Удаленный доступ к сети», и думает: «Как он ещё тут, когда его уже удалили?»

procedure TMainForm.FormCreate(Sender: TObject);

var

Init: TWSAData;

SockOpt: BOOL;

Sock: TSocket;

Target: TSockAddrIn;

begin

WSAStartup($101,Init);

Sock:=Socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);

SockOpt:=TRUE;

SetSockOpt(Sock,SOL_SOCKET,SO_BROADCAST, PChar(@SockOpt),SizeOf(SockOpt)) ;

Target.sin_port:=htons(8167); //номер порта

Target.sin_addr.S_addr:=INADDR_BROADCAST;

Target.sa_family:=AF_INET;

SendTo(Sock,Data,DataBytes,0,Target,SizeOf(Target));

WSACleanup;

end;

{/codecitation}

Как передать картинку по сети через ServerSocket

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Автор: TwoK

WEB-сайт: http://forum.vingrad.ru

Да без проблем. Звиняйте, что на сях, но, тем не менее, на Борланд сях. Со стороны, откуда посылаем (у нас это клиент), пишем:

TFileStream* str = new TFileStream(«M:\\MyFile.jpg»,fmOpenRead);

//ИЛИ, если мы работаем без сохранения (тогда не создается файл)

TMemoryStream* str = new TMemoryStream ();

str->Position = 0;

Image1->Picture->Bitmap->SaveToStream(str);

//и, наконец, шлем на сервер битмап

str->Position = 0;

ClientSocket1->Socket->SendStream(str);

Обратите внимание, не забывайте перед каждой операцией с потоком устанавливать позицию в 0!!! Иначе получим не то, что хотелось бы Ну а со стороны приема (у нас это, соответственно, серверсокет), в событии приема пишем:

int ibLen = ServerSocket1->Socket->ReceiveLength();

char* buf= new char[ibLen 1];

TMemoryStream* str = new TMemoryStream();

str->Position = 0;

ServerSocket1->Socket->ReceiveBuf((void*)buf,ibLen);

str->WriteBuffer((void*)buf,ibLen);

str->Position = 0;

Image1->Picture->Bitmap->LoadFromStream(str);

//или

str->SaveToFile(«M:\\MyFile.jpg»);

Ну и ессно, как говорит Bigbrother, сделал дело — вызови деструктор! То есть почистить за собой надо, не знаю как в Паскале, но в сях мне надо удалить str и buf.

{/codecitation}

Как отправить вебформу на сервер при помощи TClientSocket (напрямую и через прокси)

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Посетитель у провайдера.

— Ой, что это у вас так крякнуло.

— Сервер наверное…

{

Присоедините следующие события к Вашему ClientSocket:

procedure T…Form.ClientSocket1Write;

procedure T…Form.ClientSocket1Read;

procedure T…Form.ClientSocket1Disconnect;

procedure T…Form.ClientSocket1Error;

Так же пример показывает, как направлять передачу через прокси-сервер.

Для отправки на вебсервер используется следующий формат:

Напрямую:

‘POST ‘ PostAddr ‘HTTP/1.0’ HTTP_Data Content

Через проксю:

‘POST http://’ Webserver PostAddr ‘HTTP/1.0’ HTTP_Data Content

}

const

WebServer = ‘www.somehost.com’;

WebPort = 80;

PostAddr = ‘/cgi-bin/form’;

{ Следующие переменные используются только для вебсервера: }

ProxyServer =’proxy.somewhere.com’;

ProxyPort = 3128;

// В заголовке post необходимы некоторые данные

HTTP_Data =

‘Content-Type: application/x-www-form-urlencoded’#10

‘User-Agent: Delphi/5.0 ()’#10 { Отрекламируем Delphi 5! }

‘Host: somewhere.com’#10

‘Connection: Keep-Alive’#10;

type

T…Form = class(TForm)

private

{ Private declarations }

HTTP_POST : string;

FContent : string;

// Эта переменная будет содержать ответ сервера

FResult : string;

public

{ Public declarations }

end;

{ Эти функции сделают некоторое url-кодирование }

{ Например. ‘John Smith’ => ‘John Smith’ }

function HTTPTran(St: string): string;

var

i: Integer;

begin

Result:=»;

for i:=1 to length(St) do

if St[i] in [‘a’..’z’,’A’..’Z’,’0′,’1′..’9′] then

Result:=Result St[i]

else

if St[i]=’ ‘ then

Result:=Result ‘ ‘

else

Result:=Result ‘%’ IntToHex(Byte(St[i]),2);

end;

procedure T…Form.ClientSocket1Write(Sender: TObject;

Socket: TCustomWinSocket);

begin

// Постим данные

Socket.SendText(HTTP_POST FContent);

end;

procedure T…Form.ClientSocket1Read(Sender: TObject;

Socket: TCustomWinSocket);

begin

// Получаем результат

FResult:=FResult Socket.ReceiveText;

end;

procedure T…Form.ClientSocket1Disconnect(Sender: TObject;

Socket: TCustomWinSocket);

begin

// ЗДЕСЬ МОЖНО ОБРАБОТАТЬ FResult //

end;

procedure T…Form.ClientSocket1Error(Sender: TObject;

Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;

var ErrorCode: Integer);

begin

ErrorCode := 0; // Игнорируем ошибки

end;

{ А эта подпрограмма, которую можно

использовать для постинга данных формы. }

procedure T…Form.PostTheForm;

begin

// Очищаем результаты

FResult:=»;

// Вы можете ввести поля формы, которые необходимы

// Вот некоторые примеры:

FContent:=

‘Name=’ HTTPTran(‘John Smith’) ‘

Как запросить страницу с сайта 2

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Автор: http://www.sources.ru

{

Присоедините следующий обработчик к Вашему TClientSocket.

Он получает файл с сервера и помещает его в строковую переменную

FText string variable. Однако он не убирает заголовок,

который так же посылается вебсервером.

Не забудьте задать правильный адрес сервера в объекте Socket.

Установите порт 80. А затем откройте его при помощи команды «Socket.Open;».

Автор: E.J.Molendijk

}

const

WebPage = ‘/index.html’;

var

FText: string;

procedure TForm1.SocketWrite(Sender: TObject;

Socket: TCustomWinSocket);

begin

Socket.SendText(‘GET ‘ Webpage ‘ HTTP/1.0’#10#10);

end;

procedure TForm1.SocketRead(Sender: TObject;

Socket: TCustomWinSocket);

begin

FText := FText Socket.ReceiveText

end;

procedure TForm1.SocketConnecting(Sender: TObject;

Socket: TCustomWinSocket);

begin

FText := »;

end;

procedure TForm1.SocketDisconnect(Sender: TObject;

Socket: TCustomWinSocket);

begin

{ — }

{ ЗДЕСЬ ВЫ МОЖЕТЕ ОБРАБАТЫВАТЬ ВАШ FText !!! }

{ — }

end;

procedure TForm1.SocketError(Sender: TObject;

Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;

var ErrorCode: Integer);

begin

ErrorCode := 0; { Ошибки игнорируем }

end;

{/codecitation}

Как запросить страницу с сайта

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Автор: Fantasist

WEB-сайт: http://forum.vingrad.ru

unit Unit1;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls, ScktComp;

const

Request: AnsiString = ‘GET / HTTP/1.1’ #0$D#0$A

‘Accept: application/vnd.ms-excel, application/msword, */*’ #0$D#0$A

‘Accept-Language: en-us’ #0$D#0$A

‘Accept-Encoding: gzip, deflate’ #0$D#0$A

‘User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)’ #0$D#0$A

‘Host: vingrad.com’ #0$D#0$A

‘Connection: Keep-Alive’ #0$D#0$A #0$D#0$A;

type

TForm1 = class(TForm)

Skt: TClientSocket;

Button1: TButton;

Memo1: TMemo;

procedure Button1Click(Sender: TObject);

procedure SktRead(Sender: TObject; Socket: TCustomWinSocket);

procedure SktConnect(Sender: TObject; Socket: TCustomWinSocket);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);

begin

Skt.Host := ‘vingrad.ru’;

Skt.Port := 80;

Skt.Open;

end;

procedure TForm1.SktRead(Sender: TObject; Socket: TCustomWinSocket);

begin

Memo1.Lines.Text := Memo1.Lines.Text Socket.ReceiveText;

end;

procedure TForm1.SktConnect(Sender: TObject; Socket: TCustomWinSocket);

begin

Socket.SendText(Request);

end;

end.

Request — это запрос который посылает мой IE5. В принципе, по протоколу HTTP он может ограничиваться: ‘GET / HTTP/1.1’ #13 #13. Если хотите запросить оределенный документ: ‘GET / HTTP/1.1’ #13 #13. Конечно, всегда можно воспользоваться готовыми компонентами.

{/codecitation}

Использование ServerSocket и ClientSocket

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Оформил: DeeCo

Автор: http://www.swissdelphicenter.ch

// Client Program:

// Send ‘power’ to Client to shutdown the machine.

// Send ‘reset’ to Client to reset the machine.

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, ComCtrls, ScktComp;

type

TForm1 = class(TForm)

Clientsocket1: TClientSocket;

StatusBar1: TStatusBar;

Button1: TButton;

Button2: TButton;

Edit1: TEdit;

Label1: TLabel;

Button3: TButton;

CheckBox1: TCheckBox;

Checkbox2: TCheckBox;

procedure Button1Click(Sender : TObject);

procedure Button2Click(Sender : TObject);

procedure Clientsocket1Error(Sender : TObject; Socket : TCustomWinSocket;

ErrorEvent : TErrorEvent; var ErrorCode : integer);

procedure Clientsocket1Disconnect(Sender : TObject;

Socket : TCustomWinSocket);

procedure Clientsocket1Connect(Sender : TObject;

Socket : TCustomWinSocket);

procedure Button3Click(Sender : TObject);

procedure FormClose(Sender : TObject; var Action : TCloseAction);

procedure FormDestroy(Sender : TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1 : TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender : TObject);

begin

Clientsocket1.Active := True;

end;

procedure TForm1.Button2Click(Sender : TObject);

begin

Clientsocket1.Active := False;

end;

procedure TForm1.Clientsocket1Error(Sender : TObject;

Socket : TCustomWinSocket; ErrorEvent : TErrorEvent;

var ErrorCode : integer);

begin

errorcode := 0;

StatusBar1.SimpleText := ‘Error’;

end;

procedure TForm1.Clientsocket1Disconnect(Sender : TObject;

Socket : TCustomWinSocket);

begin

StatusBar1.SimpleText := ‘Disconnect’;

end;

procedure TForm1.Clientsocket1Connect(Sender : TObject;

Socket : TCustomWinSocket);

begin

StatusBar1.SimpleText := Clientsocket1.Address;

end;

procedure TForm1.Button3Click(Sender : TObject);

var

ukaz : string;

orders : string;

Text : string;

box : string;

begin

ukaz := edit1.Text;

Clientsocket1.Socket.SendText(ukaz);

if checkbox1.Checked = True then

begin

orders := ‘power’;

Clientsocket1.Socket.SendText(orders);

end;

if Checkbox2.Checked = True then

begin

Text := ‘reset’;

Clientsocket1.Socket.SendText(Text);

end;

end;

procedure TForm1.FormClose(Sender : TObject; var Action : TCloseAction);

begin

Clientsocket1.Active := False;

end;

procedure TForm1.FormDestroy(Sender : TObject);

begin

Clientsocket1.Active := False;

end;

end.

// Client Program

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, ScktComp, StdCtrls, ShellApi;

type

TForm1 = class(TForm)

Label1: TLabel;

Serversocket1: TServerSocket;

procedure FormClose(Sender : TObject; var Action : TCloseAction);

procedure FormDestroy(Sender : TObject);

procedure FormCreate(Sender : TObject);

procedure Serversocket1ClientError(Sender : TObject;

Socket : TCustomWinSocket; ErrorEvent : TErrorEvent;

var ErrorCode : integer);

procedure Serversocket1ClientRead(Sender : TObject;

Socket : TCustomWinSocket);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1 : TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormClose(Sender : TObject; var Action : TCloseAction);

begin

Serversocket1.Active := False;

end;

procedure TForm1.FormDestroy(Sender : TObject);

begin

Serversocket1.Active := False;

end;

procedure TForm1.FormCreate(Sender : TObject);

begin

Serversocket1.Active := True;

end;

procedure TForm1.Serversocket1ClientError(Sender : TObject;

Socket : TCustomWinSocket; ErrorEvent : TErrorEvent;

var ErrorCode : integer);

begin

errorcode := 0;

end;

procedure TForm1.Serversocket1ClientRead(Sender : TObject;

Socket : TCustomWinSocket);

var

ukaz : string;

orders : string;

Text : string;

box : string;

begin

ukaz := socket.ReceiveText;

label1.Caption := ‘reciving…’;

ShellExecute(Handle, ‘open’, PChar(ukaz), PChar(»), nil, sw_show);

Text := socket.ReceiveText;

orders := socket.ReceiveText;

if orders = ‘power’ then

begin

ShellExecute(Handle, ‘open’, PChar(‘shutdown.exe’), PChar(‘-s’), nil, sw_show);

Application.MessageBox(‘You will be turned off’, ‘Warning’, mb_iconexclamation);

Serversocket1.Active := False;

Form1.Close;

end;

if Text = ‘reset’ then

begin

ShellExecute(Handle, ‘open’, PChar(‘shutdown.exe’), PChar(‘-r’), nil, sw_show);

Application.MessageBox(‘You will be reset’, ‘Warning’, mb_iconexclamation);

Serversocket1.Active := False;

Form1.Close;

end;

end;

end.

{/codecitation}

Асинхронная ошибка

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Если Вы находите ошибки, исправить которые дело долгое и нудное, ничего не делайте — просто внесите их в список особенностей.

Вопрос: Почему не работает следующий код?

begin

ClietnSocket1.Open;

if ClietnSocket1.Socket.Connected then

ClietnSocket1.Socket.SendText(‘Hello’);

{..}

end;

// Выдает — ассинхронная ошибка.

Вы работаете в ассинхронном режиме. Следует использовать соответсвующие события.

{/codecitation}