1. Trang chủ
  2. » Giáo Dục - Đào Tạo

BÁO cáo CHI TIẾT lập TRÌNH MẠNG đề tài SOCKET FOR CLIENTS

90 1 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề SOCKET FOR CLIENTS
Tác giả Nguyễn Thị Thùy Linh, Hoàng Trung Công, Nguyễn Văn An, Nguyễn Mạnh Thắng
Người hướng dẫn ThS. Nguyễn Mạnh Hùng
Trường học Trường Đại Học Kiến Trúc Hà Nội
Chuyên ngành Công Nghệ Thông Tin
Thể loại Báo cáo Chi Tiết
Năm xuất bản 2021
Thành phố Hà Nội
Định dạng
Số trang 90
Dung lượng 2,11 MB

Cấu trúc

  • CHƯƠNG I: SOCKET FOR CLIENTS

    • 1.1 Sử dụng socket

      • 1.1.1 Điều tra giao thức với Telnet

      • 1.1.2 Đọc từ máy chủ có socket

      • 1.1.3 Viết vào máy chủ có socket

    • 1.2 Xây dựng và kết nối socket

      • 1.2.1 Nhà xây dựng cơ bản

      • 1.2.2 Xây dựng mà không cần kết nối

      • 1.2.3 Địa chỉ socket

      • 1.2.4 Máy chủ Proxy

    • 1.3 Nhận thông tin về socket

      • 1.3.1 Đã đóng hoặc Đã kết nối?

    • 1.4 Tùy chọn cài đặt socket

    • 1.5 Ngoại lệ socket

    • 1.6 Socket trong ứng dụng GUI

      • 1.6.1 Whois

      • 1.6.2 A netwworl client library

  • CHƯƠNG II: SOCKET FOR SERVERS

    • 2.1 Sử dụng ServerSockets

      • 2.1.1 Phục vụ dữ liệu nhị phân

      • 2.1.2 Máy chủ đa luồng

      • 2.1.3 Viết vào máy chủ có socket

      • 2.1.4 Đóng socket Máy chủ

    • 2.2 Đăng nhập

      • 2.2.1 What to log

      • 2.2.2 Cách đăng nhập

    • 2.3 Xây dựng server socket

      • 2.3.1 Xây dựng mà không có ràng buộc

    • 2.4 Lấy thông tin về server socket

    • 2.5 Tùy chọn socket

      • 2.5.1 SO_TIMEOUT

      • 2.5.2 SO_REUSEADDR

      • 2.5.3 SO_RCVBUF

      • 2.5.4 Lớp dịch vụ

    • 2.6 Máy chủ HTTP

      • 2.6.1 Máy chủ Một Tệp

      • 2.6.2 Bộ chuyển hướng

      • 2.6.3 Máy chủ HTTP chính thức

Nội dung

SOCKET FOR CLIENTS

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

• 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.1.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ủ.

1.1.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.M., Greenwich Meantime 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.

• Chuỗi UTC (NIST) là một hằng số, và OTM gần như là một hằng số (một dấu hoa thị trừ khi có điều gì đó thực sự kỳ lạ đã xảy ra).

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

Xây dựng và kết nối socket

Lớp java.net.Socket là lớp cơ bản của Java để thực hiện các hoạt động TCP phía máy khách Các lớp hướng đến khách hàng khác tạo ra các kết nối mạng TCP như URL, URLConnection, Applet và JEditorPane cuối cùng đều kết thúc bằng cách gọi các phương pháp của lớp này Bản thân lớp này sử dụng mã gốc để giao tiếp với ngăn xếp TCP cục bộ của hệ điều hành máy chủ.

1.2.1 Nhà xây dựng cơ bản

Mỗi trình xây dựng Socket chỉ định máy chủ và cổng để kết nối Máy chủ có thể được chỉ định là InetAddress hoặc String Cổng từ xa được chỉ định là giá trị int từ 1 đến 65535: public Socket(String host, int port) throws UnknownHostException, IOException public Socket(InetAddress host, int port) throws IOException

Các nhà xây dựng này kết nối socket (tức là, trước khi trình xây dựng trở lại, kết nối mạng đang hoạt động được thiết lập với máy chủ từ xa) Nếu kết nối không thể được mở vì một lý do nào đó, người xây dựng sẽ ném IOException hoặc tion UnknownHostExcep. Chẳng hạn:

Trong trình xây dựng này, đối số máy chủ chỉ là một tên máy chủ được thể hiện dưới dạng Chuỗi Nếu máy chủ tên miền không thể giải quyết tên máy chủ hoặc không hoạt động, người xây dựng sẽ ném UnknownHostException Nếu socket không thể được mở vì một số lý do khác, người xây dựng ném một IOException Có nhiều lý do khiến nỗ lực kết nối có thể thất bại: máy chủ mà bạn đang cố gắng tiếp cận có thể không chấp nhận kết nối trên cổng đó, dịch vụ WiFi của khách sạn có thể chặn bạn cho đến khi bạn đăng nhập vào trang web của nó và trả 14,95 đô la hoặc các vấn đề định tuyến có thể ngăn các gói của bạn đến đích.

Bởi vì trình xây dựng này không chỉ tạo ra một đối tượng Socket mà còn cố gắng kết nối socket với máy chủ từ xa, bạn có thể sử dụng đối tượng để xác định xem các kết nối đến một cổng cụ thể có được phép hay không, như trong Ví dụ 8-5.

Ví dụ 8-5 Tìm hiểu cổng nào trong số 1024 cổng đầu tiên dường như đang lưu trữ máy chủ TCP trên một máy chủ được chỉ định Đây là đầu ra mà chương trình này tạo ra trên máy chủ địa phương của tôi (kết quả của bạn sẽ khác nhau, tùy thuộc vào cổng nào bị chiếm đóng):

Nếu bạn tò mò về những máy chủ nào đang chạy trên các cổng này, hãy thử thử nghiệm với Telnet Trên hệ thống Unix, bạn có thể tìm ra dịch vụ nào nằm trên cổng nào bằng cách tìm kiếm trong tệp / vv / dịch vụ Nếu LowPortScanner tìm thấy bất kỳ cổng nào đang chạy máy chủ nhưng không được liệt kê trong / etc / dịch vụ, thì điều đó thật thú vị.

Mặc dù chương trình này trông đơn giản, nhưng nó không phải là không có công dụng của nó Bước đầu tiên để đảm bảo một hệ thống là hiểu nó Chương trình này giúp bạn hiểu những gì hệ thống của bạn đang làm để bạn có thể tìm thấy (và đóng) các điểm vào có thể cho kẻ tấn công Bạn cũng có thể tìm thấy các máy chủ giả mạo: ví dụ, LowPortScanner có thể cho bạn biết rằng có một máy chủ trên cổng 800, trong đó, khi điều tra thêm, hóa ra là một máy chủ HTTP mà ai đó đang chạy để phục vụ các tệp MP3 và đang bão hòa T1 của bạn.

Ba nhà xây dựng tạo ra các socket không được kết nối Chúng cung cấp nhiều quyền kiểm soát hơn đối với chính xác cách socket cơ bản hoạt động, ví dụ bằng cách chọn một máy chủ proxy khác hoặc sơ đồ mã hóa:

1.2.2 Xây dựng mà không cần kết nối

Tất cả các nhà xây dựng mà chúng ta đã nói đến cho đến nay đều tạo ra đối tượng socket và mở kết nối mạng với máy chủ từ xa Đôi khi bạn muốn chia nhỏ các hoạt động đó Nếu bạn không đưa ra đối số cho trình xây dựng Socket, nó không có nơi nào để kết nối với: Public socket ()

Bạn có thể kết nối sau đó bằng cách chuyển SocketAddress đến một trong các phương pháp kết nối () Chẳng hạn:

Bạn có thể vượt qua một int là đối số thứ hai để chỉ định số mili giây để chờ trước khi kết nối hết thời gian: public void connect (SocketAddress endpoint, int timeout) throws IOException

Mặc định, 0, có nghĩa là chờ đợi mãi mãi.

Raison d'être cho nhà xây dựng này là để cho phép các loại socket khác nhau Bạn cũng cần sử dụng nó để thiết lập một tùy chọn socket chỉ có thể được thay đổi trước khi socket kết nối Tôi sẽ thảo luận về điều này trong "Thiết lập tùy chọn socket" trên trang 259 Tuy nhiên, lợi ích chính mà tôi tìm thấy là nó cho phép tôi dọn dẹp mã trong các khối cuối cùng, đặc biệt là trước Java 7 Các noargs constructor ném không có ngoại lệ vì vậy nó cho phép bạn tránh kiểm tra null gây phiền nhiễu khi đóng một socket trong một khối cuối cùng Với trình xây dựng ban đầu, hầu hết các mã trông như thế này:

Với nhà xây dựng mái vòm, nó trông như thế này: Điều đó không hoàn toàn tốt đẹp như phiên bản autoclosing trong Java 7, nhưng nó là một cải tiến.

Lớp SocketAddress đại diện cho một điểm cuối kết nối Nó là một lớp trừu tượng trống rỗng không có phương pháp nào ngoài một trình xây dựng mặc định Ít nhất về mặt lý thuyết, lớp SocketAddress có thể được sử dụng cho cả socket TCP và không TCP Trong thực tế, chỉ có socket TCP / IP hiện đang được hỗ trợ và các địa chỉ socket bạn thực sự sử dụng là tất cả các trường hợp của InetSocketAddress.

Mục đích chính của lớp SocketAddress là cung cấp một cửa hàng thuận tiện cho thông tin kết nối socket thoáng qua như địa chỉ IP và cổng có thể được tái sử dụng để tạo socket mới, ngay cả sau khi socket ban đầu bị ngắt kết nối và thu gom rác Để đạt được điều này, lớp Socket cung cấp hai phương pháp trả về các đối tượng SocketAddress (getRemoteSocketAddress() trả về địa chỉ của hệ thống được kết nối và getLocalSocketAddress() trả về địa chỉ mà từ đó kết nối được thực hiện):

Nhận thông tin về socket

Các đối tượng socket có một số thuộc tính có thể truy cập thông qua các phương pháp getter:

Dưới đây là các phương pháp getter để truy cập các thuộc tính này:

Không có phương pháp setter Các thuộc tính này được thiết lập ngay khi socket kết nối và được cố định từ đó trở đi.

Các phương thức getInetAddress() và getPort() cho bạn biết máy chủ từ xa và cổng socket được kết nối; hoặc, nếu kết nối hiện đã đóng, máy chủ và cổng nào socket được kết nối khi nó được kết nối. Các phương pháp getLocalAddress() và getLo calPort() cho bạn biết giao diện mạng và cổng mà Socket được kết nối từ đó.

Không giống như cổng từ xa, (đối với socket khách hàng) thường là một "cổng nổi tiếng" đã được ký trước bởi một ủy ban tiêu chuẩn, cổng địa phương thường được hệ thống lựa chọn vào thời gian chạy từ các cổng không sử dụng có sẵn Bằng cách này, nhiều khách hàng khác nhau trên một hệ thống có thể truy cập cùng một dịch vụ cùng một lúc Cổng cục bộ được nhúng trong các gói IP ra nước ngoài cùng với địa chỉ IP của máy chủ cục bộ, vì vậy máy chủ có thể gửi dữ liệu trở lại cổng bên phải trên máy khách.

Ví dụ 8-6 đọc một danh sách các tên máy chủ từ dòng lệnh, cố gắng mở một socket cho mỗi cái, và sau đó sử dụng bốn phương pháp này để in máy chủ từ xa, cổng từ xa, địa chỉ địa phương và cổng địa phương.

Ví dụ 8-6 Lấy thông tin của socket Đây là kết quả của một cuộc chạy mẫu Tôi đã bao gồm www.oreilly.com trên đường dây chỉ huy hai lần để chứng minh rằng mỗi kết nối được gán một cổng địa phương khác nhau, bất kể máy chủ từ xa; cổng cục bộ được gán cho bất kỳ kết nối nào là không thể đoán trước và phụ thuộc chủ yếu vào những cổng khác đang được sử dụng Kết nối với login.ibiblio.org thất bại vì máy đó không chạy bất kỳ máy chủ nào trên cổng 80:

1.3.1 Đã đóng hoặc Đã kết nối?

Phương pháp isClosed() trả về đúng nếu socket được đóng, sai nếu không Nếu bạn không chắc chắn về trạng thái của socket, bạn có thể kiểm tra nó bằng phương pháp này thay vì mạo hiểm ioexception. Chẳng hạn: if (socket isClosed()) { // do something

Tuy nhiên, đây không phải là một bài kiểm tra hoàn hảo Nếu socket chưa bao giờ được kết nối ngay từ đầu, isClosed () trả về sai, mặc dù socket không chính xác mở.

Lớp Socket cũng có phương pháp isConnected() Cái tên hơi gây hiểu lầm Nó không cho bạn biết nếu socket hiện đang được kết nối với một máy chủ từ xa (như nếu nó là UNCLOSED) Thay vào đó, nó cho bạn biết liệu socket đã từng được kết nối với máy chủ từ xa hay chưa Nếu socket có thể kết nối với máy chủ từ xa, phương pháp này sẽ trở thành sự thật, ngay cả sau khi socket đó đã được đóng lại. Để biết nếu một socket hiện đang mở, bạn cần kiểm tra xem isConnected () trả về đúng và isClosed() trả về sai Chẳng hạn: boolean connected = socket.isConnected() && ! socket.isClosed();

Cuối cùng, phương pháp isBound () cho bạn biết liệu socket có bị ràng buộc thành công với cổng đi trên hệ thống địa phương hay không Trong khi isConnected() đề cập đến đầu từ xa của socket,isBound() đề cập đến kết thúc cục bộ Điều này vẫn chưa quan trọng lắm Ràng buộc sẽ trở nên quan trọng hơn khi chúng ta thảo luận về các socket máy chủ trong Chương 9.

Tùy chọn cài đặt socket

Tùy chọn socket chỉ định cách các socket gốc mà lớp Java Socket dựa vào gửi và nhận dữ liệu Java hỗ trợ chín tùy chọn cho socket phía máy khách:

Những cái tên trông buồn cười cho các tùy chọn này được lấy từ các hằng số được đặt tên trong

Các tệp tiêu đề C được sử dụng ở Berkeley Unix, nơi các socket được phát minh Do đó, họ tuân theo các quy ước đặt tên Unix C cổ điển thay vì các quy ước đặt tên Java dễ đọc hơn Ví dụ, SO_SNDBUF thực sự có nghĩa là "Tùy chọn socket gửi kích thước bộ đệm."

Ngoại lệ socket

public class SocketException extends IOException

Tuy nhiên, biết rằng một vấn đề xảy ra thường không đủ để giải quyết vấn đề Máy chủ từ xa có từ chối kết nối vì nó bận không? Có phải máy chủ từ xa từ chối kết nối vì không có dịch vụ nào nghe trên cổng? Kết nối đã cố gắng hết thời gian chờ vì tắc nghẽn mạng hoặc vì máy chủ đã ngừng hoạt động? Có một số phân lớp củaSocketException cung cấp thêm thông tin về những gì đã xảy ra và tại sao:

BindException được ném nếu bạn cố gắng xây dựng một đối tượng Socket hoặc ServerSocket trên một cổng cục bộ đang được sử dụng hoặc bạn không có đủ đặc quyền để sử dụng Một Connec tException được ném khi một kết nối bị từ chối tại máy chủ từ xa, điều này thường xảy ra vì máy chủ bận rộn hoặc không có quy trình nào đang nghe trên cổng đó Cuối cùng, một NoRouteToHostException chỉ ra rằng kết nối đã hết thời gian.

Gói java.net cũng bao gồm ProtocolException, một lớp con trực tiếp của IOException: public class ProtocolException extends IOException Điều này được ném khi dữ liệu được nhận từ mạng bằng cách nào đó vi phạm đặc điểm kỹ thuật TCP / IP.

Không có lớp ngoại lệ nào trong số này có bất kỳ phương pháp đặc biệt nào bạn sẽ không tìm thấy trong bất kỳ lớp ngoại lệ nào khác,nhưng bạn có thể tận dụng các lớp con này để cung cấp thêm thông báo lỗi thông tin hoặc để quyết định xem việc thử lại hoạt động vi phạm có khả năng thành công hay không.

Socket trong ứng dụng GUI

Trình duyệt web HotJava là máy khách mạng Java GUI quy mô lớn đầu tiên HotJava đã ngừng hoạt động, nhưng vẫn còn nhiều ứng dụng khách nhận thức được mạng được viết bằng Java, bao gồmEclipse IDE và máy khách Frostwire BitTorrent Hoàn toàn có thể viết các ứng dụng khách hàng chất lượng thương mại trong Java; và đặc biệt có thể viết các ứng dụng nhận thức mạng, cả khách hàng và máy chủ Phần này thể hiện một khách hàng mạng, whois, để minh họa cho điểm này; và thảo luận về những cân nhắc đặc biệt phát sinh khi tích hợp mã mạng với các ứng dụng Swing Ví dụ dừng lại ngắn của những gì có thể được thực hiện, nhưng chỉ trong giao diện người dùng Tất cả các mã mạng cần thiết đều có mặt Thật vậy, một lần nữa bạn phát hiện ra rằng mã mạng rất dễ dàng; đó là giao diện người dùng khó khăn.

Whois là một giao thức dịch vụ thư mục đơn giản được xác định trong RFC 954; ban đầu nó được thiết kế để theo dõi các quản trị viên chịu trách nhiệm về máy chủ và tên miền Internet Một máy khách whois kết nối với một trong một số máy chủ trung tâm và yêu cầu thông tin thư mục cho một người hoặc người; nó thường có thể cung cấp cho bạn một số điện thoại, một địa chỉ email, và một địa chỉ thư ốc sên (không nhất thiết phải là những người hiện tại, mặc dù) Với sự phát triển bùng nổ của Internet, các lỗ hổng đã trở nên rõ ràng trong giao thức whois, đáng chú ý nhất là bản chất tập trung của nó Một sự thay thế phức tạp hơn được gọi là whois ++ được ghi lại trong RFCs 1913 và 1914 nhưng chưa được triển khai rộng rãi.giao thức dịch vụ thư mục whois)

Hãy bắt đầu với một máy khách đơn giản để kết nối với máy chủ whois Cấu trúc cơ bản của giao thức WHOIS là:

1 Máy khách mở socket TCP đến cổng 43 trên máy chủ.

2 Máy khách gửi chuỗi tìm kiếm bị chấm dứt bởi cặp return/linefeed vận chuyển (\r\n) Chuỗi tìm kiếm có thể là tên, danh sách tên hoặc lệnh đặc biệt, như đã thảo luận trong thời gian ngắn Bạn cũng có thể tìm kiếm tên miền, như www.oreilly.com hoặc netscape.com, cung cấp cho bạn thông tin về mạng.

3 Máy chủ gửi một lượng thông tin không xác định có thể đọc được của con người để đáp ứng với lệnh và đóng kết nối.

4 Khách hàng hiển thị thông tin này cho người dùng.

Chuỗi tìm kiếm mà khách hàng gửi có định dạng khá đơn giản Ở mức cơ bản nhất, nó chỉ là tên của người mà bạn đang tìm kiếm.Dưới đây là một tìm kiếm đơn giản cho "Harold":

Mặc dù đầu vào trước đó có định dạng khá rõ ràng, định dạng đó đáng tiếc là không đạt tiêu chuẩn Các máy chủ whois khác nhau có thể và làm gửi đầu ra khác nhau Ví dụ: đây là một vài kết quả đầu tiên từ cùng một tìm kiếm tại máy chủ whois chính của Pháp, whois.nic.fr: Ở đây mỗi bản ghi đầy đủ được trả lại thay vì chỉ là một danh sách các trang web Các máy chủ whois khác có thể sử dụng các định dạng khác Giao thức này hoàn toàn không được thiết kế để xử lý máy Bạn khá nhiều phải viết mã mới để xử lý đầu ra của mỗi máy chủ whois khác nhau Tuy nhiên, bất kể định dạng đầu ra, mỗi phản hồi có thể chứa một tay cầm, trong đầu ra Internic là một tên miền và trong nic.fr đầu ra nằm trong trường nic-hdl Tay cầm được đảm bảo là duy nhất và được sử dụng để có được thông tin cụ thể hơn về một người hoặc một mạng Nếu bạn tìm kiếm một tay cầm, bạn sẽ nhận được nhiều nhất một trận đấu Nếu tìm kiếm của bạn chỉ có một trận đấu, vì bạn may mắn hoặc bạn đang tìm kiếm một tay cầm, máy chủ sẽ trả về một bản ghi chi tiết hơn Đây là tìm kiếm oreilly.com Bởi vì chỉ có một oreilly.com trong cơ sở dữ liệu, máy chủ trả về tất cả thông tin mà nó có trên tên miền này:

Giao thức whois hỗ trợ một số cờ bạn có thể sử dụng để hạn chế hoặc mở rộng tìm kiếm của mình Ví dụ, nếu bạn biết bạn muốn tìm kiếm một người tên là "Elliott" nhưng bạn không chắc chắn liệu anh ta có đánh vần tên của mình là Elliot, Elliott, hoặc thậm chí có thể là một cái gì đó không thể như Elliotte, bạn sẽ gõ:

Tốt nhất là nghĩ về các giao thức mạng như whois về các bit và byte di chuyển trên mạng, cho dù là gói, dữ liệu hoặc luồng Không có giao thức mạng nào phù hợp với GUI (ngoại trừ Giao thức Khung từ xa được sử dụng bởi VNC và X11) Nó thường là tốt nhất để gói gọn mã mạng vào một thư viện riêng biệt mà mã GUI có thể gọi khi cần thiết.

Ví dụ 8-7 là một lớp Whois có thể tái sử dụng Hai trường xác định trạng thái của mỗi đối tượng Whois: vật chủ, đối tượng InetAddress và cổng, một int Cùng nhau, chúng xác định máy chủ mà đối tượng Whois cụ thể này kết nối với Năm nhà xây dựng thiết lập các trường này từ các kết hợp khác nhau của các đối số Hơn nữa, máy chủ có thể được thay đổi bằng cách sử dụng phương pháp se tHost().

Chức năng chính của lớp nằm trong một phương pháp, lookUpNames() Phương pháp lookUp Names() trả về chuỗi chứa đáp ứng whois cho một truy vấn đã cho Các đối số chỉ định chuỗi cần tìm kiếm, loại bản ghi nào cần tìm kiếm, cơ sở dữ liệu nào cần tìm kiếm và liệu có cần khớp chính xác hay không Tôi có thể đã sử dụng chuỗi hoặc hằng số int để chỉ định loại bản ghi để tìm kiếm và cơ sở dữ liệu để tìm kiếm, nhưng vì chỉ có một số lượng nhỏ các giá trị hợp lệ, lookUpNames () định nghĩa enums với một số lượng thành viên cố định thay thế Giải pháp này cung cấp kiểm tra loại thời gian biên dịch chặt chẽ hơn nhiều và đảm bảo lớp Whois sẽ không phải xử lý một giá trị bất ngờ.

Hình 8-1 cho thấy một giao diện có thể cho một máy khách whois đồ họa phụ thuộc vào Ví dụ 8-7 cho các kết nối mạng thực tế Giao diện này có một trường văn bản để nhập tên cần tìm kiếm và hộp kiểm để xác định xem trận đấu nên chính xác hay một phần Một nhóm các nút radio cho phép người dùng chỉ định nhóm bản ghi nào họ muốn tìm kiếm Một nhóm nút radio khác chọn các trường cần tìm kiếm Theo mặc định, máy khách này tìm kiếm tất cả các trường của tất cả các bản ghi để khớp chính xác.

Khi người dùng nhập chuỗi trong Whois: hộp tìm kiếm và nhấn Enter hoặc bấm nút Tìm, chương trình sẽ tạo kết nối với máy chủ whois và truy xuất các bản ghi khớp với chuỗi đó Chúng được đặt trong khu vực văn bản ở dưới cùng của cửa sổ Ban đầu, máy chủ

Hình 8-1 Một máy khách whois đồ họa được đặt thành whois.internic.net, nhưng người dùng có thể tự do thay đổi cài đặt này Ví dụ 8-8 là chương trình sản xuất giao diện này

Phương pháp chính () là khối mã thông thường để khởi động một ứng dụng độc lập Nó xây dựng một đối tượng Whois và sau đó sử dụng nó để xây dựng một đối tượng WhoisGUI Sau đó, nhà xây dựng WhoisGUI () thiết lập giao diện Swing Có rất nhiều mã dự phòng ở đây, vì vậy nó được chia thành các phương pháp riêng tư initSearchFields(), initServer Choice(), makeSearchInRadioButton(), và makeSearchForRadioButton() Như thường lệ với các giao diện dựa trên LayoutManager, việc thiết lập khá liên quan Bởi vì bạn có thể sẽ sử dụng một nhà thiết kế hình ảnh để xây dựng một ứng dụng như vậy, tôi sẽ không mô tả nó chi tiết ở đây Khi trình xây dựng trở lại, phương pháp chính () gắn một lớp bên trong ẩn danh vào cửa sổ sẽ đóng ứng dụng khi cửa sổ đóng (Điều này không có trong trình xây dựng vì các chương trình khác sử dụng lớp này có thể không muốn thoát khỏi chương trình khi cửa sổ đóng.) chính () sau đó đóng gói và hiển thị cửa sổ Để tránh một điều kiện chủng tộc tối nghĩa có thể dẫn đến bế tắc, điều này cần phải được thực hiện trong chủ đề công văn sự kiện; do đó lớp bên trong FrameShower thực hiện Runnable và cuộc gọi đến EventQueue.invokeLater() Từ thời điểm đó, tất cả các hoạt động diễn ra trong chủ đề công văn sự kiện.

Sự kiện đầu tiên mà chương trình này phải phản hồi là người dùng gõ tên trong Whois: hộp tìm kiếm và nhấp vào nút Tìm hoặc nhấn Enter Trong trường hợp này, lớp bên trong Tra cứu Tên đặt văn bản chính thành chuỗi trống và thực thi SwingWorker để thực hiện kết nối mạng SwingWorker (được giới thiệu trong Java 6) là một lớp thực sự quan trọng để tìm hiểu xem bạn sẽ viết các ứng dụng GUI truy cập mạng hoặc cho vấn đề đó thực hiện bất kỳ I / O nào cả.

Vấn đề swingworker giải quyết là điều này Trong bất kỳ ứng dụng Java GUI nào cũng có hai quy tắc bạn phải tuân theo để tránh bế tắc và chậm chạp:

• Tất cả các bản cập nhật cho các thành phần Swing xảy ra trên chủ đề công văn sự kiện.

SOCKET FOR SERVERS

Sử dụng ServerSockets

Lớp ServerSocket chứa mọi thứ cần thiết để viết máy chủ trong Java Nó có các nhà xây dựng tạo ra các đối tượngServerSocket mới , các phương pháp nghe các kết nối trên một cổng được chỉ định, các phương pháp cấu hình các tùy chọn socket máy chủ khác nhau và các phương pháp linh tinh thông thường như toString().

Trong Java, vòng đời cơ bản của một chương trình máy chủ là:

1 Một ServerSocket mới được tạo trên một cổng cụ thể bằng cách sử dụng trình xây dựng ServerSocket().

2 ServerSocket lắng nghe các nỗ lực kết nối đến trên cổng đó bằng cách sử dụng phương pháp chấp nhận () của nó chấp nhận () chặn cho đến khi khách hàng cố gắng thực hiện kết nối, tại thời điểm đó chấp nhận () trả về đối tượng Socket kết nối máy khách và máy chủ.

3 Tùy thuộc vào loại máy chủ, phương pháp getInputStream () của Socket, phương pháp getOutputStream() hoặc cả hai đều được gọi để có được các luồng đầu vào và đầu ra giao tiếp với khách hàng.

4 Máy chủ và máy khách tương tác theo một giao thức đã thỏa thuận cho đến khi đến lúc đóng kết nối.

5 Máy chủ, máy khách hoặc cả hai đều đóng kết nối.

6 Máy chủ trở lại bước 2 và chờ kết nối tiếp theo.

Hãy chứng minh với một trong những giao thức đơn giản hơn, ban ngày Hãy nhớ lại từ Chương 8 rằng một máy chủ ban ngày nghe trên cổng 13 Khi máy khách kết nối, máy chủ sẽ gửi thời gian ở định dạng con người có thể đọc được và đóng kết nối Ví dụ: đây là kết nối với máy chủ ban ngày tại time-a.nist.gov:

Connected to time-a.nist.gov.

Connection closed by foreign host

Triển khai máy chủ ban ngày của riêng bạn rất dễ dàng Đầu tiên, tạo socket máy chủ nghe trên cổng 13:

Tiếp theo, chấp nhận kết nối: socket connection = server.accept ();

Các khối cuộc gọi chấp nhận () Đó là, chương trình dừng lại ở đây và chờ đợi, có thể trong nhiều giờ hoặc nhiều ngày, cho đến khi khách hàng kết nối trên cổng 13 Khi máy khách thực hiện kết nối, phương thức chấp nhận() trả về đối tượng Socket.

Lưu ý rằng kết nối được trả về một đối tượng java.net.Socket , giống như bạn đã sử dụng cho khách hàng trong chương trước Giao thức ban ngày yêu cầu máy chủ (và chỉ máy chủ) nói chuyện, vì vậy hãy lấy OutputStream từ socket Bởi vì giao thức ban ngày yêu cầu văn bản, hãy chuỗi điều này với OutputStreamWriter:

Writer = New OutputStreamWriter(writer, "ASCII");

Bây giờ lấy thời gian hiện tại và viết nó lên luồng Giao thức ban ngày không yêu cầu bất kỳ định dạng cụ thể nào khác ngoài việc nó có thể đọc được của con người, vì vậy hãy để Java chọn cho bạn: Date now = new Date(); out.write(now.toString() +"\r\n");

Tuy nhiên, lưu ý việc sử dụng cặp return/linefeed vận chuyển để chấm dứt đường dây Đây hầu như luôn là những gì bạn muốn trong một máy chủ mạng Bạn nên chọn rõ ràng điều này thay vì sử dụng bộ tách đường hệ thống, cho dù rõ ràng với System.getProper ty ("line.separator") hoặc ngầm thông qua một phương pháp như println() Cuối cùng, xả kết nối và đóng nó: out.flush(); connection.close();

Bạn sẽ không phải lúc nào cũng phải đóng kết nối chỉ sau một lần viết Ví dụ, nhiều giao thức, dict và HTTP 1.1, cho phép khách hàng gửi nhiều yêu cầu qua một socket duy nhất và mong đợi máy chủ gửi nhiều phản hồi Một số giao thức như FTP thậm chí có thể giữ một socket mở vô thời hạn Tuy nhiên, giao thức ban ngày chỉ cho phép một phản hồi duy nhất.

Nếu máy khách đóng kết nối trong khi máy chủ vẫn đang hoạt động,các luồng đầu vào và/hoặc đầu ra kết nối máy chủ với máy khách sẽ ném một tion InterruptedIOExcep vào lần đọc hoặc ghi tiếp theo.

Trong cả hai trường hợp, máy chủ sau đó sẽ sẵn sàng để xử lý kết nối đến tiếp theo.

Tất nhiên, bạn sẽ muốn làm tất cả điều này nhiều lần, vì vậy bạn sẽ đặt tất cả điều này trong một vòng lặp Mỗi lần đi qua vòng lặp gọi phương pháp chấp nhận () một lần Điều này trả về một đối tượng Socket đại diện cho kết nối giữa máy khách từ xa và máy chủ cục bộ Tương tác với khách hàng diễn ra thông qua đối tượng Socket này Chẳng hạn: Điều này được gọi là một máy chủ lặp đi lặp lại Có một vòng lặp lớn, và trong mỗi lần đi qua vòng lặp, một kết nối duy nhất được xử lý hoàn toàn Điều này hoạt động tốt cho một giao thức rất đơn giản với các yêu cầu và phản hồi rất nhỏ như ban ngày, mặc dù ngay cả với giao thức đơn giản này, một khách hàng chậm có thể trì hoãn các khách hàng nhanh hơn khác Các ví dụ sắp tới sẽ giải quyết vấn đề này với nhiều chủ đề hoặc I /O không đồng bộ.

Khi xử lý ngoại lệ được thêm vào, mã trở nên phức tạp hơn một chút Điều quan trọng là phải phân biệt giữa các trường hợp ngoại lệ có thể nên tắt máy chủ và ghi lại thông báo lỗi và các trường hợp ngoại lệ chỉ cần đóng kết nối hoạt động đó Các trường hợp ngoại lệ trong phạm vi của một kết nối cụ thể nên đóng kết nối đó, nhưng không ảnh hưởng đến các kết nối khác hoặc tắt máy chủ Các trường hợp ngoại lệ nằm ngoài phạm vi của một yêu cầu riêng lẻ có lẽ nên tắt máy chủ Để tổ chức điều này, lồng các khối thử :

Luôn luôn đóng một socket khi bạn đã hoàn thành với nó TrongChương 8, tôi đã nói rằng một khách hàng không nên dựa vào phía bên kia của kết nối để đóng socket; điều đó tăng gấp ba lần cho các máy chủ Khách hàng hết giờ hoặc gặp sự cố; người dùng hủy giao dịch; mạng lưới đi xuống trong thời kỳ hightraffic; tin tặc khởi động các cuộc tấn công từ chối dịch vụ Vì bất kỳ lý do nào trong số này hoặc một trăm lý do nữa, bạn không thể dựa vào khách hàng để đóng socket, ngay cả khi giao thức yêu cầu họ, điều này không.

2.1.1 Phục vụ dữ liệu nhị phân

Gửi dữ liệu nhị phân, phi văn bản không khó hơn đáng kể. Bạn chỉ cần sử dụng đầu ra

Luồng mà viết một mảng byte chứ không phải là một Nhà văn viết một Chuỗi Ví dụ 9-2 thể hiện với một máy chủ thời gian lặp đi lặp lại theo giao thức thời gian được nêu trong RFC 868 Khi một máy khách kết nối, máy chủ sẽ gửi một số nguyên 4 byte, big- endian, không có chữ ký chỉ định số giây đã trôi qua kể từ 12:00A.M., ngày 1 tháng 1 năm 1900, GMT (kỷ nguyên) Một lần nữa,thời gian hiện tại được tìm thấy bằng cách tạo một đối tượng Ngày mới Tuy nhiên, vì lớp Date của Java có số mili giây kể từ 12:00A.M., ngày 1 tháng 1 năm 1970, GMT thay vì vài giây kể từ 12:00A.M., ngày 1 tháng 1 năm 1900, GMT, một số chuyển đổi là cần thiết

Như với TimeClient của chương trước, hầu hết các nỗ lực ở đây đi vào hoạt động với một định dạng dữ liệu (số nguyên 32 bit không được ký) mà Java không hỗ trợ bản địa.

Đăng nhập

Máy chủ chạy không giám sát trong thời gian dài Nó thường quan trọng để gỡ lỗi những gì đã xảy ra khi trong một máy chủ rất lâu sau khi thực tế Vì lý do này, nên lưu trữ nhật ký máy chủ trong ít nhất một khoảng thời gian.

Có hai điều chính bạn muốn lưu trữ trong nhật ký của mình:

Thật vậy, các máy chủ thường giữ hai logfiles khác nhau cho hai mục khác nhau này Nhật ký kiểm toán thường chứa một mục cho mỗi kết nối được thực hiện với máy chủ Các máy chủ thực hiện nhiều hoạt động trên mỗi kết nối có thể có một mục nhập cho mỗi hoạt động thay thế Ví dụ, một máy chủdict có thể ghi lại một mục nhập cho mỗi từ mà khách hàng tìm kiếm Nhật ký lỗi chứa hầu hết các trường hợp ngoại lệ bất ngờ xảy ra trong khi máy chủ đang chạy.

Ví dụ: bất kỳ NullPointerException nào xảy ra nên được đăng nhập ở đây vì nó chỉ ra một lỗi trong máy chủ bạn sẽ cần phải sửa chữa. Nhật ký lỗi không chứa lỗi của khách hàng, chẳng hạn như máy khách bất ngờ ngắt kết nối hoặc gửi yêu cầu bị dị dạng Chúng đi vào nhật ký yêu cầu Nhật ký lỗi chỉ dành riêng cho các trường hợp ngoại lệ bất ngờ.

Nguyên tắc chung của ngón tay cái cho nhật ký lỗi là mọi dòng trong nhật ký lỗi nên được xem xét và giải quyết Số lượng mục nhập lý tưởng trong nhật ký lỗi là bằng không Mỗi mục nhập trong nhật ký này đại diện cho một lỗi cần được điều tra và giải quyết Nếu việc điều tra mục nhật ký lỗi kết thúc với quyết định rằng ngoại lệ đó không thực sự là vấn đề và mã đang hoạt động như dự định, hãy loại bỏ tuyên bố nhật ký Nhật ký lỗi lấp đầy quá nhiều báo động sai nhanh chóng trở nên bị bỏ qua và vô dụng.

Vì lý do tương tự, không giữ nhật ký gỡ lỗi trong sản xuất Không đăng nhập mỗi khi bạn nhập một phương pháp, mỗi khi một điều kiện được đáp ứng, v.v Không ai từng nhìn vào những khúc gỗ này.

Họ chỉ lãng phí không gian và che giấu những vấn đề thực sự Nếu bạn cần ghi nhật ký cấp phương pháp để gỡ lỗi, hãy đặt nó vào một tệp riêng biệt và tắt nó trong tệp thuộc tính toàn cầu khi chạy trong sản xuất.

Các hệ thống ghi nhật ký nâng cao hơn cung cấp các công cụ phân tích nhật ký cho phép bạn thực hiện những việc như chỉ hiển thị tin nhắn có THÔNG TIN ƯU TIÊN trở lên hoặc chỉ hiển thị các tin nhắn có nguồn gốc từ một phần nhất định của mã Những công cụ này làm cho nó khả thi hơn để giữ một logfile hoặc cơ sở dữ liệu duy nhất, thậm chí có thể chia sẻ một bản ghi giữa nhiều nhị phân hoặc chương trình khác nhau Tuy nhiên, nguyên tắc vẫn áp dụng rằng một bản ghi nhật ký mà không ai sẽ nhìn vào là vô giá trị tốt nhất và thường xuyên hơn là không gây mất tập trung hoặc gây nhầm lẫn. Đừng làm theo antipattern thông thường của việc ghi lại tất cả mọi thứ bạn có thể nghĩ đến chỉ trong trường hợp ai đó có thể cần nó một ngày nào đó Trong thực tế, các lập trình viên rất tệ trong việc đoán trước những thông điệp nhật ký mà họ có thể cần để gỡ lỗi các vấn đề sản xuất Một khi một vấn đề xảy ra, đôi khi rõ ràng những thông điệp bạn cần; Nhưng rất hiếm khi có thể dự đoán trước điều này Thêm tin nhắn "chỉ trong trường hợp" vào logfiles thường có nghĩa là khi một vấn đề xảy ra, bạn đang điên cuồng săn lùng các tin nhắn có liên quan trong một biển dữ liệu thậm chí còn lớn hơn.

Nhiều chương trình kế thừa có niên đại từ Java 1.3 trở về trước vẫn sử dụng các thư viện ghi nhật ký của bên thứ ba như log4j hoặc Apache Commons Logging, nhưng gói java.util.logging có sẵn kể từ Java 1.4 đủ cho hầu hết các nhu cầu Chọn nó tránh được rất nhiều sự phụ thuộc phức tạp của bên thứ ba.

Mặc dù bạn có thể tải một logger theo yêu cầu, nó thường dễ dàng nhất để chỉ tạo ra một cho mỗi lớp như vậy: private final static Logger auditLogger = Logger.getLogger("requests");

Loggers là chủ đề an toàn, vì vậy không có vấn đề lưu trữ chúng trong một trường tĩnh chia sẻ Thật vậy, chúng gần như phải là bởi vì ngay cả khi đối tượng Logger không được chia sẻ giữa các chủ đề, logfile hoặc cơ sở dữ liệu sẽ được Điều này rất quan trọng trong các máy chủ đa luồng cao.

Ví dụ này xuất ra một nhật ký có tên là "yêu cầu" Nhiều đối tượng Logger có thể xuất ra cùng một khúc gỗ, nhưng mỗi logger luôn ghi lại chính xác một bản ghi Nhật ký là gì và ở đâu phụ thuộc vào cấu hình bên ngoài Thông thường nhất đó là một tệp, có thể hoặc không thể được đặt tên là "yêu cầu"; nhưng nó có thể là một cơ sở dữ liệu, một dịch vụ SOAP chạy trên một máy chủ khác, một chương trình Java khác trên cùng một máy chủ, hoặc một cái gì đó khác.

