Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2009.03.15;
Скачать: [xml.tar.bz2];

Вниз

Помогите с winsocket в WinAPI   Найти похожие ветки 

 
chemelin   (2008-01-15 12:30) [0]

Нужно написать на winsocket без VCL компонентов клиент-сервер которые будут обмениваться данными, помогите пожалуйста, если можно исходник или ссылки на статьи или готовые варианты реализации, буду очень благодарен, малое представление имею о winsocket но не могу реализовать передачу данных!


 
chemelin   (2008-01-15 12:38) [1]

продолжение вопроса...
Вот у меня есть акая реализация сервер-клмент, подправьте что не так.
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.

Пробывал сам сделать не выходит!


 
Сергей М. ©   (2008-01-15 12:44) [2]


> не выходит


Что конкретно "не выходит" ?


 
chemelin   (2008-01-15 12:51) [3]


> Что конкретно "не выходит" ?

Не выходит корректно передать драные с клиента на сервер и на оборот, допустим с клиента передаю строку "Hello"  и чтобы на сервера она отобразилась в Edit"e, ну и обратная связь! (образный пример)


 
chemelin   (2008-01-15 12:54) [4]

Вот кусочек кода с UDP протокола, работает. Но не могу перенести этот код в TCP протокол (в тот код что писал выше)!

//Прослушивание
iSize:=sizeof(clientaddr);
if recvfrom(sServerListen, sRecvStr, 255, 0,
    clientaddr, iSize)<>SOCKET_ERROR then
 Label1.Caption:=sRecvStr;


 
Григорьев Антон ©   (2008-01-15 12:58) [5]

Навскидку ошибок не видно, но код нити TCPClientThread не приведён, может, там что не так. Только просьба - пользуйтесь кнопкой "код" справа от поля ввода, а то неоформленный листинг читать тяжело.


 
Сергей М. ©   (2008-01-15 13:05) [6]


> chemelin   (15.01.08 12:51) [3]


Что говорит отладчик ?


 
chemelin   (2008-01-15 13:09) [7]


> пользуйтесь кнопкой "код" справа от поля ввода

Давно был на этом форуме, забыл за кнопку "код", извиняюсь.

Делал вот так:

...
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"


 
Сергей М. ©   (2008-01-15 13:17) [8]

Функция accept() не предназначена для приема данных, для этого существует ф-ция recv()


 
chemelin   (2008-01-15 13:28) [9]


> Функция accept()

а для чего она?
Делаю фот так:

...
recv(sServerListen, buf, 1024, 0);
edit1.Text  := sRecvStr;
...

в чем ошибка?


 
chemelin   (2008-01-15 13:47) [10]

О, по идее разобрался. Когда сделаю что будет все работать как надо, выложу здесь кодинг, для примера другим!


 
Сергей М. ©   (2008-01-15 13:47) [11]


> для чего она?


Для ожидания запросов на соединение и собственно установки соединения.


> Делаю фот так


Полную ерунду ты делаешь.

Справку по 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.

А ты что толкаешь первым параметром ? Ты туда толкаешь дескриптор слушающего сокета.
Разницу между слушающим сокетом и сокетом, ассоциированным с успешно установленным соединением, осюсяешь ?
Получается что не осюсяешь.
Ну так вперед - читать документацию !


 
Сергей М. ©   (2008-01-15 13:49) [12]


> выложу здесь кодинг


Не надо.

Твоему дурному примеру могут последовать неокрепшие умы твентинпрограммеров)


 
chemelin   (2008-01-15 13:54) [13]


> Не надо.

Я видел что я неправильно был здесь написал

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);
...


 
Сергей М. ©   (2008-01-15 14:05) [14]


> Вот смотри


Ну вижу.

И что ?


 
chemelin   (2008-01-15 14:10) [15]

Всем спасибо за внимание! Можете тему закрывать!


 
Сергей М. ©   (2008-01-15 14:51) [16]

Все же традиционный вопрос - готовые компоненты чем не угодили ?)

Только не надо бухтеть про "размер")


 
chemelin   (2008-01-15 15:27) [17]


> Только не надо бухтеть про "размер")

ну в том то и дело что нужен маленький размер, с готовыми компонентами вопросов нет, с ними я уже работал воб-щем то сложности с ними нету. Ну и нужно затвердить знание WinAPI (хорошая вешь хоть и мороки хватает).


 
Сергей М. ©   (2008-01-15 15:32) [18]


> в том то и дело что нужен маленький размер


Только не надо бухтеть про дискетку и про нищих)


