Текущий архив: 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.56 MB
Время: 0.031 c