Một khi bạn có một logger, bạn có thể viết cho nó bằng cách sử dụng bất kỳ của một số phương pháp Cơ bản nhất là log() Ví dụ: khối bắt này ghi lại một ngoại lệ thời gian chạy bất ngờ ở mức cao nhất:

Bao gồm ngoại lệ thay vì chỉ là một tin nhắn là tùy chọn nhưng thông thường khi đăng nhập từ một khối bắt

Có bảy cấp độ được định nghĩa là hằng số được đặt tên trong java.util.logging.Level theo thứ tự giảm dần của độ nghiêm trọng:

• Level.SEVERE (giá trị cao nhất)

• Level.FINEST (giá trị thấp nhất)

Tôi sử dụng thông tin cho nhật ký kiểm toán và cảnh báo hoặc nghiêm trọng cho nhật ký lỗi Mức thấp hơn chỉ dành cho gỡ lỗi và không nên được sử dụng trong các hệ thống sản xuất Thông tin, nghiêm trọng và cảnh báo đều có các phương pháp trợ giúp thuận tiện đăng nhập ở mức đó Ví dụ: câu lệnh này ghi lại một lượt truy cập bao gồm ngày và địa chỉ từ xa:

Bạn có thể sử dụng bất kỳ định dạng nào thuận tiện cho các bản ghi nhật ký riêng lẻ Nói chung, mỗi bản ghi phải chứa dấu thời gian, địa chỉ khách hàng và bất kỳ thông tin cụ thể nào cho yêu cầu đang được xử lý Nếu thông báo nhật ký đại diện cho một lỗi, hãy bao gồm ngoại lệ cụ thể đã được ném Java điền vào vị trí trong mã nơi tin nhắn được đăng nhập tự động, vì vậy bạn không cần phải lo lắng về điều đó.

Xây dựng server socket

Các nhà xây dựng này chỉ định cổng, độ dài của hàng đợi được sử dụng để giữ các yêu cầu kết nối đến và giao diện mạng cục bộ để liên kết Hầu như tất cả chúng đều làm điều tương tự, mặc dù một số sử dụng các giá trị mặc định cho độ dài hàng đợi và địa chỉ để liên kết.

Ví dụ: để tạo socket máy chủ sẽ được sử dụng bởi một máy chủ HTTP trên cổng 80, bạn sẽ viết:

ServerSocket httpd =new ServerSocket(80); Để tạo socket máy chủ sẽ được sử dụng bởi một máy chủ HTTP trên cổng 80 và xếp hàng lên đến 50 kết nối không được chấp nhận cùng một lúc:

Nếu bạn cố gắng mở rộng hàng đợi qua chiều dài hàng đợi tối đa của hệ điều hành, chiều dài hàng đợi tối đa được sử dụng thay thế.

Theo mặc định, nếu máy chủ có nhiều giao diện mạng hoặc địa chỉ

IP, socket máy chủ sẽ nghe trên cổng được chỉ định trên tất cả các giao diện và địa chỉ IP Tuy nhiên, bạn có thể thêm đối số thứ ba để chỉ ràng buộc với một địa chỉ IP cục bộ cụ thể Đó là, socket máy chủ chỉ nghe các kết nối đến trên địa chỉ được chỉ định; nó sẽ không lắng nghe các kết nối đi vào thông qua các địa chỉ khác của máy chủ.

Ví dụ, login.ibiblio.org là một hộp Linux cụ thể ở Bắc Carolina Nó được kết nối với Internet với địa chỉ IP 152.2.210.122. Cùng một hộp có thẻ Ethernet thứ hai với địa chỉ IP cục bộ 192.168.210.122 không hiển thị từ Internet công cộng, chỉ từ mạng cục bộ Nếu, vì một lý do nào đó, bạn muốn chạy một máy chủ trên máy chủ này chỉ phản hồi các kết nối cục bộ từ trong cùng một mạng, bạn có thể tạo một socket máy chủ nghe trên cổng 5776 của 192.168.210.122 nhưng không phải trên cổng 5776 của 152.2.210.122, như vậy:

ServerSocket httpd =new ServerSocket (5776, 10, local);

Trong cả ba nhà xây dựng, bạn có thể vượt qua 0 cho số cổng để hệ thống sẽ chọn một cổng có sẵn cho bạn Một cổng được hệ thống chọn như thế này đôi khi được gọi là cổng ẩn danh vì bạn không biết trước số của nó (mặc dù bạn có thể tìm hiểu sau khi cổng đã được chọn) Điều này thường hữu ích trong các giao thức đa mông như FTP Trong FTP thụ động, máy khách đầu tiên kết nối với một máy chủ trên cổng nổi tiếng 21, vì vậy máy chủ phải chỉ định cổng đó Tuy nhiên, khi một tệp cần được chuyển, máy chủ bắt đầu nghe trên bất kỳ cổng có sẵn nào Máy chủ sau đó cho khách hàng biết cổng nào khác mà nó nên kết nối với dữ liệu bằng cách sử dụng kết nối lệnh đã mở trên cổng 21 Do đó, cổng dữ liệu có thể thay đổi từ phiên này sang phiên tiếp theo và không cần phải biết trước. (Active FTP tương tự ngoại trừ khách hàng nghe trên một cổng phù du để máy chủ kết nối với nó, thay vì ngược lại.)

Tất cả các nhà xây dựng này ném một IOException, cụ thể, một BindException, nếu socket không thể được tạo ra và bị ràng buộc với cổng được yêu cầu IoException khi tạo ServerSocket hầu như luôn có nghĩa là một trong hai điều Hoặc là một socket máy chủ khác, có thể từ một chương trình hoàn toàn khác, đã sử dụng cổng được yêu cầu hoặc bạn đang cố gắng kết nối với cổng từ 1 đến 1023 trên Unix (bao gồm Linux và Mac OS X) mà không có đặc quyền root (superuser).

Bạn có thể tận dụng điều này để viết một biến thể về chương trình LowPortScanner của chương trước Thay vì cố gắng kết nối với một máy chủ chạy trên một cổng nhất định, thay vào đó bạn cố gắng mở một máy chủ trên cổng đó Nếu nó bị chiếm đóng, nỗ lực sẽ thất bại.

2.3.1 Xây dựng mà không có ràng buộc

Trình xây dựng mái vòm tạo ra một đối tượng ServerSocket nhưng không thực sự liên kết nó với một cổng, vì vậy ban đầu nó không thể chấp nhận bất kỳ kết nối nào Nó có thể bị ràng buộc sau này bằng cách sử dụng các phương pháp bind() :

Việc sử dụng chính cho tính năng này là cho phép các chương trình thiết lập các tùy chọn socket máy chủ trước khi ràng buộc vào cổng.Một số tùy chọn được cố định sau khi socket máy chủ đã bị ràng buộc Mô hình chung trông như thế này:

Bạn cũng có thể vượt qua null cho SocketAddress để chọn một cổng tùy ý Điều này giống như vượt qua 0 cho số cổng trong các nhà xây dựng khác.

Lấy thông tin về server socket

Lớp ServerSocket cung cấp hai phương pháp getter cho bạn biết địa chỉ địa phương và cổng bị chiếm đóng bởi socket máy chủ. Chúng rất hữu ích nếu bạn đã mở socket máy chủ trên cổng ẩn danh và / hoặc giao diện mạng không xác định Đây sẽ là trường hợp, ví dụ, trong kết nối dữ liệu của phiên FTP: public InetAddress getInetAddress()

Phương thức này trả về địa chỉ đang được sử dụng bởi máy chủ (máy chủ cục bộ) Nếu máy chủ cục bộ có một địa chỉ IP duy nhất (như hầu hết), đây là địa chỉ được trả về bởi InetAd dress.getLocalHost() Nếu máy chủ cục bộ có nhiều hơn một địa chỉ

IP, địa chỉ cụ thể được trả về là một trong những địa chỉ IP của máy chủ Bạn không thể dự đoán địa chỉ nào bạn sẽ nhận được Chẳng hạn:

Nếu ServerSocket chưa bị ràng buộc với giao diện mạng, phương thức này trả về null:

Các công cụ xây dựng ServerSocket cho phép bạn nghe trên một cổng không xác định bằng cách vượt qua 0 cho số cổng Phương pháp này cho phép bạn tìm hiểu cổng nào bạn đang nghe Bạn có thể sử dụng điều này trong một chương trình đa trang ngang hàng, nơi bạn đã có một phương tiện để thông báo cho các đồng nghiệp khác về vị trí của bạn Hoặc một máy chủ có thể sinh ra một số máy chủ nhỏ hơn để thực hiện các hoạt động cụ thể Máy chủ nổi tiếng có thể thông báo cho khách hàng về những cổng họ có thể tìm thấy các máy chủ nhỏ hơn Tất nhiên, bạn cũng có thể sử dụng getLocalPort() để tìm một cổng nonanonymous, nhưng tại sao bạn cần phải?

Tùy chọn socket

Tùy chọn socket chỉ định cách các socket gốc mà lớp ServerSocket dựa vào gửi và nhận dữ liệu Đối với socket máy chủ, Java hỗ trợ ba tùy chọn:

Nó cũng cho phép bạn đặt tùy chọn hiệu suất cho các gói của socket.

SO_TIMEOUT là lượng thời gian, tính bằng mili giây, chấp nhận () chờ kết nối đến trước khi ném java.io.InterruptedIOException Nếu SO_TIMEOUT là 0, chấp nhận () sẽ không bao giờ hết thời gian Mặc định là không bao giờ hết thời gian.

Thiết lập SO_TIMEOUT là không phổ biến Bạn có thể cần nó nếu bạn đang thực hiện một giao thức phức tạp và an toàn đòi hỏi nhiều kết nối giữa máy khách và máy chủ, nơi các phản hồi cần thiết để xảy ra trong một khoảng thời gian cố định Tuy nhiên, hầu hết các máy chủ được thiết kế để chạy trong khoảng thời gian không xác định và do đó chỉ sử dụng giá trị thời gian chờ mặc định, 0 (không bao giờ hết thời gian) Nếu bạn muốn thay đổi điều này, phương pháp setSoTi meout() đặt trường SO_TIMEOUT cho đối tượng socket máy chủ này: Đếm ngược bắt đầu khi chấp nhận () được gọi Khi hết thời gian chờ, ac cept() ném SocketTimeoutException, một lớp con củaIOException Bạn cần đặt tùy chọn này trước khi gọi chấp nhận();bạn không thể thay đổi giá trị thời gian chờ trong khi ac cept() đang chờ kết nối Đối số thời gian chờ phải lớn hơn hoặc bằng 0; nếu không, phương pháp này sẽ ném một IllegalArgumentException

Tùy chọn SO_REUSEADDR cho socket máy chủ rất giống với tùy chọn tương tự cho socket máy khách, được thảo luận trong chương trước Nó xác định xem một socket mới sẽ được phép liên kết với một cổng đã sử dụng trước đó trong khi vẫn có thể có dữ liệu đi qua mạng được gửi đến socket cũ Như bạn có thể mong đợi, có hai phương pháp để có được và thiết lập tùy chọn này:

Giá trị mặc định phụ thuộc vào nền tảng Đoạn mã này xác định giá trị mặc định bằng cách tạo ServerSocket mới và sau đó gọi getReuseAddress():

Trên các hộp Linux và Mac OS X nơi tôi đã thử nghiệm mã này, các socket máy chủ có thể tái sử dụng theo mặc định.

Tùy chọn SO_RCVBUF đặt kích thước bộ đệm nhận mặc định cho socket máy khách được socket máy chủ chấp nhận Nó được đọc và viết bằng hai phương pháp sau:

Cài đặt SO_RCVBUF trên socket máy chủ giống như gọi setReceiveBufferSize() trên mỗi socket riêng lẻ được trả về bằng chấp nhận () (ngoại trừ việc bạn không thể thay đổi kích thước bộ đệm nhận được sau khi socket đã được chấp nhận) Nhớ lại từ chương trước rằng tùy chọn này gợi ý một giá trị cho kích thước của các gói IP riêng lẻ trong luồng Các kết nối nhanh hơn sẽ muốn sử dụng bộ đệm lớn hơn, mặc dù hầu hết thời gian giá trị mặc định là tốt.

Bạn có thể đặt tùy chọn này trước hoặc sau khi socket máy chủ bị ràng buộc, trừ khi bạn muốn đặt kích thước bộ đệm nhận lớn hơn64K Trong trường hợp đó, bạn phải đặt tùy chọn trên ServerSocket không bị ràng buộc trước khi ràng buộc nó

Như bạn đã biết trong chương trước, các loại dịch vụ Internet khác nhau có nhu cầu hiệu suất khác nhau Ví dụ, phát trực tiếp video thể thao cần băng thông tương đối cao Mặt khác, một bộ phim vẫn có thể cần băng thông cao nhưng có thể chịu được độ trễ và độ trễ cao hơn Email có thể được truyền qua các kết nối băng thông thấp và thậm chí được giữ trong vài giờ mà không gây hại lớn Bốn lớp giao thông chung được xác định cho TCP:

Các lớp lưu lượng truy cập này có thể được yêu cầu cho một Socket nhất định Ví dụ: bạn có thể yêu cầu độ trễ tối thiểu có sẵn với chi phí thấp Các biện pháp này đều mờ nhạt và tương đối, không đảm bảo dịch vụ Không phải tất cả các bộ định tuyến và ngăn xếp TCP gốc đều hỗ trợ các lớp này.

Phương pháp setPerformancePreferences() thể hiện các ưu tiên tương đối dành cho thời gian kết nối, độ trễ và băng thông cho các socket được chấp nhận trên máy chủ này:

Ví dụ: bằng cách đặt kết nối Thời gian đến 2, độ trễ đến 1 và băng thông thành 3, bạn chỉ ra rằng băng thông tối đa là đặc điểm quan trọng nhất, độ trễ tối thiểu là ít quan trọng nhất và thời gian kết nối ở giữa: ss setPerformancePreferences(2, 1, 3);

Chính xác làm thế nào bất kỳ VM nào thực hiện điều này là phụ thuộc vào việc thực hiện Việc thực hiện socket cơ bản không bắt buộc phải tôn trọng bất kỳ yêu cầu nào trong số này Họ chỉ cung cấp một gợi ý cho ngăn xếp TCP về chính sách mong muốn.Nhiều triển khai bao gồm Android bỏ qua các giá trị này hoàn toàn.

Máy chủ HTTP

Phần này hiển thị một số máy chủ HTTP khác nhau mà bạn có thể xây dựng với các socket máy chủ, mỗi máy có một mục đích đặc biệt khác nhau và mỗi máy chủ phức tạp hơn một chút so với trước đó.

HTTP là một giao thức lớn Như bạn đã thấy trong Chương 5, một máy chủ HTTP đầy đủ tính năng phải trả lời các yêu cầu về tệp, chuyển đổi URL thành tên tệp trên hệ thống cục bộ, trả lời các yêu cầu POST và GET, xử lý các yêu cầu đối với các tệp không tồn tại, giải thích các loại MIME và nhiều, nhiều hơn nữa Tuy nhiên, nhiều máy chủ HTTP không cần tất cả các tính năng này.

Ví dụ: nhiều trang web chỉ đơn giản là hiển thị thông báo "đang được xây dựng".

Rõ ràng, Apache là quá mức cần thiết cho một trang web như thế này Một trang web như vậy là một ứng cử viên cho một máy chủ tùy chỉnh chỉ làm một điều Thư viện lớp mạng của Java làm cho việc viết các máy chủ đơn giản như thế này gần như tầm thường.

Máy chủ tùy chỉnh không chỉ hữu ích cho các trang web nhỏ Các trang web có lưu lượng truy cập cao như Yahoo! cũng là ứng cử viên cho các máy chủ tùy chỉnh vì một máy chủ chỉ làm một việc thường có thể nhanh hơn nhiều so với một máy chủ đa năng như Apache hoặc Microsoft IIS Thật dễ dàng để tối ưu hóa một máy chủ mục đích đặc biệt cho một nhiệm vụ cụ thể; kết quả thường hiệu quả hơn nhiều so với một máy chủ đa năng cần đáp ứng nhiều loại yêu cầu khác nhau Ví dụ: các biểu tượng và hình ảnh được sử dụng nhiều lần trên nhiều trang hoặc trên các trang có lưu lượng truy cập cao có thể được xử lý tốt hơn bởi một máy chủ đọc tất cả các tệp hình ảnh vào bộ nhớ khi khởi động và sau đó phục vụ chúng trực tiếp ra khỏi RAM, thay vì phải đọc chúng ra khỏi đĩa cho mỗi yêu cầu Hơn nữa, máy chủ này có thể tránh lãng phí thời gian vào việc ghi nhật ký nếu bạn không muốn theo dõi các yêu cầu hình ảnh riêng biệt với các yêu cầu cho các trang mà chúng được bao gồm.