> нужно затвердить знание WinAPI


Тут не затвердением пахнет - тут пахнет многократным штудированием от начала до конца)

Ну и , к слову, WinAPI <> WinsockAPI


 
chemelin   (2008-01-15 15:38) [19]

ещё один вопрос! Когда сервер стоит на прослушивании, ожидания приёма информации, он как будто бы повисает и никаких действий выполнить нельзя,
все из за цикла while (true) do это как то исправить


 
chemelin   (2008-01-15 15:40) [20]


> Тут не затвердением пахнет - тут пахнет многократным штудированием
> от начала до конца)

признаюсь, WinAPI учил изначально на C++ , но WinAPI  на Delphi очень пож на WinAPI С++.
И хватит меня критиковать!


 
Сергей М. ©   (2008-01-15 16:04) [21]


> chemelin   (15.01.08 15:38) [19]


> как то исправить


Оч просто - либо задействовать неблок.режим слушающего гнезда либо вынести "стояние на прослушивании" в отдельный кодовый поток.


> И хватит меня критиковать


Изволишь чтобы тебя хвалили за принципиальное нежелание читать документацию ?


 
Григорьев Антон ©   (2008-01-15 16:10) [22]


> 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, можно проверять по таймеру.


 
chemelin   (2008-01-15 16:21) [23]


> Изволишь чтобы тебя хвалили за принципиальное нежелание
> читать документацию ?

Учту!


> Григорьев Антон

Спасибо!


 
Григорьев Антон ©   (2008-01-15 16:33) [24]


> chemelin   (15.01.08 12:30)  
> если можно исходник или ссылки на статьи

Кстати, а вот это видели?
http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1021
http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1060


 
chemelin   (2008-01-15 17:19) [25]


> 6. Делать accept в отдельной нити, которая больше ничем
> другим заниматься не будет - тогда её зависание всей остальной
> программе будет по барабану.

Покажи пожалуйста на примере я что-то не очень понял как делать accept в отдельной нити, статью прочитал. Извиняюсь! Буду очень благодарен!


 
Григорьев Антон ©   (2008-01-15 18:34) [26]


> chemelin   (15.01.08 17:19) [25]

И что же конкретно вам непонятно? С нитями работать вроде умеете...


 
chemelin   (2008-01-15 18:44) [27]

Никак не могу убрать блокировку! ((((
Пробую это, не помогает, точнее с значением "1", ошибка:

   Arg:=1;
 IOCtlSocket(sServerListen,FIONBIO,Arg);


 
Григорьев Антон ©   (2008-01-15 19:22) [28]

Так вы в отдельную нить выносите или неблокирующий сокет делаете?

И какая же ошибка возникает?


 
chemelin   (2008-01-15 19:28) [29]

Вот смотрите:

//Создание Сервера
procedure crestserver;
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;

При таком создании сервера происходит повисание, я толком не понял что такое "нить". Куда и какой здесь нужно вставить строки кода что бы не происходило повисание?? Помогите, я просто с сокетами работал очень мало.


 
Григорьев Антон ©   (2008-01-15 19:52) [30]

Нить - это то, что вы называете потоком. Я предпочитаю слово "нить", потому что поток - это не только thread, но и stream, возникает путаница. А код надо вставлять в тело специально созданной для этого нити. Вы же TCPClientThread сделали, значит, и с этим разберётесь.


 
Сергей М. ©   (2008-01-15 19:57) [31]


> chemelin


Ты действительно идиот или оным прикидываешься ?)


 
chemelin   (2008-01-15 20:36) [32]


> Ты действительно идиот или оным прикидываешься ?)

прикидываюсь


 
Сергей М. ©   (2008-01-15 21:18) [33]

А смысл ?


 
chemelin   (2008-01-15 21:26) [34]

Спасибо Вам за поддержку, уже все сделал, все работает, мне было лень читать документацию.



Страницы: 1 вся ветка

Форум: "Сети";
Текущий архив: 2009.03.15;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.56 MB
Время: 0.044 c
2-1233057509
mixmix
2009-01-27 14:58
2009.03.15
Установить курсор в TEdit


4-1206457103
Informer
2008-03-25 17:58
2009.03.15
Кнопка закрытия формы


2-1232649379
batya-x
2009-01-22 21:36
2009.03.15
Сглаженный текст


2-1232568829
F@T@L_Err0r
2009-01-21 23:13
2009.03.15
Перезвонить при обрыве связи через DSL


6-1200679020
ad_Wolf
2008-01-18 20:57
2009.03.15
Вопрос по Indy





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский