CHƯƠNG I: SOCKET FOR CLIENTS Dữ liệu được truyền qua Internet trong các gói có kích thước hữu hạn được gọi là datagrams. Mỗi datagram chứa một tiêu đề và một tải trọng. Tiêu đề chứa địa chỉ và cổng mà gói tin đang đi, địa chỉ và cổng mà gói tin đến, séc để phát hiện tham nhũng dữ liệu và nhiều thông tin vệ sinh khác được sử dụng để đảm bảo truyền tải đáng tin cậy. Tải trọng chứa chính dữ liệu.
Tuy nhiên, vì datagram có độ dài hữu hạn, nên thường cần phải chia dữ liệu trên nhiều gói và lắp ráp lại tại điểm đến. Cũng có thể một hoặc nhiều gói có thể bị mất hoặc bị hỏng trong quá trình vận chuyển và cần phải được truyền lại hoặc các gói đến ngoài trật tự và cần phải được sắp xếp lại. Theo dõi điều này - chia dữ liệu thành các gói, tạo tiêu đề, phân tích tiêu đề của các gói đến, theo dõi những gói nào đã và chưa được nhận, v. - là rất nhiều công việc và đòi hỏi rất nhiều mã phức tạp.
May mắn thay, bạn không cần phải tự mình thực hiện công việc. Socket cho phép lập trình viên xử lý kết nối mạng chỉ là một luồng khác mà byte có thể được viết và từ đó có thể đọc byte. Socket bảo vệ lập trình viên khỏi các chi tiết cấp thấp của mạng, chẳng hạn như phát hiện lỗi, kích thước gói, tách gói, truyền lại gói, địa chỉ mạng và hơn thế nữa.1 Sử dụng socket Socket là một kết nối giữa hai máy chủ. Nó có thể thực hiện bảy thao tác cơ bản: • Kết nối với máy từ xa • Gửi dữ liệu • Nhận dữ liệu • Đóng kết nối • Liên kết với cổng • Lắng nghe dữ liệu đến • Chấp nhận kết nối từ các máy từ xa trên cổng ràng buộc Lớp Socket của Java , được sử dụng bởi cả khách hàng và máy chủ, có các phương pháp tương ứng với bốn hoạt động đầu tiên trong số này.
Ba thao tác cuối cùng chỉ cần thiết bởi các máy chủ, chờ khách hàng kết nối với họ. Chúng được thực hiện bởi lớp ServerSocket, được thảo luận trong chương tiếp theo. Các chương trình Java thường sử dụng socket máy khách theo cách sau: • Chương trình tạo ra một socket mới với một constructor. • Socket cố gắng kết nối với máy chủ từ xa.
Khi kết nối được thiết lập, các máy chủ cục bộ và từ xa sẽ nhận được các luồng đầu vào và đầu ra từ socket và sử dụng các luồng đó để gửi dữ liệu cho nhau. Kết nối này là full-duplex. Cả hai máy chủ có thể gửi và nhận dữ liệu cùng một lúc. Dữ liệu có nghĩa là gì phụ thuộc vào giao thức; các lệnh khác nhau được gửi đến máy chủ FTP so với máy chủ HTTP.
Thông thường sẽ có một số cái bắt tay được thỏa thuận, tiếp theo là việc truyền dữ liệu từ cái này sang cái khác. Khi việc truyền dữ liệu hoàn tất, một hoặc cả hai bên đóng kết nối. Một số giao thức, chẳng hạn như HTTP 1.0, yêu cầu kết nối phải được đóng sau mỗi yêu cầu được phục vụ. Những người khác, chẳng hạn như FTP và HTTP 1.1, cho phép nhiều yêu cầu được xử lý trong một kết nối duy nhất.1 Điều tra giao thức với Telnet Trong chương này, bạn sẽ thấy khách hàng sử dụng socket để giao tiếp với một số dịch vụ Internet được biết đến như thời gian, sắc lệnh và hơn thế nữa.
Bản thân các socket đủ đơn giản; tuy nhiên, các giao thức để giao tiếp với các máy chủ khác nhau làm cho cuộc sống trở nên phức tạp. Để có được cảm giác về cách một giao thức hoạt động, bạn có thể sử dụng Telnet để kết nối với máy chủ, nhập các lệnh khác nhau cho nó và xem phản hồi của nó. Theo mặc định, Telnet cố gắng kết nối với cổng 23. Để kết nối với máy chủ trên các cổng khác nhau, hãy chỉ định cổng bạn muốn kết nối như sau: $ telnet localhost 25 Điều này yêu cầu kết nối với cổng 25, cổng SMTP, trên máy cục bộ; SMTP là giao thức được sử dụng để chuyển email giữa các máy chủ hoặc giữa máy khách thư và máy chủ.
Nếu bạn biết các lệnh để tương tác với máy chủ SMTP, bạn có thể gửi email mà không cần thông qua chương trình thư. Thủ thuật này có thể được sử dụng để giả mạo email. Ví dụ, một vài năm trước, các sinh viên mùa hè tại Đài quan sát năng lượng mặt trời quốc gia ở Sunspot, New Mexico, đã làm cho nó xuất hiện rằng bữa tiệc mà một trong những nhà khoa học đã ném sau trận đấu bóng chuyền hàng năm giữa các nhân viên và các sinh viên trên thực tế là một bữa tiệc chiến thắng cho các sinh viên. (Tất nhiên, tác giả của cuốn sách này hoàn toàn không liên quan gì đến hành vi đê hèn như vậy.;-) ) Sự tương tác với máy chủ SMTP đã diễn ra như thế này; nhập các loại người dùng được hiển thị in đậm (tên đã được thay đổi để bảo vệ người cả tin): Một số nhân viên đã hỏi Bart tại sao anh ta, một nhân viên, lại tổ chức một bữa tiệc chiến thắng cho các sinh viên.
Đạo đức của câu chuyện này là bạn không bao giờ nên tin tưởng email, đặc biệt là email vô lý như thế này, mà không có xác minh độc lập. Trong 20 năm kể từ khi điều này xảy ra, hầu hết các máy chủ SMTP đã thêm một chút bảo mật hơn so với hiển thị ở đây. Họ có xu hướng yêu cầu tên người dùng và mật khẩu, và chỉ chấp nhận kết nối từ khách hàng trong các mạng cục bộ và các máy chủ thư đáng tin cậy khác. Tuy nhiên, bạn vẫn có thể sử dụng Telnet để mô phỏng máy khách, xem máy khách và máy chủ tương tác như thế nào và do đó tìm hiểu xem chương trình Java của bạn cần làm gì.
Mặc dù phiên này không chứng minh tất cả các tính năng của giao thức SMTP, nhưng nó đủ để cho phép bạn suy luận cách một ứng dụng email đơn giản nói chuyện với máy chủ.2 Đọc từ máy chủ có socket Hãy bắt đầu với một ví dụ đơn giản. Bạn sẽ kết nối với máy chủ ban ngày tại Viện Tiêu chuẩn và Công nghệ Quốc gia (NIST) và yêu cầu nó cho thời điểm hiện tại. Giao thức này được định nghĩa trong RFC 867. Đọc điều đó, bạn thấy rằng máy chủ ban ngày nghe trên cổng 13 và máy chủ gửi thời gian ở định dạng có thể đọc được của con người và đóng kết nối.
Bạn có thể kiểm tra máy chủ ban ngày với Telnet như thế này: Dòng "56375 13-03-24 13:37:50 50 0 888.8 UTC (NIST)" được gửi bởi máy chủ ban ngày. Khi bạn đọc InputStream của Socket, đây là những gì bạn sẽ nhận được. Các dòng khác được sản xuất bởi vỏ Unix hoặc bởi chương trình Telnet. RFC 867 không chỉ định bất kỳ định dạng cụ thể nào cho đầu ra ngoài việc nó có thể đọc được của con người.
Trong trường hợp này, bạn có thể thấy kết nối này được thực hiện vào ngày 24 tháng 3 năm 2013, lúc 1:37: 50 P. Cụ thể hơn, định dạng được định nghĩa là JJJJJ YY-MM-DD HH:MM:SS TT L H msADV UTC(NIST) OTM trong đó: • JJJJJ là "Ngày Julian sửa đổi" (tức là, đó là số ngày kể từ nửa đêm ngày 17 tháng 11 năm 1858). • YY-MM-DD là hai chữ số cuối cùng của năm, tháng và ngày hiện tại của tháng. • HH: MM: SS là thời gian tính bằng giờ, phút và giây trong Giờ phối hợp toàn cầu (UTC, về cơ bản là Giờ trung bình Greenwich).
• TT cho biết liệu Hoa Kỳ hiện đang tuân thủ giờ chuẩn hay giờ tiết kiệm ánh sáng ban ngày: 00 có nghĩa là thời gian tiêu chuẩn; 50 có nghĩa là thời gian tiết kiệm ánh sáng ban ngày. Các giá trị khác đếm ngược số ngày cho đến khi chuyển đổi. • L là một mã có một chữ số cho biết liệu một giây nhuận sẽ được thêm hoặc trừ vào nửa đêm vào ngày cuối cùng của tháng hiện tại: 0 cho không có giây nhuận, 1 để thêm giây nhuận và 2 để trừ đi một giây nhuận. • H đại diện cho sức khỏe của máy chủ: 0 có nghĩa là khỏe mạnh, 1 có nghĩa là lên đến 5 giây tắt, 2 có nghĩa là hơn 5 giây tắt, 3 có nghĩa là một số lượng không chính xác không xác định, và 4 là chế độ bảo trì.
• msADV là một số mili giây mà NIST thêm vào thời gian nó gửi để bù đắp cho sự chậm trễ của mạng. Trong mã trước, bạn có thể thấy rằng nó đã thêm 888,8 mili giây vào kết quả này, bởi vì đó là thời gian nó ước tính sẽ mất để phản hồi trở lại. Những chi tiết này đều là NIST cụ thể. Chúng không phải là một phần của tiêu chuẩn ban ngày.
Mặc dù chúng cung cấp rất nhiều dữ liệu, nhưng nếu bạn có nhu cầu lập trình thực sự để đồng bộ hóa với máy chủ thời gian mạng, bạn nên sử dụng giao thức NTP được xác định trong RFC 5905 thay thế. Tôi không chắc ví dụ này sẽ hoạt động trong bao lâu như được hiển thị ở đây. Các máy chủ này bị quá tải và tôi đã gặp sự cố liên tục khi kết nối trong khi viết chương này. Vào đầu năm 2013, NISTđã đưa ra một danh từ, "Người dùng giao thức NIST DAYTIME trên cổng TCP 13 cũng được khuyến khích mạnh mẽ để nâng cấp lên giao thức thời gian mạng, cung cấp độ chính xác cao hơn và yêu cầu băng thông mạng ít hơn.
Máy khách thời gian NIST (nistime-32bit.exe) hỗ trợ cả hai giao thức. Chúng tôi hy vọng sẽ thay thế phiên bản tcp của giao thức này bằng một phiên bản udpbased gần cuối năm 2013". Tôi sẽ chỉ cho bạn cách truy cập dịch vụ này qua UDP trong Chương 11. Bây giờ chúng ta hãy xem làm thế nào để truy xuất dữ liệu tương tự này theo chương trình bằng cách sử dụng socket.
Đầu tiên, mở socket để time.gov trên cổng 13: Điều này không chỉ tạo ra đối tượng. Nó thực sự làm cho kết nối trên mạng. Nếu kết nối hết thời gian hoặc thất bại vì máy chủ không nghe trên cổng 13, thì người xây dựng sẽ ném IOException, vì vậy bạn thường sẽ bọc điều này trong một khối thử. Trong Java 7, Socket triển khai Autocloseable để bạn có thể sử dụng try-with- resources: Trong Java 6 trở về trước, bạn sẽ muốn đóng rõ ràng socket trong một khối cuối cùng để giải phóng tài nguyên mà socket nắm giữ: //ignore } } } Bước tiếp theo là tùy chọn nhưng rất được khuyến khích.