Cuối cùng, Java không phải là một ngôn ngữ tồi cho các máy chủ web đầy đủ tính năng nhằm cạnh tranh với apache hoặc IIS Ngay cả khi bạn tin rằng các chương trình Java chuyên sâu về CPU chậm hơn các chương trình C và C ++ chuyên sâu về CPU (điều mà tôi rất nghi ngờ là đúng trong các máy ảo hiện đại), hầu hết các máy chủ HTTP đều bị giới hạn bởi băng thông mạng và độ trễ, không phải bởi tốc độ CPU Do đó, những lợi thế khác của Java, chẳng hạn như bản chất nửa biên dịch / nửa giải thích của nó, tải lớp năng động, thu gom rác và bảo vệ bộ nhớ thực sự có cơ hội tỏa sáng Đặc biệt, các trang web sử dụng nhiều nội dung động thông qua servlets, trang PHP hoặc các cơ chế khác thường có thể chạy nhanh hơn nhiều khi được cài đặt lại trên một máy chủ web Java thuần túy hoặc chủ yếu là thuần túy Thật vậy, có một số máy chủ web sản xuất được viết bằng Java, chẳng hạn như Jetty của Eclipse Foundation Nhiều máy chủ web khác được viết bằng C hiện nay bao gồm các thành phần Java đáng kể để hỗ trợ API Java Servlet vàJava Server Pages Chúng phần lớn thay thế các CGIs truyền thống, ASP và phía máy chủ bao gồm, chủ yếu là do các tương đương Java nhanh hơn và ít tốn tài nguyên hơn.

Tôi sẽ không khám phá những công nghệ này ở đây bởi vì chúng dễ dàng xứng đáng có một cuốn sách của riêng họ Tôi giới thiệu độc giả quan tâm đến Chương trình Java Servlet của Jason Hunter (O'Reilly) Tuy nhiên, điều quan trọng cần lưu ý là các máy chủ nói chung và máy chủ web nói riêng là một lĩnh vực mà Java thực sự cạnh tranh với

C về hiệu suất trong thế giới thực.

Cuộc điều tra của về các máy chủ HTTP bắt đầu với một máy chủ luôn gửi cùng một tệp, bất kể yêu cầu nào Nó được gọi là SingleFileHTTPServer và được hiển thị trong Ví dụ 9-10 Tên tệp, cổng cục bộ và mã hóa nội dung được đọc từ dòng lệnh Nếu cổng bị bỏ qua, cổng 80 được giả định Nếu mã hóa bị bỏ qua, ASCII được giả định.

Ví dụ 9-10 Một máy chủ HTTP phục vụ một tệp duy nhất

Các nhà xây dựng thiết lập dữ liệu được gửi cùng với tiêu đề HTTP bao gồm thông tin về độ dài nội dung và mã hóa nội dung Tiêu đề và cơ thể của phản ứng được lưu trữ trong các mảng byte trong mã hóa mong muốn để chúng có thể được thổi cho khách hàng rất nhanh chóng.

Lớp SingleFileHTTPServer giữ nội dung cần gửi, tiêu đề cần gửi và cổng để ràng buộc Phương pháp start() tạo ra một ServerSocket trên cổng được chỉ định, sau đó đi vào một vòng lặp vô hạn liên tục chấp nhận các kết nối và xử lý chúng.

Mỗi socket đến được xử lý bởi một đối tượng Handler có thể chạy được gửi đến một nhóm chủ đề Do đó, một khách hàng chậm chạp không thể bỏ đói các khách hàng khác. Mỗi Handler nhận được một InputStream từ nó mà nó đọc yêu cầu của khách hàng Nó nhìn vào dòng đầu tiên để xem liệu nó có chứa chuỗi HTTP hay không Nếu nhìn thấy chuỗi này, máy chủ giả định rằng máy khách hiểu HTTP/1.0 trở lên và do đó gửi tiêu đề MIME cho tệp; Sau đó nó gửi dữ liệu Nếu yêu cầu của máy khách không chứa chuỗi HTTP, máy chủ sẽ bỏ qua tiêu đề, tự gửi dữ liệu Cuối cùng, người xử lý đóng kết nối.

Phương pháp chính () chỉ đọc các tham số từ dòng lệnh Tên của tệp được phục vụ được đọc từ đối số dòng lệnh đầu tiên Nếu không có tệp nào được chỉ định hoặc tệp không thể mở được, thông báo lỗi sẽ được in và chương trình thoát ra Giả sử tệp có thể được đọc, nội dung của nó được đọc vào dữ liệu mảng byte bằng cách sử dụng các lớp Đường dẫn và Tệp được giới thiệu trong Java 7 Lớp URLConnection đưa ra một dự đoán hợp lý về loại nội dung của tệp và phỏng đoán đó được lưu trữ trong biếnType nội dung Tiếp theo, số cổng được đọc từ đối số dòng lệnh thứ hai Nếu không có cổng nào được chỉ định hoặc nếu đối số thứ hai không phải là số nguyên từ 1 đến 65.535, cổng 80 được sử dụng Mã hóa được đọc từ đối số dòng lệnh thứ ba, nếu có Nếu không, UTF-8 được giả định Sau đó, các giá trị này được sử dụng để xây dựng một đối tượngSingleFileHTTPServer và bắt đầu nó.

Phương pháp chính () chỉ là một giao diện có thể Bạn có thể dễ dàng sử dụng lớp học này như một phần của một số chương trình khác Nếu bạn thêm phương pháp setter để thay đổi nội dung, bạn có thể dễ dàng sử dụng nó để cung cấp thông tin trạng thái đơn giản về máy chủ hoặc hệ thống đang chạy Tuy nhiên, điều đó sẽ đặt ra một số vấn đề bổ sung về an toàn luồng mà Ví dụ 9-10 không phải giải quyết vì dữ liệu là bất biến.

Dưới đây là những gì bạn thấy khi bạn kết nối với máy chủ này thông qua Telnet (các chi tiết cụ thể phụ thuộc vào máy chủ và tệp chính xác):

Redirection là một ứng dụng đơn giản nhưng hữu ích khác cho một máy chủ

HTTP có mục đích đặc biệt.

Trong phần này, bạn phát triển một máy chủ chuyển hướng người dùng từ trang web này sang trang web khác - ví dụ: từ cnet.com đến www.cnet.com Ví dụ 9-11 đọc URL và số cổng từ dòng lệnh, mở socket máy chủ trên cổng và chuyển hướng tất cả các yêu cầu mà nó nhận được đến trang web được chỉ định bởi URL mới bằng mã 302 FOUND Trong ví dụ này, tôi đã chọn sử dụng một chủ đề mới thay vì một nhóm chủ đề cho mỗi kết nối Điều này có lẽ đơn giản hơn một chút để viết mã và hiểu nhưng có phần kém hiệu quả hơn

Tuy nhiên, nếu bạn kết nối với trình duyệt web, bạn nên được gửi đến http://www.cafeconleche.org/ chỉ với một sự chậm trễ nhỏ Bạn không bao giờ nên thấy

Ngày đăng: 06/12/2022, 00:04

HÌNH ẢNH LIÊN QUAN

Đầu ra điển hình giống như nếu bạn kết nối với Telnet: $ java DaytimeClient - BÁO cáo CHI TIẾT lập TRÌNH MẠNG đề tài  SOCKET FOR CLIENTS
u ra điển hình giống như nếu bạn kết nối với Telnet: $ java DaytimeClient (Trang 13)
Hình 8-1 cho thấy một giao diện có thể cho một máy khách whois - BÁO cáo CHI TIẾT lập TRÌNH MẠNG đề tài  SOCKET FOR CLIENTS
Hình 8 1 cho thấy một giao diện có thể cho một máy khách whois (Trang 48)
w