Chương 6<br />
<br />
Lập trình Socket cho giao thức TCP<br />
1. Mô hình client/server<br />
Mô hình được phổ biến nhất và được chấp nhận rộng rãi trong các hệ thống phân tán<br />
là mô hình client/server. Trong mô hình này sẽ có một tập các tiến trình mà mỗi tiến trình<br />
đóng vai trò như là một trình quản lý tài nguyên cho một tập hợp các tài nguyên cho trước và<br />
một tập hợp các tiến trình client trong đó mỗi tiến trình thực hiện một tác vụ nào đó cần truy<br />
xuất tới tài nguyên phần cứng hoặc phần mềm dùng chung. Bản thân các trình quản lý tài<br />
nguyên cần phải truy xuất tới các tài nguyên dùng chung được quản lý bởi một tiến trình<br />
khác, vì vậy một số tiến trình vừa là tiến trình client vừa là tiến trình server. Các tiến trình<br />
phát ra các yêu cầu tới các server bất kỳ khi nào chúng cần truy xuất tới một trong các tài<br />
nguyên của các server. Nếu yêu cầu là đúng đắn thì server sẽ thực hiện hành động được<br />
yêu cầu và gửi một đáp ứng trả lời tới tiến trình client.<br />
Mô hình client/server cung cấp một cách tiếp cận tổng quát để chia sẻ tài nguyên<br />
trong các hệ thống phân tán. Mô hình này có thể được cài đặt bằng rất nhiều môi trường<br />
phần cứng và phần mềm khác nhau. Các máy tính được sử dụng để chạy các tiến trình<br />
client/server có nhiều kiểu khác nhau và không cần thiết phải phân biệt giữa chúng; cả tiến<br />
trình client và tiến trình server đều có thể chạy trên cùng một máy tính. Một tiến trình server<br />
có thể sử dụng dịch vụ của một server khác.<br />
Mô hình truyền tin client/server hướng tới việc cung cấp dịch vụ. Quá trình trao đổi dữ<br />
liệu bao gồm:<br />
1. Truyền một yêu cầu từ tiến trình client tới tiến trình server<br />
2. Yêu cầu được server xử lý<br />
3. Truyền đáp ứng cho client<br />
Mô hình truyền tin này liên quan đến việc truyền hai thông điệp và một dạng đồng bộ<br />
hóa cụ thể giữa client và server. Tiến trình server phải nhận thức được thông điệp được yêu<br />
cầu ở bước một ngay khi nó đến và hành động phát ra yêu cầu trong client phải được tạm<br />
dừng (bị phong tỏa) và buộc tiến trình client ở trạng thái chờ cho tớ khi nó nhận được đáp<br />
ứng do server gửi về ở bước ba.<br />
Mô hình client/server thường được cài đặt dựa trên các thao tác cơ bản là gửi (send)<br />
và nhận (receive).<br />
<br />
119<br />
<br />
Sưu tầm bởi: www.daihoc.com.vn<br />
<br />
Client<br />
Server<br />
<br />
Request message<br />
<br />
Wait<br />
<br />
Reply Execution<br />
<br />
Request message<br />
<br />
Tiến trình đang xử lý<br />
<br />
Tiến trình đang phong tỏa<br />
Hình 4.1<br />
Quá trình giao tiếp client và server có thể diễn ra theo một t rong hai chế độ: bị phong<br />
tỏa (blocked) và không bị phong tỏa (non-blocked).<br />
Chế độ bị phong tỏa (blocked):<br />
Trong chế độ bị phong tỏa, khi tiến trình client hoặc server phát ra lệnh gửi dữ liệu<br />
(send), việc thực thi của tiến trình sẽ bị tạm ngừng cho tới khi tiến trình nhận phát ra lệnh<br />
nhận dữ liệu (receive).<br />
Tương tự đối với tiến trình nhận dữ liệu, nếu tiến trình nào đó (client hoặc server) phát<br />
ra lệnh nhận dữ liệu, mà tại thời điểm đó chưa có dữ liệu gửi tới thì việc thực thi của tiến<br />
trình cũng sẽ bị tạm ngừng cho tới khi có dữ liệu gửi tới.<br />
Chế độ không bị phong tỏa (non-blocked)<br />
Trong chế độ này, khi tiến trình client hay server phát ra lệnh gửi dữ liệu thực sự, việc<br />
thực thi của tiến trình vẫn được tiến hành mà không quan tâm đến việc có tiến trình nào phát<br />
ra lệnh nhận dữ liệu đó hay không.<br />
Tương tự cho trường hợp nhận dữ liệu, khi tiến trình phát ra lệnh nhận dữ liệu, nó sẽ<br />
nhận dữ liệu hiện có, việc thực thi của tiến trình vẫn được tiến hành mà không quan tâm đến<br />
việc có tiến trình nào phát ra lệnh gửi dữ liệu tiếp theo hay không.<br />
<br />
2. Các kiến trúc Client/Server<br />
2.1. Client/Server hai tầng (two-tier client/server)<br />
Kiến trúc client/server đơn giản nhất là kiến trúc hai tầng. Trong thực tế hầu hết các<br />
kiến trúc client/server là kiến trúc hai tầng. Một ứng dụng hai tầng cung cấp nhiều trạm làm<br />
việc với một tầng trình diễn thống nhất, tầng này truyền tin với tầng lưu trữ dữ liệu tập trung.<br />
Tầng trình diễn thông thường là client, và tầng lưu trữ dữ liệu là server.<br />
Hầu hết các ứng dụng Internet như là email, telnet, ftp thậm chí là cả Web là các ứng<br />
dụng hai tầng. Phần lớn các lập trình viên trình ứng dụng viết các ứng dụng client/server có<br />
xu thế sử dụng kiến trúc này.<br />
<br />
120<br />
<br />
Sưu tầm bởi: www.daihoc.com.vn<br />
<br />
Trong ứng dụng hai tầng truyền thống, khối lượng công việc xử lý được dành cho<br />
phía client trong khi server chỉ đơn giản đóng vai trò như là chương trình kiểm soát luồng<br />
vào ra giữa ứng dụng và dữ liệu. Kết quả là không chỉ hiệu năng của ứng dụng bị giảm đi do<br />
tài nguyên hạn chế của PC, mà khối lượng dữ liệu truyền đi trên mạng cũng tăng theo. Khi<br />
toàn bộ ứng dụng được xử lý trên một PC, ứng dụng bắt buộc phải yêu cầu nhiều dữ liệu<br />
trước khi đưa ra bất kỳ kết quả xử lý nào cho người dùng. Nhiều yêu cầu dữ liệu cũng làm<br />
giảm hiệu năng của mạng. Một vấn đề thường gặp khác đối với ứng dụng hai tầng là vấn đề<br />
bảo trì. Chỉ cần một thay đổi nhỏ đối với ứng dụng cũng cần phải thay đổi lại toàn bộ ứng<br />
dụng client và server.<br />
<br />
Hình 4.2<br />
2.2. Client/Server ba tầng<br />
Ta có thể tránh được các vấn đề của kiến trúc client/server hai tầng bằng cách mở<br />
rộng kiến trúc thành ba tầng. Một kiến trúc ba tầng có thêm một tầng mới tác biệt việc xử lý<br />
dữ liệu ở vị trí trung tâm.<br />
<br />
Hình 4.3<br />
<br />
121<br />
<br />
Sưu tầm bởi: www.daihoc.com.vn<br />
<br />
Theo kiến trúc ba tầng, một ứng dụng được chia thành ba tầng tách biệt nhau về mặt<br />
logic. Tầng đầu tiên là tầng trình diễn thường bao gồm các giao diện đồ họa. Tầng thứ hai,<br />
còn được gọi là tầng trung gian hay tầng tác nghiệp. Tầng thứ ba chứa dữ liệu cần cho ứng<br />
dụng. Tầng thứ ba về cơ bản là chương trình thực hiện các lời gọi hàm để tìm kiếm dữ liệu<br />
cần thiết. Tầng trình diễn nhận dữ liệu và định dạng nó để hiển thị. Sự tách biệt giữa chức<br />
năng xử lý với giao diện đã tạo nên sự linh hoạt cho việc thiết kế ứng dụng. Nhiều giao diện<br />
người dùng được xây dựng và triển khai mà không làm thay đổi logic ứng dụng.<br />
Tầng thứ ba chứa dữ liệu cần thiết cho ứng dụng. Dữ liệu này có thể bao gồm bất kỳ<br />
nguồn thông tin nào, bao gồm cơ sở dữ liệu như Oracale, SQL Server hoặc tài liệu XML.<br />
2.3. Kiến trúc n-tầng<br />
Kiến trúc n-tầng được chia thành các tầng như sau:<br />
Tầng giao diện người dùng: quản lý tương tác của người dùng với ứng dụng<br />
<br />
<br />
Tầng logic trình diễn: Xác định cách thức hiển thị giao diện người dùng và các yêu<br />
cầu của người dùng được quản lý như thế nào.<br />
<br />
<br />
<br />
Tầng logic tác nghiệp: Mô hình hóa các quy tắc tác nghiệp,<br />
<br />
<br />
<br />
Tầng các dịch vụ hạ tầng: Cung cấp một chức năng bổ trợ cần thiết cho ứng dụng<br />
như các thành phần (truyền thông điệp, hỗ trợ giao tác).<br />
<br />
3. Mô hình truyền tin socket<br />
Server<br />
<br />
Client<br />
<br />
Socket()<br />
<br />
1<br />
<br />
Socket()<br />
<br />
Bind()<br />
<br />
2<br />
<br />
Bind()<br />
<br />
Listen()<br />
<br />
3<br />
4<br />
<br />
Connect()<br />
<br />
Accept()<br />
<br />
5<br />
<br />
Các chức<br />
năng gửi<br />
và nhận<br />
<br />
6<br />
<br />
Các chức<br />
năng gửi<br />
và nhận<br />
<br />
Close()<br />
<br />
7<br />
<br />
Close()<br />
<br />
Hình 4.4<br />
<br />
122<br />
<br />
Sưu tầm bởi: www.daihoc.com.vn<br />
<br />
Khi lập trình, ta cần quan tâm đến chế độ bị phong tỏa, vì nó có thể dẫn đến tình<br />
huống một tiến trình nào đó sẽ rơi vào vòng lặp vô hạn của quá trình gửi hoặc nhận.<br />
Trong chương 1 chúng ta đã biết hai giao thức TCP và UDP là các giao thức tầng<br />
giao vận để truyền dữ liệu. Mỗi giao thức có những ưu và nhược điểm riêng. Chẳng hạn,<br />
giao thức TCP có độ tin cậy truyền tin cao, nhưng tốc độ truyền tin bị hạn chế do phải có giai<br />
đoạn thiết lập và giải phóng liên kết khi truyền tin, khi gói tin có lỗi hay bị thất lạc thì giao<br />
thức TCP phải có trách nhiệm truyền lại,…Ngược lại, giao thức UDP có tốc độ truyền tin rất<br />
nhanh vì nó chỉ có một cơ chế truyền tin rất đơn giản: không cần phải thiết lập và giải phóng<br />
liên kết. Khi lập trình cho TCP ta sử dụng các socket luồng, còn đối với giao thức UDP ta<br />
sẽ sử dụng lớp DatagramSocket và DatagramPacket.<br />
Truyền tin hướng liên kết nghĩa là cần có giai đoạn thiết lập liên kết và giải phóng liên<br />
kết trước khi truyền tin. Dữ liệu được truyền trên mạng Internet dưới dạng các gói (packet)<br />
có kích thước hữu hạn được gọi là datagram. Mỗi datagram chứa một header và một<br />
payload. Header chứa địa chỉ và cổng cần truyền gói tin đến, cũng như địa chỉ và cổng xuất<br />
phát của gói tin, và các thông tin khác được sử dụng để đảm bảo độ tin cậy truyền tin,<br />
payload chứa dữ liệu. Tuy nhiên do các datagram có chiều dài hữu hạn nên thường phải<br />
phân chia dữ liệu thành nhiều gói và khôi phục lại dữ liệu ban đầu từ các gói ở nơi nhận.<br />
Trong quá trình truyền tin có thể có thể có một hay nhiều gói bị mất hay bị hỏng và cần phải<br />
truyền lại hoặc các gói tin đến không theo đúng trình tự. Để tránh những điều này, việc phân<br />
chia dữ liệu thành các gói, tạo các header, phân tích header của các gói đến, quản lý danh<br />
sách các gói đã nhận được và các gói chưa nhận được, ... rất nhiều công việc cần phải thực<br />
hiện, và đòi hỏi rất nhiều phần mềm phức tạp.<br />
Thật may mắn, ta không cần phải tự thực hiện công việc này. Socket là một cuộc cách<br />
mạng của Berkeley UNIX. Chúng cho phép người lập trình xem một liên kết mạng như là<br />
một luồng mà có thể đọc dữ liệu ra hay ghi dữ liệu vào từ luồng này.<br />
Về mặt lịch sử Socket là một sự mở rộng của một trong những ý tưởng quan trọng<br />
nhất của UNIX: tất cả các thao tác vào/ra giống như vào ra tệp tin đối với người lập trình,<br />
cho dù ta đang làm việc với bàn phím, màn hình đồ họa, một file thông thường, hay một liên<br />
kết mạng. Các Socket che dấu người lập trình khỏi các chi tiết mức thấp của mạng như môi<br />
kiểu đường truyền, các kích thước gói, yêu cầu truyền lại gói, các địa chỉ mạng...<br />
Một socket có thể thực hiện bảy thao tác cơ bản:<br />
<br />
<br />
Kết nối với một máy ở xa (ví dụ, chuẩn bị để gửi và nhận dữ liệu)<br />
<br />
<br />
<br />
Gửi dữ liệu<br />
<br />
<br />
<br />
Nhận dữ liệu<br />
<br />
<br />
<br />
Ngắt liên kêt<br />
<br />
<br />
<br />
Gán cổng<br />
<br />
<br />
<br />
Nghe dữ liệu đến<br />
<br />
<br />
<br />
Chấp nhận liên kết từ các máy ở xa trên cổng đã được gán<br />
<br />
Lớp Socket của Java được sử dụng bởi cả client và server, có các phương thức<br />
tương ứng với bốn thao tác đầu tiên. Ba thao tác cuối chỉ cần cho server để chờ các client<br />
liên kết với chúng. Các thao tác này được cài đặt bởi lớp ServerSocket. Các socket cho<br />
client thường được sử dụng theo mô hình sau:<br />
<br />
<br />
Một socket mới được tạo ra bằng cách sử dụng hàm Socket().<br />
<br />
<br />
<br />
Socket cố gắng liên kết với một host ở xa.<br />
<br />
<br />
<br />
Mỗi khi liên kết được thiết lập, các host ở xa nhận các luồng vào và luồng ra từ<br />
socket, và sử dụng các luồng này để gửi dữ liệu cho nhau. Kiểu liên kết này được gọi<br />
<br />
123<br />
<br />
Sưu tầm bởi: www.daihoc.com.vn<br />
<br />