|
|
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Понадобилось написать TCP "прокси", чем-то аналогичный вот этой тулзе http://ws.apache.org/tcpmon/tcpmontutorial.html С сокетами знаком исключительно по форумам, поэтому решил заполнить этот пробел ограничившись для начала Java API. В качестве тестового варианта написал простейший насос данных в одну и другую сторону на plain IO. WTF#1 - метод InputStream.available() бесполезен и даже сломан для сокетов. Читаем JavaDoc Returns:an estimate of the number of bytes that can be read (or skipped over) from this input stream without blocking or 0 when it reaches the end of the input stream. На сокетах available будет изначально возвращать 0, если узел с другой стороны ничего не отправляет. Но это ещё не значит что поток завершен. WTF#2 Дальше хуже. read(), который должен вернуть -1 по окончании чтения тоже никогда -1 не возвращает. В общем случае никакого начала и конца у InputStream для сокетов нет. А использовать available для хоть какой-то эмуляции неблокируемости тоже не выйдет. Кое как заборол. Работает на браузере. Начал тестировать на боевом приложении, WTF#3 Клиент отправляет пакет данных. Он прочитан, перенаправлен на другой сервер. После этого мой серверный сокет снова пытается сделать read(). В теории он должен либо заблокироваться, либо вернуть -1. На практике чтение выкидывает исключение Connection reset! Но это пол беды. Помимо этого клиентская сторона закрывает свой сокет и на запись. Т.е. отклик сервера записать проигнорировав reset уже нельзя. WTF#4 - не смотря на connection reset и невозможность дальнейшей работы с клиентом со стороны сервера, никакие флаги у сокета не меняются. Всякие connected, closed и пр. находятся в том же состоянии что и в начале работы. Т.е. спросит клиента с сервера живо ли тот ещё никакой возможности не видно. Психанул. Переписал всё на NIO без селекторов. Т.е. тот же многопоточный подход что и в IO, только через NIO API. И, о чудо! Работает. Причем независимо от параметра configureBlocking(). Никаких проблем с чтением после первого пакета почему-то нет. Всё до окончания сессии обмена пакетами работает более менее. WTF#5 - я вообще смотрю на разные проекты и вижу что постоянные исключения в TCP на Java это вообще штатные ситуации. Имеет ли смысл избегать логирования stacktrace для повышения производительности? Ведь если разворачивать stacktrace на каждый пук при многочисленых соединениях и отсоединениях клиентов, то провал в производительности обеспечен. И напоследок вопрос про NIO - как лучше всего избегать холостых циклов чтения, когда данные ещё не отправленый второй стороной. Они почему-то возникают даже при configureBlocking(true). Перейти исключительно на селекторы? Варианты с Thread.sleep(n) как-то не улыбаются. Комментарии и нравоучения приветствуются. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 17:20:43 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Можно селектор использовать http://tutorials.jenkov.com/java-nio/selectors.html ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 17:54:49 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
ЛагманМожно селектор использовать http://tutorials.jenkov.com/java-nio/selectors.html Прочитано несколько раз. Спасибо. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 17:59:29 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Blazkowicz, относительно 1-3. На транспортном вы не можете знать сколько байт должен в итоге передать клиент, потому что это определяется протоколом приложения, поэтому для сокета у вас фактически только три состояния: в буфере есть данные, их нужно считать и отправить дальше, в буфере нет данных - нужно ждать когда появятся, сокет закрыт. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 18:03:07 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Андрей ПанфиловНа транспортном вы не можете знать сколько байт должен в итоге передать клиент, потому что это определяется протоколом приложения Правильно. Если можно привязаться к протоколу, то всё становится просто. Андрей ПанфиловПоэтому для сокета у вас фактически только три состояния: в буфере есть данные, их нужно считать и отправить дальше, в буфере нет данных - нужно ждать когда появятся, сокет закрыт. Именно. Проблема в том что нет какого-то однозначно способа определить состояния и поступить правильно в зависимости от этого в IO. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 18:06:13 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
BlazkowiczПроблема в том что нет какого-то однозначно способа определить состояния и поступить правильно в зависимости от этого в IO.Не понял проблемы.. д.б. что-то в духе: Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. по вкусу вставить sleep, т.е. на одно проксируемое соединение уходит один поток ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 18:14:18 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Спасибо за пример! Попробую на досуге в таком ключе. На первый взгляд, действительно, можно избежать вышеуказаных проблем таким способом. Я просто сходу в разных потоках это делал. Для NIO понял что лучше объединить. Для IO, вижу, вообще без объединения не обойтись. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 18:18:19 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Для файлового ввода-вывода имплементация IO должна импортировать в себя пакеты NIO. Как там с сокетами - ХЗ но странно всё это. Насчёт переписывания. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 19:31:19 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Не очень понял, что у вас за проблемы с available(). Вот пример, в котором все абсолютно четко работает: Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. Но это "Java из Java". Я легко допускаю, что клиенты/сервера, с которыми вы взаимодействуете могут рвать соединения некорректно. В любом случае, для вычитки никаких проблем быть не должно. Метод available() имеет лишь ограниченную применимость, и скедулить потоки, полагаясь на него, разумеется, нельзя. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 20:02:25 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
По NIO - да, лучше всего сидеть на селекторах. Один тред слушает slector.select(), потом диспатчит это в другие треды - это типичный паттерн для этого дела. Вот очень хороший туториал по NIO: http://rox-xmlrpc.sourceforge.net/niotut/ P.S.: Если често, не очень понял, как NIO может быть без селекторов ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 20:08:06 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
cdtyjvP.S.: Если често, не очень понял, как NIO может быть без селекторов Тот же IO, только проще, и без блокировки. http://tutorials.jenkov.com/java-nio/socket-channel.html ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 20:11:49 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
cdtyjvНе очень понял, что у вас за проблемы с available(). Вот пример, в котором все абсолютно четко работает: Я не знаю размеров пакетов. И не могу на них полагаться. Я полагался на то что метод read заблокируется если что или вернет -1. А ещё я к тому что SocketInputStream нарушает контракт InputStream. cdtyjvЯ легко допускаю, что клиенты/сервера, с которыми вы взаимодействуете могут рвать соединения некорректно. Так и есть. cdtyjvВ любом случае, для вычитки никаких проблем быть не должно. Вот чтение меня больше всего и озадачило. Как только я пытаюсь в IO читать больше чем длина пакета, то получаю исключение Connection reset, вместо ожидаемой блокировки или -1. И что самое удивительно что в блокирующем режиме через NIO такого не происходит. Я понимаю что частично это и косяк стороннего клиентского сокета. Но как-то слишком много сюрпризов по сравнению с ожиданиями из JavaDoc. cdtyjvМетод available() имеет лишь ограниченную применимость, и скедулить потоки, полагаясь на него, разумеется, нельзя. Скедулить потоки я на нем не собирался. Я думал как бы его вообще применить к моей задаче. Но вот до такого 14920507 не додумался, так как всё делал в 2х потоках а не в одном. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 20:20:18 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Blazkowicz, Приведите пример Вашего кода работы с IO. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 20:28:57 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
DoSOfRedRiverBlazkowicz, Приведите пример Вашего кода работы с IO. Две такие асинхронные таски запускаются с каждой стороны. Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 20:34:17 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
BlazkowiczСкедулить потоки я на нем не собирался. Я думал как бы его вообще применить к моей задаче. Но вот до такого 14920507 не додумался, так как всё делал в 2х потоках а не в одном.Настоятельно НЕ рекомендую использовать этот код, ибо это что-то сродни busy loop в многопоточном программировании. Если вы не хотите блокироваться, то смело берите селектор, и вперед. Там есть нормальный Selector.select(), с которым никаких Thread.sleep() и прочего *** не надо делать. Тупо один поток сидит на селекторе. Как только с сокетом происходит какое-то событие - он просыпается, и говорит вам, что это было. А вы в зависимости от этого переадресуете работу в другой поток. В общем, по ссылке, что я привел выше это очень хорошо расписано. Мне в свое время именно этот материал поставил голову на место по части NIO. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 21:05:52 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
авторДальше хуже. read(), который должен вернуть -1 по окончании чтения тоже никогда -1 не возвращает. -1 вернется только в случае, если другая сторона закрыла соединение пока мы висели на read (и все оповещения об этом успешно дошли). ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 21:17:43 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Андрей Панфилов, Спасибо огромное! Работает в лучшем виде! Код на много проще и ещё другуя бага выловилась после рефакторинга. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 21:19:09 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
cdtyjvНастоятельно НЕ рекомендую использовать этот код, ибо это что-то сродни busy loop в многопоточном программировании. Какой именно? cdtyjvЕсли вы не хотите блокироваться, то смело берите селектор, и вперед. Это я знаю. Я планировал блокироваться в первой итерации. cdtyjvТам есть нормальный Selector.select(), с которым никаких Thread.sleep() и прочего *** не надо делать. Я в курсе. Мануал прочитан. Примеры прочитаны. Thread.sleep() не планировал использовать в продакшне. Собственно это всё никак не отвечает на мои вопросы выше. Было интересно как сделать через IO. И почему именно так. cdtyjvТупо один поток сидит на селекторе. Как только с сокетом происходит какое-то событие - он просыпается, и говорит вам, что это было. А вы в зависимости от этого переадресуете работу в другой поток. На словах я это сам могу рассказывать кому угодно. :) Так и сделаю когда до нагрузки дойдёт. cdtyjvВ общем, по ссылке, что я привел выше это очень хорошо расписано. Мне в свое время именно этот материал поставил голову на место по части NIO. Спасибо прочту. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 21:24:30 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
schwa-1 вернется только в случае, если другая сторона закрыла соединение пока мы висели на read (и все оповещения об этом успешно дошли). Ок. А может мне кто на пальцах объяснить что такое Connection reset и как на это принято реагировать в TCP? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 21:25:17 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Андрей Панфилов Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. Блин. Рано радовался. Сессия обмена данных протекла успешно. Это супер. Но! Вылез косяк. После сессии оба сокета открыты. Соединение активно. Я не могу выйти из этого цикла! Ведь я не делаю блокирующего read(), который бы мог вернуть -1 или выкинуть исключение. Так что IO вариант, похоже, в моём случае окончательно сломан. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 21:30:14 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Blazkowicz, тут беда с isClosed() ровно как и с вашим inSocket.isInputShutdown() - чтобы получить на них true, нужно закрытие сокета инициировать в JVM. По факту жавская реализация сокета предполагает, что чтобы убедиться что сокет закрыт, нужно либо в него что-то записать (тут мы протокол ломаем), либо прочитать (тут зависнем на таймауте), как вариант можно вместо isClosed() ловить эксепшн на sendUrgentData() ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 21:39:29 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Андрей Панфиловлибо прочитать (тут зависнем на таймауте) Или отхватим исключение ещё до окончания записи. Андрей Панфиловкак вариант можно вместо isClosed() ловить эксепшн на sendUrgentData() Не вижу разницы с write. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 21:43:57 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Blazkowicz, Сразу оговорюсь, что могу в чём-то ошибаться. Ключевые моменты лучше проверить. Итак, WTF#1 Действительно, available возвращает количество байт, доступных для чтения. То-есть, если данные на вашу машину ещё не пришли, либо пришли не полностью, то available() вернёт 0. WTF#2 А какой, собсна, read() вы используете? У меня всё нормально работало, для read() . Советую вам использовать более удобное и эффективное чтение в массив read(byte [] arg), которое возвращает количество считанных байт. И да, авториспользовать available для хоть какой-то эмуляции неблокируемости тоже не выйдет WTF#3 По поводу Connetion reset вот что пишут. Сдаётся мне, проблемы конкретно у вас. WTF#4 Думается, вытекает из третьего. Всё должно нормально работать. WTF#5 Избегайте логгирования в местах, где ошибки предсказуемы и некритичны. Можете глянуть на то, как обрабатываются ошибки в Netty, там даже лисенеры специальные есть. По поводу NIO: Селекторы достаточно эффективная и удобная вещь, почему вы их избегаете? Вообще, классический IO давно устарел, он неудобен и непрактичен, потому писать лучше сразу с использованием NIO\NIO.2 Приведу здесь пример. Как по мне, в нём отражена вся суть работы IO, кроме, разве что, многопоточного взаимодействия. Пример не мой, кстати. Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. 189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203. 204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215. 216. 217. 218. 219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. 234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248. 249. 250. 251. 252. 253. 254. 255. 256. 257. 258. 259. 260. 261. 262. 263. 264. 265. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 21:44:24 |
|
||
|
IO/NIO WTF
|
|||
|---|---|---|---|
|
#18+
Blazkowicz, sendUrgentData данные не передает а шлет флаг, правда минут что поведение зависит от ОС. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 21:48:59 |
|
||
|
|

start [/forum/topic.php?fid=59&msg=38416155&tid=2128468]: |
0ms |
get settings: |
9ms |
get forum list: |
17ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
231ms |
get topic data: |
12ms |
get forum data: |
3ms |
get page messages: |
87ms |
get tp. blocked users: |
1ms |
| others: | 234ms |
| total: | 600ms |

| 0 / 0 |
