-
Нужно написать на winsocket без VCL компонентов клиент-сервер которые будут обмениваться данными, помогите пожалуйста, если можно исходник или ссылки на статьи или готовые варианты реализации, буду очень благодарен, малое представление имею о winsocket но не могу реализовать передачу данных!
-
продолжение вопроса... Вот у меня есть акая реализация сервер-клмент, подправьте что не так. TCPClient procedure TestWinSockError(S:String); var iErr:Integer; sFullErr:String; begin sFullErr:='Неизвестная ошибка'; iErr:=WSAGetLastError();
case iErr of WSANOTINITIALISED: sFullErr:='Нужно сначала вызвать функцию WSASturtup, а потом создавать сокет'; WSAENETDOWN: sFullErr:='Cвязь нарушена, возможные причины - отошёл кабель или отключились от Интернета'; WSAEADDRINUSE: sFullErr:='Указанный адрес уже используется'; WSAEFAULT: sFullErr:='Параметры name и namelen не соответствуют выбранной адресации. Параметр namelen может быть меньше необходимого значения, а name содержать некорректные данные'; WSAEINPROGRESS: sFullErr:='Выполняется операция в блокирующем режиме. Вы уже запустили на выполнение какую-то функцию и нужно дождаться завершения её работы'; WSAEINVAL: sFullErr:='Сокет уже связан с адресом'; WSAENOBUFS: sFullErr:='Недостаточно буферов, слишком много соединений'; WSAENOTSOCK: sFullErr:='Неверный дескриптор сокета'; WSAEISCONN: sFullErr:='Сокет уже подключён'; WSAEMFILE: sFullErr:='Нет больше доступных дескрипторов'; end;
MessageBox(0, PChar('Ошибка в функции '+S+' - '+sFullErr), 'Ошибка', 0); end;
function LookupName(name:String): TInAddr; var HostEnt: PHostEnt; InAddr: TInAddr; begin if name[4]='.' then InAddr.s_addr := inet_addr(PChar(name)) else begin HostEnt := gethostbyname(PChar(name)); FillChar(InAddr, SizeOf(InAddr), 0); if HostEnt <> nil then begin with InAddr, HostEnt^ do begin S_un_b.s_b1 := h_addr^[0]; S_un_b.s_b2 := h_addr^[1]; S_un_b.s_b3 := h_addr^[2]; S_un_b.s_b4 := h_addr^[3]; end; end end; Result := InAddr; end;
procedure TTCPClientForm.btSendClick(Sender: TObject); var wData : WSADATA; sServerListen: TSOCKET; server_addr : sockaddr_in; iRet : Integer; sRecvBuff : array [0..255] of char; begin // Загрузка WinSock if WSAStartup(MAKEWORD(1,1), wData) <> 0 then begin MessageBox(0, 'Не могу загрузить WinSock', 'Ошибка', 0); exit; end;
// Создание сокета sServerListen := socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if sServerListen = INVALID_SOCKET then begin MessageBox(0, 'Ошибка создания сокета', 'Ошибка', 0); exit; end;
// Запонение структуры адреса server_addr.sin_addr.s_addr := htonl(INADDR_ANY); server_addr.sin_family := AF_INET; server_addr.sin_port := htons(5050); server_addr.sin_addr := LookupName(edServer.Text);
if (connect(sServerListen, server_addr, sizeof(server_addr)) = SOCKET_ERROR) then begin TestWinSockError('Send'); exit; end;
sRecvBuff:='get'; iRet := send(sServerListen, sRecvBuff, 3, 0); if (iRet = SOCKET_ERROR) then begin MessageBox(0, 'Ошибка передачи данных', 'Внимание!!!', 0); exit; end;
iRet := recv(sServerListen, sRecvBuff, 1024, 0); if (iRet = SOCKET_ERROR) then begin MessageBox(0, 'Ошибка получения данных', 'Внимание!!!', 0); exit; end;
edServer.Text:=sRecvBuff; CloseSocket(sServerListen); end;
end. --------------------------------- TCPServer procedure TestWinSockError(S:String); var iErr:Integer; sFullErr:String; begin sFullErr:='Неизвестная ошибка'; iErr:=WSAGetLastError();
case iErr of WSANOTINITIALISED: sFullErr:='Нужно сначала вызвать функцию WSASturtup, а потом создавать сокет'; WSAENETDOWN: sFullErr:='Cвязь нарушена, возможные причины - отошёл кабель или отключились от Интернета'; WSAEADDRINUSE: sFullErr:='Указанный адрес уже используется'; WSAEFAULT: sFullErr:='Параметры name и namelen не соответствуют выбранной адресации. Параметр namelen может быть меньше необходимого значения, а name содержать некорректные данные'; WSAEINPROGRESS: sFullErr:='Выполняется операция в блокирующем режиме. Вы уже запустили на выполнение какую-то функцию и нужно дождаться завершения её работы'; WSAEINVAL: sFullErr:='Сокет уже связан с адресом'; WSAENOBUFS: sFullErr:='Недостаточно буферов, слишком много соединений'; WSAENOTSOCK: sFullErr:='Неверный дескриптор сокета'; WSAEISCONN: sFullErr:='Сокет уже подключён'; WSAEMFILE: sFullErr:='Нет больше доступных дескрипторов'; end;
MessageBox(0, PChar('Ошибка в функции '+S+' - '+sFullErr), 'Ошибка', 0); end;
function TestFuncError(iErr:Integer; FuncName:String):Boolean; begin Result:=false; if iErr = SOCKET_ERROR then begin TestWinSockError(FuncName); Result:=true; end; end;
procedure TForm1.bStartServerClick(Sender: TObject); var wData : WSADATA; sServerListen, sClient : TSOCKET; localaddr, clientaddr : sockaddr_in; iSize : Integer; s1 : TCPClientThread; begin // Загрузка WinSock if WSAStartup(MAKEWORD(1,1), wData) <> 0 then begin MessageBox(0, 'Не могу загрузить WinSock', 'Ошибка', 0); exit; end;
// Создание сокета sServerListen := socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if sServerListen = INVALID_SOCKET then begin MessageBox(0, 'Ошибка создания сокета', 'Ошибка', 0); exit; end;
// Запонение структуры адреса localaddr.sin_addr.s_addr := htonl(INADDR_ANY); localaddr.sin_family := AF_INET; localaddr.sin_port := htons(5050);
//Связывание сокета с локальным адресом if bind(sServerListen, localaddr, sizeof(localaddr)) = SOCKET_ERROR then begin TestWinSockError('Bind'); exit; end;
//Прослушивание if TestFuncError(listen(sServerListen, 4), 'Listen') then exit;
MessageBox(0, 'Сервер запущен', 'Внимание!!!', 0); while (true) do begin iSize := sizeof(clientaddr);
//Приём нового соединения sClient := accept(sServerListen, @clientaddr, @iSize); if sClient = INVALID_SOCKET then begin TestWinSockError('accept'); break; end;
// Соединение принято, создаём поток s1:=TCPClientThread.Create(true); s1.Sock:=sClient; s1.Resume; end; closesocket(sServerListen); end;
end.
Пробывал сам сделать не выходит!
-
> не выходит
Что конкретно "не выходит" ?
-
> Что конкретно "не выходит" ?
Не выходит корректно передать драные с клиента на сервер и на оборот, допустим с клиента передаю строку "Hello" и чтобы на сервера она отобразилась в Edit'e, ну и обратная связь! (образный пример)
-
Вот кусочек кода с UDP протокола, работает. Но не могу перенести этот код в TCP протокол (в тот код что писал выше)!
iSize:=sizeof(clientaddr);
if recvfrom(sServerListen, sRecvStr, 255, 0,
clientaddr, iSize)<>SOCKET_ERROR then
Label1.Caption:=sRecvStr;
-
Навскидку ошибок не видно, но код нити TCPClientThread не приведён, может, там что не так. Только просьба - пользуйтесь кнопкой "код" справа от поля ввода, а то неоформленный листинг читать тяжело.
-
> chemelin (15.01.08 12:51) [3]
Что говорит отладчик ?
-
> пользуйтесь кнопкой "код" справа от поля ввода
Давно был на этом форуме, забыл за кнопку "код", извиняюсь. Делал вот так:
...
var
sRecvStr : array [0..255] of char;
...
sClient :=accept(sServerListen, @sRecvStr, @iSize);
if sClient = INVALID_SOCKET then
begin
TestWinSockError('accept');
end;
edit1.Text := sRecvStr;
...
После приёма данных, в edit1 выводит такой вот символ "|" заместь "get"
-
Функция accept() не предназначена для приема данных, для этого существует ф-ция recv()
-
> Функция accept()
а для чего она? Делаю фот так:
...
recv(sServerListen, buf, 1024, 0);
edit1.Text := sRecvStr;
...
в чем ошибка?
-
О, по идее разобрался. Когда сделаю что будет все работать как надо, выложу здесь кодинг, для примера другим!
-
> для чего она?
Для ожидания запросов на соединение и собственно установки соединения.
> Делаю фот так
Полную ерунду ты делаешь.
Справку по Winsock API ты вообще читал перед тем как лепить код ?
Там же черным по белому написано:
The Windows Sockets recv function receives data from a socket.
int recv (
SOCKET s, char FAR* buf, int len, int flags );
..
s
[in] A descriptor identifying a connected socket.
А ты что толкаешь первым параметром ? Ты туда толкаешь дескриптор слушающего сокета. Разницу между слушающим сокетом и сокетом, ассоциированным с успешно установленным соединением, осюсяешь ? Получается что не осюсяешь. Ну так вперед - читать документацию !
-
> выложу здесь кодинг
Не надо.
Твоему дурному примеру могут последовать неокрепшие умы твентинпрограммеров)
-
> Не надо.
Я видел что я неправильно был здесь написал
recv(sServerListen, buf, 1024, 0);
edit1.Text := sRecvStr;
не хотел снова лепить сообщение. Вот смотри:
...
var
sRecvBuff, sSendBuff : array [0..255] of char;
ret:Integer;
s:String;
begin
while(true) do
begin
ret := recv(sock, sRecvBuff, 1024, 0);
if (ret = 0) then
Continue
else
if (ret = SOCKET_ERROR) then
begin
MessageBox(0, 'Ошибка получения данных', 'Внимание!!!', 0);
exit;
end;
s:=sRecvBuff;
if s[Length(s)]=#10 then
s:=Copy(s, 1, Length(s)-2);
if s<>'get' then
continue;
sSendBuff:='Command get OK';
ret := send(sock, sSendBuff, sizeof(sSendBuff), 0);
if (ret = SOCKET_ERROR) then
begin
MessageBox(0, 'Ошибка передачи данных', 'Внимание!!!', 0);
break;
end;
end;
CloseSocket(sock);
...
-
> Вот смотри
Ну вижу.
И что ?
-
Всем спасибо за внимание! Можете тему закрывать!
-
Все же традиционный вопрос - готовые компоненты чем не угодили ?)
Только не надо бухтеть про "размер")
-
> Только не надо бухтеть про "размер")
ну в том то и дело что нужен маленький размер, с готовыми компонентами вопросов нет, с ними я уже работал воб-щем то сложности с ними нету. Ну и нужно затвердить знание WinAPI (хорошая вешь хоть и мороки хватает).
-
> в том то и дело что нужен маленький размер
Только не надо бухтеть про дискетку и про нищих)
> нужно затвердить знание WinAPI
Тут не затвердением пахнет - тут пахнет многократным штудированием от начала до конца)
Ну и , к слову, WinAPI <> WinsockAPI
-
ещё один вопрос! Когда сервер стоит на прослушивании, ожидания приёма информации, он как будто бы повисает и никаких действий выполнить нельзя, все из за цикла while (true) do это как то исправить
-
> Тут не затвердением пахнет - тут пахнет многократным штудированием > от начала до конца)
признаюсь, WinAPI учил изначально на C++ , но WinAPI на Delphi очень пож на WinAPI С++. И хватит меня критиковать!
-
> chemelin (15.01.08 15:38) [19]
> как то исправить
Оч просто - либо задействовать неблок.режим слушающего гнезда либо вынести "стояние на прослушивании" в отдельный кодовый поток.
> И хватит меня критиковать
Изволишь чтобы тебя хвалили за принципиальное нежелание читать документацию ?
-
> chemelin (15.01.08 15:38) [19] > ещё один вопрос! Когда сервер стоит на прослушивании, ожидания > приёма информации, он как будто бы повисает и никаких действий > выполнить нельзя, > все из за цикла while (true) do это как то исправить
Это не только из-за цикла while True do, это, в основном, из-за того, что функция accept по умолчанию работает в блокирующем режиме. Вариантов исправления много:
1. Использовать select с таймаутом и вызывать accept только если select показал готовность сокета
2. Перевести слушающий сокет в неблокирующий режим.
3. Используя WSAAsyncSelect, связать сокет с оконным сообщением и вызывать accept только по приходу этого сообщения.
4. Используя WSAEventSelect, связать сокет с событием и при необходимости проверять состояние этого события с помощью WSAWaitForMultipleEvents.
5. Использовать AcceptEx в режиме перекрытого ввода-вывода.
6. Делать accept в отдельной нити, которая больше ничем другим заниматься не будет - тогда её зависание всей остальной программе будет по барабану.
Ну и от цикла тоже (за исключением варианта 6), конечно, надо избавиться. Например, если вы выберете вариант 1 или 2, можно проверять по таймеру.
-
> Изволишь чтобы тебя хвалили за принципиальное нежелание > читать документацию ?
Учту!
> Григорьев Антон
Спасибо!
-
-
> 6. Делать accept в отдельной нити, которая больше ничем > другим заниматься не будет - тогда её зависание всей остальной > программе будет по барабану.
Покажи пожалуйста на примере я что-то не очень понял как делать accept в отдельной нити, статью прочитал. Извиняюсь! Буду очень благодарен!
-
> chemelin (15.01.08 17:19) [25]
И что же конкретно вам непонятно? С нитями работать вроде умеете...
-
Никак не могу убрать блокировку! (((( Пробую это, не помогает, точнее с значением "1", ошибка:
Arg:=1;
IOCtlSocket(sServerListen,FIONBIO,Arg);
-
Так вы в отдельную нить выносите или неблокирующий сокет делаете?
И какая же ошибка возникает?
-
Вот смотрите:
procedure crestserver;
var
wData : WSADATA;
sServerListen, sClient : TSOCKET;
localaddr, clientaddr : sockaddr_in;
iSize : Integer;
s1 : TCPClientThread;
begin
if WSAStartup(MAKEWORD(1,1), wData) <> 0 then
begin
MessageBox(0, 'Не могу загрузить WinSock', 'Ошибка', 0);
exit;
end;
sServerListen := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if sServerListen = INVALID_SOCKET then
begin
MessageBox(0, 'Ошибка создания сокета', 'Ошибка', 0);
exit;
end;
localaddr.sin_addr.s_addr := htonl(INADDR_ANY);
localaddr.sin_family := AF_INET;
localaddr.sin_port := htons(5050);
if bind(sServerListen, localaddr, sizeof(localaddr)) = SOCKET_ERROR then
begin
TestWinSockError('Bind');
exit;
end;
if TestFuncError(listen(sServerListen, 4), 'Listen') then
exit;
MessageBox(0, 'Сервер запущен', 'Внимание!!!', 0);
while (true) do
begin
iSize := sizeof(clientaddr);
sClient := accept(sServerListen, @clientaddr, @iSize);
if sClient = INVALID_SOCKET then
begin
TestWinSockError('accept');
break;
end;
s1:=TCPClientThread.Create(true);
s1.Sock:=sClient;
s1.Resume;
end;
closesocket(sServerListen);
end;
При таком создании сервера происходит повисание, я толком не понял что такое "нить". Куда и какой здесь нужно вставить строки кода что бы не происходило повисание?? Помогите, я просто с сокетами работал очень мало.
-
Нить - это то, что вы называете потоком. Я предпочитаю слово "нить", потому что поток - это не только thread, но и stream, возникает путаница. А код надо вставлять в тело специально созданной для этого нити. Вы же TCPClientThread сделали, значит, и с этим разберётесь.
-
> chemelin
Ты действительно идиот или оным прикидываешься ?)
-
> Ты действительно идиот или оным прикидываешься ?)
прикидываюсь
-
А смысл ?
-
Спасибо Вам за поддержку, уже все сделал, все работает, мне было лень читать документацию.
|