Я создаю сканер портов, чтобы проверить, открыт ли какой-либо порт TCP на удаленном компьютере.
Чтобы повысить производительность, я просто создаю и отправляю пакет TCP SYN на удаленный порт, а не делаю полный 3-способ рукопожатия. И если я получу пакет SYN-ACK успешно, то этот порт будет считаться открытым.
Вот часть моего кода:
conn, _:= net.Dial("ip4:tcp", target)
tcpSynPacket := BuildTcpSynPacket() // here I build a tcp syn packet
conn.Write(tcpSynPacket.Marshal())
deadlineTime := time.NewTicker(time.Second * 2)
defer deadlineTime.Stop()
for {
select {
case <-deadlineTime.C:
return nil
default:
bytes := make([]byte, 128)
conn.SetReadDeadline(time.Now().Add(time.Millisecond * 200))
readnum, err := conn.Read(bytes)
responsePacket := parstTCPHeader(bytes[:readnum])
matched := CHECK_IF_RESPONSE_MATCH_REQUEST(tcpSynPacket,responsePacket) // here I'll check if ack-no,src-port,dest-port in tcpSynPacket match seq-no,dest-port,src-port in response packet
if !matched {
// unmatched packets,may response for another routine
continue
}
if responsePacket.rst_flg == 1 {
// the port would be consider as close
// build func return struct,and return
....
}else {
// the port would be consider as open
// build func return struct,and return
....
}
}
Нет цикла forи оператор CHECK_IF_RESPONSE_MATCH_REQUEST в старом коде. Но когда я делаю стресс-тест, я обнаружил, что это необходимо.
Допустим, мы проверим, открыт ли порт 80 на 66.220.146.94. Я открываю 1000 процедур для вызова вышеуказанного кода.
goroutine1: ack-no=11111
goroutine2: ack-no=22222
goroutine2: ack-no=33333
...
Затем я обнаружил, что в каждой процедуре следующий оператор
readnum, err := conn.Read(bytes)
responsePacket := parstTCPHeader(bytes[:readnum])
будет читать весь ответный пакет, даже если пакет чтения не соответствует пакету syn, отправленному в текущей программе.
Например, на goroutine1 я отправил син пакет (ack-no = 11111) и прочитал из conn.Затем я обнаружил, что seq-no в пакете чтения может быть 11112,22223,33334 ... Поэтому я добавляю цикл и некоторую логику проверки в CHECK_IF_RESPONSE_MATCH_REQUEST.
Но логика чтения и проверки цикла делает процессор настолько высоким.
Вот результат теста (я запускаю его каждые 60 секунд)
Стоимость 10 лучших процессоров с использованием трехстороннего встряхивания, когдаopen 1000 goroutine (продолжительность: 60 с, общее количество выборок = 780 мс (1,30%)):
Топ-10 процессорных ресурсов с использованием сканирования по tpp syn port при открытии1000 рутин (продолжительность: 60 с, общее количество образцов = 30,51 с (50,85%)):
Что я хочу знать, это:
1.Почему conn.Read (байты) считывает все ответные пакеты в каждой процедуре? Является ли net.Dial ("ip4: tcp", targettip) правильным?
2.Есть более дешевый способвыполнять периодическое сканирование портов (каждые 60 секунд) без трехсторонней встряски