Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2009.03.15;
Скачать: CL | DM;

Вниз

Помогите с 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;
Скачать: CL | DM;

Наверх




Память: 0.57 MB
Время: 0.016 c
15-1231919578
Анна
2009-01-14 10:52
2009.03.15
Отсутствие палитры компонентов в Delphi7


15-1232060933
Григорий
2009-01-16 02:08
2009.03.15
Вопрос: Передача данных приложению


2-1232454342
AnatoliyV
2009-01-20 15:25
2009.03.15
HTML в RES файл


15-1231581021
SAVA
2009-01-10 12:50
2009.03.15
Файл.quz


2-1232712216
ников
2009-01-23 15:03
2009.03.15
проверка наличияя шрифта