Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Общие вопросы > SMTP отправка почты через сокет. Цикл


Автор: BlackRu 4.7.2014, 16:01
Доброго дня. Изучаю код для отправки почты через сокеты.
Не могу понять, зачем в функции цикл. 
Так ли он необходим? 
Правильно ли я понимаю, что цикл тут нужен для ожидания ответа сервера? Т.е. на случай, если сервер ответит не сразу. 
Я задал тот же вопрос на том сайте, где нашел статью, два дня прошло, ответов нет.
Интересно, как работет fget. Изначальный код из статьи приведен ниже.

Могу ли я сделать так? Т.е. без цикла. 
Код


function ch_code(&$a, $smtp_conn) {

$a=fgets($smtp_conn,625);  
$a=substr($a,0,3);

}
$smtp_conn = fsockopen("smtp.rambler.ru", 25,$errno, $errstr, 10);
ch_code($a, $smtp_conn);
 
if(!$smtp_conn) {print "Connection Lost"; fclose($smtp_conn); exit;}
 
fputs($smtp_conn,"HELO rambler.ru\r\n"); 
ch_code($a, $smtp_conn);
 
 
fputs($smtp_conn,"AUTH LOGIN\r\n");
ch_code($a, $smtp_conn);

и т.д., и т.д.




Изначальный код из статьи: 
Код

<?php
function get_data($smtp_conn)
{
$data="";
while($str = fgets($smtp_conn,515)) 
{

$data .= $str;
if(substr($str,3,1) == " ") { break; }
}
return $data;
}

$header="Date: ".date("D, j M Y G:i:s")." +0700\r\n"; 
$header.="From: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode('Максим')))."?= <login@mail.ru>\r\n"; 
$header.="X-Mailer: The Bat! (v3.99.3) Professional\r\n"; 
$header.="Reply-To: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode('Максим')))."?= <login@mail.ru>\r\n";
$header.="X-Priority: 3 (Normal)\r\n";
$header.="Message-ID: <172562218.".date("YmjHis")."@mail.ru>\r\n";
$header.="To: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode('Сергей')))."?= <qwe@asd.ru>\r\n";
$header.="Subject: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode('проверка')))."?=\r\n";
$header.="MIME-Version: 1.0\r\n";
$header.="Content-Type: text/plain; charset=windows-1251\r\n";
$header.="Content-Transfer-Encoding: 8bit\r\n";

$text="привет, проверка связи.";

$smtp_conn = fsockopen("smtp.mail.ru", 25,$errno, $errstr, 10);
if(!$smtp_conn) {print "соединение с серверов не прошло"; fclose($smtp_conn); exit;}
$data = get_data($smtp_conn);
fputs($smtp_conn,"EHLO mail.ru\r\n");
$code = substr(get_data($smtp_conn),0,3);
if($code != 250) {print "ошибка приветсвия EHLO"; fclose($smtp_conn); exit;}
fputs($smtp_conn,"AUTH LOGIN\r\n");
$code = substr(get_data($smtp_conn),0,3);
if($code != 334) {print "сервер не разрешил начать авторизацию"; fclose($smtp_conn); exit;}

fputs($smtp_conn,base64_encode("login")."\r\n");
$code = substr(get_data($smtp_conn),0,3);
if($code != 334) {print "ошибка доступа к такому юзеру"; fclose($smtp_conn); exit;}


fputs($smtp_conn,base64_encode("password")."\r\n");
$code = substr(get_data($smtp_conn),0,3);
if($code != 235) {print "не правильный пароль"; fclose($smtp_conn); exit;}

fputs($smtp_conn,"MAIL FROM:login@mail.ru\r\n");
$code = substr(get_data($smtp_conn),0,3);
if($code != 250) {print "сервер отказал в команде MAIL FROM"; fclose($smtp_conn); exit;}

fputs($smtp_conn,"RCPT TO:qwe@asd.ru\r\n");
$code = substr(get_data($smtp_conn),0,3);
if($code != 250 AND $code != 251) {print "Сервер не принял команду RCPT TO"; fclose($smtp_conn); exit;}

fputs($smtp_conn,"DATA\r\n");
$code = substr(get_data($smtp_conn),0,3);
if($code != 354) {print "сервер не принял DATA"; fclose($smtp_conn); exit;}

fputs($smtp_conn,$header."\r\n".$text."\r\n.\r\n");
$code = substr(get_data($smtp_conn),0,3);
if($code != 250) {print "ошибка отправки письма"; fclose($smtp_conn); exit;}

fputs($smtp_conn,"QUIT\r\n");
fclose($smtp_conn);
?>



Извиняюсь за количество кода, могу сократить после ответа.
(Для практических целей использую Sendmail,  понять хочу для себя(в учебных целях).
 

Автор: polls 8.7.2014, 11:01
прошлое десятилетие,
отправляй почту через gmail из php
http://stackoverflow.com/questions/712392/send-email-using-the-gmail-smtp-server-from-a-php-pagehttp://ctrlq.org/code/19589-send-mail-php

в сутки с одного gmail акка можно отправить 500 писем, если ограничение не изменилось.

Автор: krundetz 8.7.2014, 16:29
BlackRu, цикл так как:
1. ответ может иметь разную длину, а в коде есть ограничение в 515 символов (очень странное кстати)
2. ответ может состоять из нескольких строк, а fgets работает пока не достигнут конец строки

как работает fgets можно почитать на http://www.php.net/manual/ru/function.fgets.php

Цитата(polls @  8.7.2014,  11:01 Найти цитируемый пост)
прошлое десятилетие,
отправляй почту через gmail из php

человек же ясно сказал
Цитата(BlackRu @  4.7.2014,  16:01 Найти цитируемый пост)
 понять хочу для себя(в учебных целях).


Цитата(polls @  8.7.2014,  11:01 Найти цитируемый пост)
в сутки с одного gmail акка можно отправить 500 писем, если ограничение не изменилось.

это смешная цифра. Вы не представляете себе объемы коммерческой корреспонденции. Только писем по одному вопросу с одним клиентом может быть десятки, а иногда и сотни в день. Вопросов может быть несколько да и в нормальной организации клиентов не один два, а еще не стоит забывать про сотрудников. Про отправку заказов с магазинов говорить?

polls, вам минус:
1. за некомпетентность, к вашему сведению для отправки почты GMAIL использует туже самую технологию, которую пытается понять BlackRu и которую вы называете прошлым десятилетием
2. за ответ не по теме,  BlackRu четко и ясно выразился для чего он задает этот вопрос
3. безапеляционность, BlackRu что у него отправка работает и через что.
4. за неправильную вставку ссылок, если уж настаиваете на своем решение, а слово "отправляй" я по другому расценить не могу, и приводите ссылки на материалы, то потрудитесь их вставить так чтобы человек смог ими воспользоваться.
5. общая невнимательность.

Все в совокупности дало минус. Если бы вы сформулировали свой ответ по другому. Например:
Цитата

Я использую gmail почему бы тебе его тоже не попробовать, узнать о нем можно http://stackoverflow.com/questions/712392/send-email-using-the-gmail-smtp-server-from-a-php-page и http://ctrlq.org/code/19589-send-mail-php, там правда есть ограничение в 500 писем с аккаунта.

в таком случае я бы списал ваш ответ просто на невнимательное изучение сообщения ТС и минуса бы не поставил.

Автор: _zorn_ 9.7.2014, 05:03
Цитата(krundetz @  9.7.2014,  00:29 Найти цитируемый пост)
BlackRu, цикл так как:
1. ответ может иметь разную длину, а в коде есть ограничение в 515 символов (очень странное кстати)
2. ответ может состоять из нескольких строк, а fgets работает пока не достигнут конец строки

Так то оно так, но там нужно 3 первых символа - поэтому особого смысла в цикле нет.
И да, можно заюзать fread вместо fgets, но здесь все равно без разницы.

И цикл там все равно не правильный. Что будет если встретится стока состоящая из одного символа "0" ?

Автор: krundetz 9.7.2014, 10:29
Цитата(_zorn_ @  9.7.2014,  05:03 Найти цитируемый пост)
Так то оно так, но там нужно 3 первых символа - поэтому особого смысла в цикле нет.

вот пример обмена сообщениями
Код

220 smtp25.mail.ru ESMTP ready
EHLO bob.examle.org
250-smtp25.mail.ru
250-SIZE 73400320
250-8BITMIME
250-PIPELINING
250-AUTH PLAIN LOGIN XOAUTH2
250 STARTTLS

а теперь вопрос, как быть если нужно узнать SIZE?

Цитата(_zorn_ @  9.7.2014,  05:03 Найти цитируемый пост)
И цикл там все равно не правильный. 

Там не цикл неправильный, а функция извлечения кода ответа, а если быть точнее то ошибка в проектирование, которая будет мешать расширяемости в дальнейшем.

Я сделал хотя бы так:
Код

function getResponse($socet) {
    $response = '';
    while($str = fgets($socet)) {
        $response .= $str;
    }
    return $response;
}
function getCode($response) {
    return substr($response, 0, 3);
}

так как могут понадобиться и другие данные из ответа.

Цитата(_zorn_ @  9.7.2014,  05:03 Найти цитируемый пост)
Что будет если встретится стока состоящая из одного символа "0" ?

Не совсем понял имеется в виду нуль символ или строка из нуля?

ИМХО, программист который создавал код, взятый за основу BlackRu, либо не до конца понимает что он делает и на чем, либо делал времянку для чего то. Вот поэтому и код такой же.

Автор: _zorn_ 10.7.2014, 05:29
Цитата(krundetz @  9.7.2014,  18:29 Найти цитируемый пост)
Не совсем понял имеется в виду нуль символ или строка из нуля?

Я имел ввиду что то типа такого
Код
afafwwf
gggw
0
this is the end
but we not get it

Понимаю что в данном случае такое вряд ли встретится, однако...
Код
while(($str = fgets($socet))!==false)


Автор: krundetz 10.7.2014, 10:35
_zorn_, нет, ваш вариант:
1. не обработает ситуацию когда в ответ возвращаются неправильные данные
2. не имеет смысла, так как fgets() всегда возвращает строку

Если надо обработать правильность входящих данных, то как то так:
Код

function isVaild($response) {
    rerurn preg_match_all('/(\d){3}(\s|-).*/', $response);
}

Не совсем уверен, что правильно будут обработаны несколько строк. Надо еще подумать.

Или можно пойти дальше извлекая только данные соответствующие шаблону:
Код

function getData($response) {
    $data = array();
    $matches = array();
    $count = preg_match_all('/(\d){3}(\s|-).*/', $response, $macthes);
    for($i = 0; $i < $count; $i++) {
        $data[$i]['code'] = $matches[$i][1];
        $data[$i]['message'] = $matches[$i][3];
    }
    return $data;
}

P.S. Данный код совсем не оптимален приведен как отправная точка для дальнейших размышлений.

Автор: BlackRu 10.7.2014, 23:11
Спасибо, 
krundetz, 
_zorn_

Уже не ожидал ответа, поэтому пропал.   
Ага, этот 4  (получается по счету) символ - пробел, по которому происходит выход из цикла, может оказаться в ответе сервера и не пробелом. Если я не ошибаюсь.

Автор: _zorn_ 11.7.2014, 02:50
Цитата(krundetz @  10.7.2014,  18:35 Найти цитируемый пост)
2. не имеет смысла, так как fgets() всегда возвращает строку

Вы забываете одну "приятную" особенность ПХП - динамические типы данных.

Код
<?php
if('0'==0) echo "OLOLO1\n";
if('0'===0) echo "OLOLO2\n";
if(0==false) echo "OLOLO3\n";
if('0'==false) echo "OLOLO4\n";
if('0'===false) echo "OLOLO5\n";

Код
OLOLO1
OLOLO3
OLOLO4


http://ideone.com/8svgZ2

Автор: BlackRu 11.7.2014, 08:54
Т.е. суммируя вышесказанное, если я правильно понимаю: 
  • Желательно получить всю строку, которая будут в ответе сервера?! 
  • Найти в этой строке числовой трехзначный код?! 
  • Проверить расположение кода относительно предыдущей и следующей подстроки?! Убедиться, что код не прикленн к другим данным.


Интересно, какова вероятность, что код ответа будет не вначале, а где-нибудь в середине или конце строки?

Т.е., получается, получив строку ответа, желательно проверить наличие в ней трехзначного кода? Если данные неправильные (по каким-то причинам), то следует повторить последний запрос?

 

Автор: krundetz 11.7.2014, 09:58
Цитата(BlackRu @  11.7.2014,  08:54 Найти цитируемый пост)
Желательно получить всю строку, которая будут в ответе сервера?! 

Да, желательно получать весь ответ сервера, который может состоять из нескольких строк. И только после этого с ним работать.
Цитата(BlackRu @  11.7.2014,  08:54 Найти цитируемый пост)
Найти в этой строке числовой трехзначный код?! 

Да, необходимо найти код ответа сервера который должен стоять в начале каждой строки.  От него и переданных на сервер данных будет зависеть дальнейшее поведение программы.
Цитата(BlackRu @  11.7.2014,  08:54 Найти цитируемый пост)
Проверить расположение кода относительно предыдущей и следующей подстроки?! Убедиться, что код не прикленн к другим данным.

Да.
Цитата(BlackRu @  11.7.2014,  08:54 Найти цитируемый пост)
Интересно, какова вероятность, что код ответа будет не вначале, а где-нибудь в середине или конце строки?

Вероятность должна стремиться к 0, но это не значит, что входящие данные не нужно проверять на корректность. Так как по идее имея собственный "почтовый сервер" с которым ваша программа будет общаться можно вести исследование на проникновение, на ваш сервер.
Цитата(BlackRu @  11.7.2014,  08:54 Найти цитируемый пост)
Если данные неправильные (по каким-то причинам), то следует повторить последний запрос?

Нет. Если ответ сервера не предусмотрен сценарием работы программы, то повтор запроса бессмыслен, следует просто прекратить обработку отправки этого письма на этот сервер и записать информацию об этом в лог.

Автор: krundetz 11.7.2014, 10:42
Цитата(_zorn_ @  11.7.2014,  02:50 Найти цитируемый пост)
Вы забываете одну "приятную" особенность ПХП - динамические типы данных.

да вы правы, не учел. smile  Следовательно меняем код на такой:
Код

function getResponse($socet) {
    $response = '';
    while(($str = fgets($socet)) !== false) {
        $response .= $str;
    }
    return $response;
}


Цитата(krundetz @  10.7.2014,  10:35 Найти цитируемый пост)
P.S. Данный код совсем не оптимален приведен как отправная точка для дальнейших размышлений.

Понял несколько моментов который меня смущали.

1. В моём коде нет проверки на начало строки поэтому такая строка:
Код

dfg250 bad string

тоже подпадет под него. Хотя на нее надо выводить ошибку.

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

250-smtp25.mail.ru
bad string
250 STARTTLS

Автор: krundetz 11.7.2014, 12:42
немного допилил getData()
Код

function getData($response) {
    $data = array();
    $matches = array();
    $count = preg_match_all('/(\d{3,})(\s|-)(.*)/', $response, $matches, PREG_PATTERN_ORDER);
    for($i = 0; $i < $count; $i++) {
        $data[$i]['code'] = $matches[1][$i];
        $data[$i]['message'] = $matches[3][$i];
    }
    return $data;
}


http://ideone.com/Phqy1b

Автор: BlackRu 11.7.2014, 14:05
krundetz, спасибо огромное за подробные ответы и пример кода. Начинаю вникать. 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)