Preview text:
lOMoARcPSD| 36625228 Ứng dụng clinet-server
B Ộ GIÁO DỤC VÀ ĐÀO TẠO
TRƯỜNG ĐẠI HỌC XÂY DỰNG HÀ NỘI
KHOA CÔNG NGHỆ THÔNG TIN Bài Tập Lớn
Xây dựng ứng dụng Chat Client – Server
Giảng Viên Hướng Dẫn : Hoàng Nam Thắng Nhóm:
Nguyễn Xuân Thưởng 1121050299
Vũ Hoàng Tuấn KIệt 1121050218 Nguyễn Trung Kiên 1121050228 Lớp:
Khoa học máy tính – K66
Hà Nội, Ngày 19 Tháng 9 Năm 2022 lOMoARcPSD| 36625228 Ứng dụng clinet-server Mục Lục LỜI NÓI ĐẦU
Chương 1. GIỚI THIỆU 1.1 .
MỤC TIÊU CỦA ĐỀ TÀI .
1.2 . GIẢI QUYẾT ĐỀ TÀI.
Chương 2 : CƠ SỞ LÝ THUYẾT
2.1 . CÁC GIAO THỨC MẠNG
2.1.1 . Giao thức IP
2.1.2 . Giao thức UDP
2.1.3 . Giao thức TCP
2.2 . MÔ HÌNH CLIENT-SERVER 2.3 . LẬP TRÌNH SOCKET
Chương 3: MÔ TẢ ỨNG DỤNG
3.1 . GIỚI THIỆU ỨNG DỤNG 3.2 . KIẾN TRÚC ỨNG DỤNG
3.2.1 . Mô hình tổng thể
3.2.2 . Gói server
3.2.3 . Gói client
3.3 . MÔ HÌNH THÔNG ĐIỆP GIỮA CLIENT VÀ SERVER
Chương 4. CÀI ĐẶT VÀ KẾT QUẢ THỬ NGHIỆM
4.1 . KẾT QUẢ THỬ NGHIỆM 4.2 . CÁC GIAO DIỆN CHÍNH CỦA CHƯƠNG
TRÌNH 4.3 . KẾT QUẢ THỬ NGHIỆM TRÊN MẠNG LAN
Chương 5. KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN
5.1. KẾT LUẬN VÀ KẾT QUẢ ĐẠT ĐƯỢC
5.1.2. Kết quả đạt được 5.2. ƯU KHUYẾT ĐIỂM
5.2.1 . Ưu điểm
5.2.2. Khuyết điểm 5.3. HƯỚNG PHÁT TRIỂN lOMoARcPSD| 36625228 Ứng dụng clinet-server
Chương 1. GIỚI THIỆU
1.1 MỤC TIÊU CỦA ĐỀ TÀI
- Dùng Java để xây dựng 1 trương trình Chat qua mạng theo mô hình --- Client/Server.
- Tạo ra được 1 chương trình mà các Client kết nối được với nhau thông qua Server.
- Các Client có thể gửi tin nhắn, hoặc trò chuyện trực tiếp với các Client khác.
1.2 GIẢI QUYẾT ĐỀ TÀI Giao thức TCP/IP Mô hình Client Server Socket trong java
TCP/IP cho phép các máy tính trong mạng TCP/IP có thể tạo ra các kết nối để trao đổi
thông tin với nhau.Với 1 địa chỉ IP + 1 cổng TCP sẽ tạo thành một cầu nối và Socket
chính là một giao diện lập trình trên các cầu nối đó. (1 IP Address + 1 Port TCP = 1 Socket )
Khi các máy tính muốn trao đổi thông tin với nhau thì các máy phải có cùng một
Socket. Một máy được coi là Server, nó mở ra một Socket và lắng nghe yêu cầu. Các
máy còn lại được coi là Client, nó gọi cho Server Socket để bắt thiết lập một kết nối.
Để thiết lập được kết nối cần có địa chỉ của máy đích (Destination IP Address ) và một cổng TCP (TCP port). lOMoARcPSD| 36625228 Ứng dụng clinet-server 2 Mô hình Client – Server
Phương thức truyền tin trong Java:
Nó sử dụng phương thức truyền tin có kết nối thông qua 2 đối tượng là:
Socket(Client) và ServerSocket(Server). Tạo 1 Socket sử dụng để kết nối tới 1 SocketServer.
Client Socket được tạo ra thông qua 1 hàm khởi tạo(Contructor) của lớp Socket:
Socket client=new Socket(Destination Address,Port) Trong đó:
+ Destination Address là địa chỉ của máy muốn kết nối tới.
+ Port là số hiệu cổng TCP đòi hỏi phải có một ServerSocket đang lắng nghe yêu cầu trên đó.
Để tạo ra một ServerSocket sử dụng câu lệnh sau:
ServerSocket SSocket=new ServerSocket(Port,Number of Connection) Trong đó:
+ Port là số hiệu cổng sẽ chờ để phục vụ
+ Number of Connection là số kết nối chấp nhận phục vụ cùng một lúc. lOMoARcPSD| 36625228 Ứng dụng clinet-server
Một SocketServer sẽ lắng nghe trên một cổng. Khi nhận được một yêu cầu từ socket
(Client Socket) nó sẽ kiểm tra xem còn chấp nhận được kết nối đó không, chưa vượt
quá số kết nối mà nó có thể phục vụ, nếu được nó sẽ tạo ra một Socket để tạo liên kết
với Client yêu cầu bằng phương thức:
Socket client=SSocket.accept();
Các Socket cung cấp 1 giao diện để đọc ghi dữ liệu thông qua 1 luồng.Khi kết nối đã
được thiết lập 2 máy tính có thể trao đổi dữ liệu thông qua các đối tượng:
BufferedReader in=new BufferedReader(new
InputStreamReader(client.getInputStream()));
PrintWriter out=PrintWriter(client.getOutputStream(),true);
Khi thực hiện xong cần hủy bỏ các liên kết để trả lại tài nguyên cho hệ thống, chúng
ta sử dụng phương thức: In.close(); Out.close(); Client.close(); lOMoARcPSD| 36625228 Ứng dụng clinet-server
Hoạt động của một ứng dụng mạng Client/Server
Chương 2 : CƠ SỞ LÝ THUYẾT
2.1 Các giao thức mạng Giao thức là gì
Giao thức hay còn gọi là nghi thức là các phương tiện để làm cho sự thông tin trở
nên khả hữu. Một quyết định phải được thực hiện khi hai hay nhiều máy tính muốn gởi và nhận dữ liệu.
2.1.1. Giao thức IP
(Internet Protocol - Giao thức Liên mạng) là một giao thức hướng dữ liệu được sử
dụng bởi các máy chủ nguồn và đích để truyền dữ liệu trong một liên mạng chuyển mạch gói.
Dữ liệu trong một liên mạng IP được gửi theo các khối được gọi là các gói (packet
hoặc datagram). Cụ thể, IP không cần thiết lập các đường truyền trước khi một máy chủ lOMoARcPSD| 36625228 Ứng dụng clinet-server
gửi các gói tin cho một máy khác mà trước đó nó chưa từng liên lạc với. Giao thức IP cung
cấp một dịch vụ gửi dữ liệu không đảm bảo (còn gọi là cố gắng cao nhất ), nghĩa là nó hầu
như không đảm bảo gì về gói dữ liệu. Gói dữ liệu có thể đến nơi mà không còn nguyên
vẹn, nó có thể đến không theo thứ tự (so với các gói khác được gửi giữa hai máy nguồnvà
đích đó), nó có thể bị trùng lặp hoặc bị mất hoàn toàn. Nếu một phần mềm ứng dụng cần
được bảo đảm, nó có thể được cung cấp từ nơi khác, thường từ các giao thức giao vận nằm
phía trên IP. Các thiết bị định tuyến liên mạng chuyển tiếp các gói tin IP qua các mạng tầng
liên kết dữ liệu được kết nối với nhau. Việc không có đảm bảo về gửi dữ liệu có nghĩa rằng
các chuyển mạch gói có thiết kế đơn giản hơn. (Lưu ý rằng nếu mạng bỏ gói tin, làm đổi
thứ tự hoặc làm hỏng nhiều gói tin, người dùng sẽ thấy hoạt động mạng trở nên kém đi.
Hầu hết các thành phần của mạng đều cố gắng tránh để xảy ra tình trạng đó. Đó là lý do
giao thức này còn được gọi là cố gắng cao nhất. Tuy nhiên, khi lỗi xảy ra không thường
xuyên sẽ không có hiệu quả đủ xấu đến mức người dung nhận thấy được). Giao thức IP rất
thông dụng trong mạng Internetcông cộng ngày nay. Giao thức tầng mạng thong dụng nhất
ngày nay là IPv4; phiên bản từ 0 đến 3 hoặc bị hạn chế, hoặc không được sử dụng. Phiên
bản 5 được dùng làm giao thức dòng (stream) thử nghiệm. Còn có các phiên bản khác,
nhưng chúng thường dành là các giao thức thử nghiệm và không được sử dụng rộng rãi.
Kề từ khi chính thức được đưa vào sử dụng và được định nghĩa trong kiến nghị
RFC791 năm 1981 đến nay, cho tới bây giờ phiên bản này vẫn đang được sử dụng rộng rãi
và cũng đã góp phần tạo ra sự phát triển bùng nổ của các mạng máy tính.
2.1.2. Giao thức TCP
Là giao thức hướng kết nối, nó cung cấp một hoạt động truyền tin tin cậy. TCP chịu
trách nhiệm phân chia dữ liệu gửi thành các segment tại máy gửi và lắp gép các segment
lại tại máy đích, trong quá trình truyền có thể truyền lại bất cứ segment nào nếu máy đích
chưa nhận được. Gói tin TCP có dạng sau: lOMoARcPSD| 36625228 Ứng dụng clinet-server
Source port: Số hiệu của cổng gọi (16 bits).
Destination Port : Số hiệu của cổng đích (16 bits).
Sequence Number: Chữa số đảm bảo tuần tự chính xác của dữ liệu đến, giống như số thứ tự (32 bits).
Acknowledgment Number (ACK): dùng trong các gói dữ liệu hồi đáp của máy nhận
cho máy gửi, báo hiệu để máy gửi biết lượng dữ liệu mà máy nhận đã nhận được và yêu
cầu gửi dữ liệu tiếp theo (32 bits).
Header Length: Số lượng các từ 32 bit trong header (32 bits).
Reserved : Set thành zero (6 bits).
Code Bits: Các chức năng điều khiển như là thiết lập và kết thúc một phiên, nó giống
như cờ gồm 6 bits.1.Cờ URG.2.Cờ ACK dùng để xác nhận.3.Cờ PSH (push) yêu cầu xóa
vùng đệm.4.Cờ RST (Reset) tái thiết lập.5.Cờ SYN (Synchronic) đồng bộ.6.Cờ FIN
(finish) Kết thúc, sử dụng khi muốn hủy kết nối.
Window: ghi kích thước cửa sổ của máy gửi báo cho máy nhận biết có gửi tín hiệu
trở lại thì không được vượt quá kích thước này(16 bits). lOMoARcPSD| 36625228 Ứng dụng clinet-server
Checksum: tính từ header và các trường dữ liệu(16 bits).
Urgent: Chỉ ra điểm kết thúc của dữ liệu chuẩn(16 bits).
Options: Một tùy chọn định ra kích thước tối đa hiện hành của gói TCP.
Data: Dữ liệu giao thức của lớp trên. 2.2.
MÔ HÌNH CLIENT-SERVER
Các ứng dụng mạng thường hoạt động theo mô hình client/server như thư điện tử,
truyền nhận tập tin, game trên mạng, ... Mô hình này gồm có một chương trình đóng vai
trò là client và một chương trình đóng vai trò là server. Hai chương trình này sẽ giao tiếp
với nhau thông qua mạng. Chương trình server đóng vai trò cung cấp dịch vụ. Chương
trình này luôn luôn lắng nghe các yêu cầu từ phía client, rồi tính toán và đáp trả kết quả
tương ứng. Chương trình client cần một dịch vụ và gởi yêu cầu dịch vụ tới chương trình
server và đợi đáp trả từ server. Như vậy, quá trình trao đổi dữ liệu giữa client/server bao gồm:
Truyền một yêu cầu từ chương trình client tới chương trình server.
Yêu cầu được server xử lý.
Truyền đáp ứng cho client. lOMoARcPSD| 36625228 Ứng dụng clinet-server
Mô hình truyền tin này thực hiện truyền hai thông điệp qua lại giữa client và server
một cách đồng bộ hóa. Chương trình server nhận được thông điệp từ client thì nó phát ra
yêu cầu client chuyển sang trạng thái chờ (tạm dừng) cho tới khi client nhận được thông
điệp đáp ứng do server gửi về. 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) và nhận (receive).
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 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 đó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à 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 xuất tới tài nguyên phần cứng và phần mềm dùng chung. Bản thân các trình
quản lý tài 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 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 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 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 yêu cầu và gửi một đáp ứng trả lời tới tiến trình client.
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
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 lOMoARcPSD| 36625228 Ứng dụng clinet-server
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
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
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 có thể sử dụng dịch vụ của một server khác.
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ữ liệu bao gồm:
Truyền một yêu cầu từ tiến trình client tới tiến trình server
Yêu cầu được server xử lý
Truyền đáp ứng cho client
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ộ 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 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 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 ứng do server gửi về ở bước ba.
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) và nhận (receive)
Quá trình giao tiếp client và server có thể diễn ra theo một trong hai chế độ: bị
phong tỏa (blocked) và không bị phong tỏa (non-blocked).
Chế độ bị phong tỏa (blocked):
Trong chế độ bị phong tỏa, khi tiến trình client/server phát ra lệnh gửi dữ liệu (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 nhận dữ liệu (receive).
Tương tự đối với tiến trình nhận dữ liệu, nếu tiến trình nào đó (client *** server)
phát 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 trình cũng sẽ bị tạm ngừng cho tới khi có dữ liệu gửi tới.
Chế độ không bị phong tỏa (non-blocked) lOMoARcPSD| 36625228 Ứng dụng clinet-server
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 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 ra lệnh nhận dữ liệu đó hay không.
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ẽ 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 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. 2.3.
LẬP TRÌNH SOCKET Socket là gì?
Sockets cung cấp một interface để lập trình mạng tại tầng Transport. Một socket là
một end-point của một liên kết giữa hai ứng dụng. Ngày nay, Socket được hỗ trợ trong hầu
hết các hệ điều hành như MS Windows (WinSock), Linux và được sử dụng trong nhiều
ngôn ngữ lập trình khác nhau: như C, C++, Java, Visual Basic, C#, . . .Windows Socket
Application Programming Interface (Winsock API) là một thư viện các hàm socket.
Winsock hỗ trợ các lập trình viên xây dựng các ứng dụng mạng trên nền TCP/IP.
Xây dựng ứng dụng Client-Server với Socket
Khi xây dụng một ứng dụng mạng, chúng ta thực hiện các bước sau:
1. Xác định kiến trúc mạng: Client – Server, Peer-to-Peer.
2. Giao thức sử dụng tầng Transport: TCP, UDP.
3. Các port sử dụng ở Server và Client.
4. Giao thức tầng ứng dụng khi trao đổi dữ liệu giữa hai end-host. 5. Lập trình.
Phần này trình bày các bước cơ bản trong việc xây dựng các ứng dụng mạng theo
kiến trúc Client-Server và giao thức sử dụng ở tầng Transport là TCP bằng Socket. lOMoARcPSD| 36625228 Ứng dụng clinet-server
Sơ đồ tương tác giữa Server - Client theo giao thức TCP
Trong giai đoạn truyền nhận dữ liệu, việc trao đổi dữ liệu giữa Client và Server
phải tuân thủ theo giao thức của ứng dụng. Ghi chú:
Nếu chúng ta phát triển ứng dụng theo các giao thức đã định nghĩa sẵn, chúng ta
phải tham khảo và tuân thủ đúng những qui định của giao thức (tham khảo trong các tài
liệu RFC (Request For Comments)).
Nếu xây dựng ứng dụng dạng Peer -to-Peer, thì một ứng dụng phải có cả chức
năng client và server trong mô hình trên. lOMoARcPSD| 36625228 Ứng dụng clinet-server
Mô hình truyền tin socket
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
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 và nhận.
Trong chương 1 chúng ta đã biết hai giao thức TCP và UDP là các giao thức tầng
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,
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 đ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 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 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 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 sẽ sử dụng lớp DatagramSocket và DatagramPacket.
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 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) 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 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 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,
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
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.
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 truyền lại 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 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 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 hiện, và đòi hỏi rất nhiều phần mềm phức tạp.
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 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à một luồng mà có thể đọc dữ liệu ra hay ghi dữ liệu vào từ luồng này. lOMoARcPSD| 36625228 Ứng dụng clinet-server
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
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,
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 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 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…
Một socket có thể thực hiện bảy thao tác cơ bản:
Kết nối với một máy ở xa (ví dụ, chuẩn bị để gửi và nhận dữ liệu) - Gửi dữ liệu - Nhận dữ liệu - Ngắt liên kêt - Gán cổng - Nghe dữ liệu đến
- Chấp nhận liên kết từ các máy ở xa trên cổng đã được gán
Lớp Socket của Java được sử dụng bởi cả client và server, có các phương thức 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 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 client
thường được sử dụng theo mô hình sau:
Một socket mới được tạo ra bằng cách sử dụng hàm Socket().
Socket cố gắng liên kết với một host ở xa.
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ừ
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 là
song công (full-duplex)-các host có thể nhận và gửi dữ liệu đồng thời. Ý nghĩa của dữ liệu
phụ thuộc vào giao thức. lOMoARcPSD| 36625228 Ứng dụng clinet-server
Khi việc truyền dữ liệu hoàn thành, một và cả hai phía ngắt liên kết. Một số giao
thức, như HTTP, đòi hỏi mỗi liên kết phải bị đóng sau mỗi khi yêu cầu được phục vụ. Các
giao thức khác, chẳng hạn FTP, cho phép nhiều yêu cầu được xử lý trong một liên kết đơn.
Socket cho Client Các constructor public Socket(String host, int port) throws
UnknownHostException, IOException
Hàm này tạo một socket TCP với host và cổng xác định, và thực hiện liên kết với host ở xa.
Trong hàm này tham số host là hostname kiểu String, nếu host không xác định và
máy chủ tên miền không hoạt động thì constructor đưa ra ngoại lệ UnknownHostException.
Vì một lý do nào đó mà không thể mở được socket thì constructor sẽ đưa ra ngoại lệ
IOException. Có nhiều nguyên nhân khiến cho một liên kết thất bại: host mà ta đang cố
gắng kết nối tới không chấp nhận liên kết, kết nối Internet có thể bị ngắt, vấn đề định tuyến
có thể ngăn ngừa các gói tin của ta tới đích.
Ví dụ: Viết chương trình để kiểm tra trên 1024 cổng đầu tiên những cổng nào đang có server hoạt động
public Socket(InetAddress host, int port)throws IOException
Tương tự như constructor trước, constructor này tạo một socket TCP với thông tin
là địa chỉ của một host được xác định bởi một đối tượng InetAddres và số hiệu cổng port,
sau đó nó thực hiện kết nối tới host. Nó đưa ra ngoại lệ IOException nhưng không đưa ra
ngoại lệ UnknownHostException. Constructor đưa ra ngoại lệ trong trường hợp không kết nối được tới host.
public Socket (String host, int port, InetAddress interface, int localPort) throws
IOException, UnknownHostException
Constructor này tạo ra một socket với thông tin là địa chỉ IP được biểu diễn bởi một
đối tượng String và một số hiệu cổng và thực hiện kết nối tới host đó. Socket kết nối tới
host ở xa thông qua một giao tiếp mạng và số hiệu cổng cục bộ được xác định bởi hai tham lOMoARcPSD| 36625228 Ứng dụng clinet-server
số sau. Nếu localPort bằng 0 thì Java sẽ lựa chọn một cổng ngẫu nhiên có sẵn nằm trong
khoảng từ 1024 đến 65535.
public Socket (InetAddress host, int port, InetAddress interface, int localPort)
throws IOException, UnknownHostException
Constructor chỉ khác constructor trên ở chỗ địa chỉ của host lúc này được biểu diễn
bởi một đối tượng InetAddress.
Nhận các thông tin về Socket
Đối tượng Socket có một số trường thông tin riêng mà ta có thể truy nhập tới
chúng thông qua các phương thức trả về các thông tin này.
public InetAddress getInetAddress()
Cho trước một đối tượng Socket, phương thức getInetAddress() cho ta biết host ở xa mà
Socket kết nối tới, liên kết đã bị ngắt thì nó cho biết host ở xa mà Socket đã kết nối tới public int getPort()
Phương thức này cho biết số hiệu cổng mà Socket kết nối tới trên host ở xa. public int getLocalPort()
Thông thường một liên kết thường có hai đầu: host ở xa và host cục bộ. Để tìm ra số hiệu
cổng ở phía host cục bộ ta gọi phương thức getLocalPort().
public InetAddress getLocalAddress()
Phương thức này cho ta biết giao tiếp mạng nào mà một socket gắn kết với nó.
public InputStream getInputStream() throws IOException
Phương thức geInputStream() trả về một luồng nhập để đọc dữ liệu từ một socket
vào chương trình. Thông thường ta có thể gắn kết luồng nhập thô InputStream tới một
luồng lọc và một luồng ký tự nhằm đưa các chức năng tiện ích (chẳng hạn như các luồng
InputStream, InputStreamReader). Để tâng cao hiệu năng, ta có thể đệm dữ liệu bằng cách
gắn kết nó với luồng lọc BufferedInputStream BufferedReader. lOMoARcPSD| 36625228 Ứng dụng clinet-server
public OutputStream getOutputStream() throws IOException
Phương thức getOutputStream() trả về một luồng xuất thô để ghi dữ liệu từ ứng
dụng ra đầu cuối của một socket. Thông thường, ta sẽ gắn kết luồng này với một luồng tiện
lợi hơn như lớp DataOuputStream OutputStreamWriter trước khi sử dụng nó. Để tăng hiệu quả ghi.
Hai phương thức getInputStream() và getOutputStream() là các phương thức cho
phép ta lấy về các luồng dữ liệu nhập và xuất. Như đã đề cập ở chương 3 vào ra trong Java
được tiến hành thông qua các luồng, việc làm việc với các socket cũng không phải là một
ngoại lệ. Để nhận dữ liệu từ một máy ở xa ta nhận về một luồng nhập từ socket và đọc dữ
liệu từ luồng đó. Để ghi dữ liệu lên một máy ở xa ta nhận về một luồng xuất từ socket và
ghi dữ liệu lên luồng. Dưới đây là hình vẽ để ta hình dung trực quan hơn. + InputStream + OutputStream + Socket + Chương trình Đóng Socket
Đến thời điểm ta đã có đầy đủ các thông tin cần thiết để triển khai một ứng dụng
phía client. Khi viết một chương trình ứng dụng phía client tất cả mọi công việc đều chuyển
về việc quản lý luồng và chuyển đổi dữ liệu từ luồng thành dạng thức mà người sử dụng
có thể hiểu được. Bản thân các socket rất đơn giản bởi vì các phần việc phức tạp đã được
che dấu đi. Đây chính là lý do để socket trở thành một lựa chọn có tính chiến lược cho lập trình mạng.
public void close() throws IOException
Các socket được đóng một cách tự động khi một trong hai luồng đóng lại, khi
chương trình kết thúc, khi socket được thu hồi bởi gabbage collector. Tuy nhiên, thực tế
cho thấy việc cho rằng hệ thống sẽ tự đóng socket là không tốt, đặc biệt là khi các chương lOMoARcPSD| 36625228 Ứng dụng clinet-server
trình chạy trong khoảng thời gian vô hạn. Để đóng một socket ta có thể dùng phương thức close().
Mỗi khi một Socket đã bị đóng lại, ta vẫn có thể truy xuất tới các trường thông tin
InetAddress, địa chỉ cục bộ, và số hiệu cổng cục bộ thông qua các phưong thức
getInetAddress(), getPort(), getLocalHost(), và getLocalPort(). Tuy nhiên khi ta gọi các
phương thức getInputStream() getOutputStream() để đọc dữ liệu từ luồng đọc InputStream
ghi dữ liệu OuputStream thì ngoại lệ IOException được đưa ra.
Các socket đóng một nửa (Half-closed socket)
Phương thức close() đóng cả các luồng nhập và luồng xuất từ socket. Trong một số
trường hợp ta chỉ muốn đóng một nửa kết nối là luồng nhập là luồng xuất. Bắt đầu từ Java
1.3, các phương thưc shutdownInput() và shutdownOutput() cho phép ta thực hiện điều này.
public void shutdownInput() throws IOException
public void shutdownOutput() throws IOException
Các phương thức này không thực sự ngắt liên kết. Tuy nhiên, nó chỉ điều chỉnh
luồng kết nối tới nó sao cho.
Trong Java đưa thêm vào hai phương thức các luồng nhập và luồng xuất mở hay đóng
- public boolean isInputShutdown()
- public boolean isOutputShutdown()
Thiết lập các tùy chọn cho Socket TCP_NODELAY
- public void setTcpNoDelay(boolean on) throws SocketException
- public boolean getTcpNoDelay() throws SocketException lOMoARcPSD| 36625228 Ứng dụng clinet-server
Thiết lập giá trị TCP_NODELAY là true để đảm bảo rằng các gói tin được gửi đi
nhanh nhất có thể mà không quan tâm đến kích thước của chúng. Thông thường, các gói
tin nhỏ được kết hợp lại thành các gói tin lớn hơn trước khi được gửi đi. Trước khi gửi đi
một gói tin khác, host cục bộ đợi để nhận các xác thực của gói tin trước đó từ hệ thống ở xa. SO_LINGER
- public void setSoLinger(boolean on, int seconds) throws SocketException
- public int getSoLinger() throws SocketException
Tùy chọn SO_LINGER xác định phải thực hiện công việc gì với datagram vẫn chưa
được gửi đi khi một socket đã bị đóng lại. Ở chế độ mặc định, phương thức close() sẽ có
hiệu lực ngay lập tức; nhưng hệ thống vẫn cố gắng để gửi phần dữ liệu còn lại. Nếu
SO_LINGER được thiết lập bằng 0, các gói tin chưa được gửi đi bị phá hủy khi socket bị
đóng lại. Nếu SO_LINGER lớn hơn 0, thì phương thức close() phong tỏa để chờ cho dữ
liệu được gửi đi và nhận được xác thực từ phía nhận. Khi hết thời gian qui định, socket sẽ
bị đóng lại và bất kỳ phần dữ liệu còn lại sẽ không được gửi đi. SO_TIMEOUT
- public void setSoTimeout(int milliseconds) throws SocketException
- public int getSoTimeout() throws SocketException
Thông thường khi ta đọc dữ liệu từ mộ socket, lời gọi phương thức phong tỏa cho
tới khi nhận đủ số byte. Bằng cách thiết lập phương thức SO_TIMEOUT, ta sẽ đảm bảo
rằng lời gọi phương thức sẽ không phong tỏa trong khoảng thời gian quá số giây quy định.
Các phương thức của lớp Object
Lớp Socket nạp chồng phương thức chuẩn của lớp java.lang.Object, toString(). Vì
các socket là các đối tượng tạm thời và thường chỉ tồn tại khi liên kết tồn tại. - public String toString() lOMoARcPSD| 36625228 Ứng dụng clinet-server
Phương thức toString() tạo ra một xâu ký tự như sau:
Socket[addr=www.oreilly.com/198.122.208.11,port=80,localport=50055]
Phương thức này thường hữu ích cho việc gỡ rối.
Các ngoại lệ Socket
Hầu hết các phương thức của lớp Socket được khai báo đưa ra ngoại lệ
IOException, và lớp con của lớp IOExcepton là lớp SocketException.
Các lớp SocketAddress
Lớp SocketAddress bắt đầu có từ phiên bản Java 1.4, biểu diễn một đầu cuối của
liên kết. Lớp SocketAddress là một lớp trừu tượng mà không có phương thức nào ngoài
construtor mặc định. Lớp này có thể được sử dụng cho cả các socket TCP và socket không
phải là TCP. Các lớp con của lớp SocketAddress cung cấp thông tin chi tiết hơn thích hợp
cho kiểu socket. Trong thực tế, chỉ hỗ trợ TCP/IP.
Mục đích chính của lớp SocketAddress là cung cấp một nơi lưu trữ các thông tin
liên kết socket tạm thời (như địa chỉ IP và số hiệu cổng) có thể được sử dụng lại để tạo ra socket mới.
- public SocketAddress getRemoteSocketAddress()
- public SocketAddress getLocalSocketAddress()
Cả hai phương thức này trả về giá trị null nếu socket vẫn chưa kết nối tới. Lớp ServerSocket
Lớp ServerSocket có đủ mọi thứ ta cần để viết các server bằng Java. Nó có các
constructor để tạo các đối tượng ServerSocket mới, các phương thức để lắng nghe các liên
kết trên một cổng xác định, và các phương thức trả về một Socket khi liên kết được thiết
lập, vì vậy ta có thể gửi và nhận dữ liệu.
Một ServerSocket mới được tạo ra trên một cổng xác định bằng cách sử dụng một constructor ServerSocket. lOMoARcPSD| 36625228 Ứng dụng clinet-server
ServerSocket lắng nghe liên kết đến trên cổng đó bằng cách sử dụng phương thức
accept(). Phương thức accept() phong tỏa cho tới khi một client thực hiện một liên kết,
phương thức accept() trả về một đối tượng Socket mà liên kết giữa client và server.
Tùy thuộc vào kiểu server, phương thức getInputStream(), getOutputStream() cả
hai được gọi để nhận các luồng vào ra để truyền tin với client.
Server và client tương tác theo một giao thức thỏa thuận sẵn cho tới khi ngắt liên kết.
Server, client cả hai ngắt liên kết
Server trở về bước hai và đợi liên kết tiếp theo.
+ Các constructor
Public ServerSocket(int port) throws IOException, BindException
Constructor này tạo một socket cho server trên cổng xác định. Nếu port bằng 0, hệ
thống chọn một cổng ngẫu nhiên cho ta. Cổng do hệ thống chọn đôi khi được gọi là cổng
vô danh vì ta không biết số hiệu cổng. Với các server, các cổng vô danh không hữu ích lắm
vì các client cần phải biết trước cổng nào mà nó nối tới (giống như người gọi điện thoại
ngoài việc xác định cần gọi cho ai cần phải biết số điện thoại để liên lạc với người đó).
+ Chấp nhận và ngắt liên kết
Một đối tượng ServerSocket hoạt động trong một vòng lặp chấp nhận các liên kết.
Mỗi lần lặp nó gọi phương thức accept(). Phương thức này trả về một đối tượng Socket
biểu diễn liên kết giữa client và server. Tương tác giữ client và server được tiến hành thông
qua socket này. Khi giao tác hoàn thành, server gọi phương thức close() của đối tượng
socket. Nếu client ngắt liên kết trong khi server vẫn đang hoạt động, các luồng vào ra kết
nối server với client sẽ đưa ra ngoại lệ InterruptedException trong lần lặp tiếp theo
Public Socket accept() throws IOException
Khi bước thiết lập liên kết hoàn thành, và ta sẵn sàng để chấp nhận liên kết, cần gọi
phương thức accept() của lớp ServerSocket. Phương thức này phong tỏa; nó dừng quá trình lOMoARcPSD| 36625228 Ứng dụng clinet-server
xử lý và đợi cho tới khi client được kết nối. Khi client thực sự kết nối, phương thức accept()
trả về đối tượng Socket. Ta sử dụng các phương thức getInputStream() và
getOutputStream() để truyền tin với client.
Public void close() throws IOException
Nếu ta đã kết thúc làm việc với một đối tượng server socket thì cần phải đóng lại đối tượng này.
Public void close() throws IOException
Nếu đã hoàn thành công việc với một ServerSocket, ta cần phải đóng nó lại, đặc biệt
nếu chương trình của ta tiếp tục chạy. Điều này nhằm tạo điều kiện cho các chương trình
khác muốn sử dụng nó. Đóng một ServerSocket không đồng nhất với việc đóng một Socket.
Lớp ServerSocket cung cấp một số phương thức cho ta biết địa chỉ cục bộ và cổng
mà trên đó đối tượng server đang hoạt động. Các phương thức này hữu ích khi ta đã mở
một đối tượng server socket trên một cổng vô danh và trên một giao tiếp mạng không
Public InetAddress getInetAddress()
Phương thức này trả về địa chỉ được sử dụng bởi server (localhost). Nếu localhost
có địa chỉ IP, địa chỉ này được trả về bởi phương thức InetAddress.getLocalHost() Public int getLocalHost()
Các contructor ServerSocket cho phép ta nghe dữ liệu trên cổng không định trước
bằng cách gán số 0 cho cổng. Phương thức này cho phép ta tìm ra cổng mà server đang nghe. lOMoARcPSD| 36625228 Ứng dụng clinet-server
Chương 3: MÔ TẢ ỨNG DỤNG 3.1.
GIỚI THIỆU ỨNG DỤNG
Ứng dụng được xây dựng nhằm mục đích chia sẻ audio giữa 1 máy (tạm gọi là
server) và các máy còn lại trong 1 nhóm nhất định (các máy client). Client:
Khi các máy client kết nối vào server thì các client sẽ nhận được các đoạn thông
báo được gửi bởi server và phát ra âm thanh ở phía client. Server:
Server nhận các đoạn chat từ các client và bắt đầu gửi tin nhắn (dưới dạng
Stream) cho các client khác.
Server sẽ biết được danh sách các client đang kết nối tới mình (mỗi client kết nối sẽ
được hiển thị lên bảng tại server). Server sẽ biết được thông tin chi tiết của từng Client và
gửi danh sách nick name cho các client còn lại.
Server có quyền đóng kết nối và không cho bất cứ 1 client nào kết nối. 3.2.
KIẾN TRÚC ỨNG DỤNG
3.2.1. Gói server
Lắng nghe và chấp nhận kết nối từ các Client.
Nhận và gửi thông điệp cho các Client.
3.2.2. Gói client
Kết nối và gửi thông điệp cho Server.
Chương 4 : Hướng dẫn cài đặt và chạy Demo chương trình lOMoARcPSD| 36625228 Ứng dụng clinet-server
1. Hướng dẫn cài đặt
Chương trình gồm 2 phần chính là : Client và Server. Muốn chương trình chạy
được thì ta phải chạy Server trước để lắng nghe các Client. Sau đó chạy các Client
Chạy Server ta Run File ServerFrame là Main
Chạy Client ta Run File ChatFramme là Main
2. Chạy Demo Chương trình
Thử nghiệm chạy chương trình lần lượt theo các bước kết quả được hiển thị qua các hình
Hình 2.1 Chạy chương trình Server lOMoARcPSD| 36625228 Ứng dụng clinet-server
Hính 2.2 Chạy Client Kết nối và đăng nhập thành công
Hình 2.3 Server thông báo có người đã kết nối
• Sau khi Server đã được bật thì đã kết nối được thành công
• Tiến hành kết nối và đăng nhập thêm nhiều Client khác lOMoARcPSD| 36625228 Ứng dụng clinet-server
Hình 2.4 Chat Room lOMoARcPSD| 36625228 Ứng dụng clinet-server
Hình 2.5 Khi bên client ngắt kết nối với server I.
Kết Luận và hướng phát triển
1. Những kết quả đạt được
Mô phỏng thành công mô hình Client – Server
Thực hiện đầy đủ yêu cầu bài ra: Đăng nhập, đăng xuất, List OnLine, Chat Room,
2. Những vấn đề tồn tại Chương trình còn đơn giản Giao diện sơ sài lOMoARcPSD| 36625228 Ứng dụng clinet-server
3. Hướng phát triển
Hoàn thiện giao diện đẹp phù hợp với người dùng II. Source code
1. Chương trình bên Server 1.1ClientHandle class
package sever; import java.io.*; import java.text.*; import
java.util.*; import java.net.*;
import java.util.logging.Level;
import java.util.logging.Logger; import Message.Message; /** * * @author Admin */
public class ClientHandle extends Thread { String name;
ObjectInputStream inObj = null; lOMoARcPSD| 36625228 Ứng dụng clinet-server
ObjectOutputStream outObj = null;
Socket s = null; boolean login = true; public ClientHandle(){ }
public ClientHandle(Socket client) throws IOException{
this.s = client; Sever.ar.add(this); outObj = new
ObjectOutputStream(s.getOutputStream()); outObj.flush(); inObj = new
ObjectInputStream(s.getInputStream()); this.start(); } public void run() { Message(); }
public String GetClientName(){ return this.name; }
public void sendOnline(Message msg){
try { outObj.writeObject(msg); outObj.flush(); lOMoARcPSD| 36625228 Ứng dụng clinet-server } catch (IOException ex) {
Logger.getLogger(ClientHandle.class.getName()).log(Level.SEVERE, null, ex); } }
public void Message(){ Thread read; read = new Thread(new
Runnable(){ public void run(){ try{
outObj.writeObject(new Message("Thong Bao","Ket noi thanh cong",""));
Message msg; msg = (Message) inObj.readObject(); name =
msg.sender; System.out.println(name); OnlineList(); while(true){ Message received;
received = (Message) inObj.readObject(); String mess = received.msg;
String recipient = received.recipient;
if(mess.equals("Exit")){ OnlineList2(); break;
} if(recipient.equals("All")){
for(ClientHandle mc : Sever.ar){
mc.outObj.writeObject(new Message(name,mess,"")); mc.outObj.flush(); } break; }
else{ for (ClientHandle mc : Sever.ar){
if (mc.name.equals(recipient) && mc.login ==true){ lOMoARcPSD| 36625228 Ứng dụng clinet-server
mc.outObj.writeObject(new Message(name,mess,"")); mc.outObj.flush(); break; } } } } }catch(Exception e){ } } }); read.start(); } public void OnlineList() {
Thread OnlineList = new Thread(new
Runnable(){ public void run(){ int i = 0;
for(ClientHandle mc:Sever.ar){ i = 0;
for(ClientHandle mt:Sever.ar){ try {
mc.outObj.writeObject(new Message(mt.name,"",Integer.toString(i))); } catch (IOException ex) {
Logger.getLogger(ClientHandle.class.getName()).log(Level.SEVERE, null, ex); } i++; lOMoARcPSD| 36625228 Ứng dụng clinet-server } i = 0; } } }); OnlineList.start(); } public void OnlineList2(){ ClientClose(); int i =0; System.out.println("Exit");
Sever.txtMessageBoard.setText(Sever.txtMessageBoard.getText() + "Co
nguoi thoat\n"); for(ClientHandle mc:Sever.ar){ i = 0;
for(ClientHandle mt:Sever.ar){ try {
mc.outObj.writeObject(new Message(mt.name,"",Integer.toString(i))); } catch (IOException ex) {
Logger.getLogger(ClientHandle.class.getName()).log(Level.SEVERE, null, ex); } i++; } i = 0; } } lOMoARcPSD| 36625228 Ứng dụng clinet-server public void ClientClose(){ Sever.ar.remove(this); } public void close(){ try { this.login=false; outObj.close(); inObj.close(); s.close(); }catch (Exception e){ e.printStackTrace(); } } }
1.2 Server class package sever; import java.io.*; import
java.text.*; import java.util.*; import java.net.*; import
java.util.logging.Level; import
java.util.logging.Logger; import javax.swing.*; lOMoARcPSD| 36625228 Ứng dụng clinet-server
public class Sever extends Thread { private
ServerSocket sever =null; private Socket socket = null;
public static JTextPane txtMessageBoard; public static
Vector ar = new Vector<>(); static int i = 0;
public Sever(int port, JTextPane txtMessageBoard) throws IOException{
this.sever = new ServerSocket(port);
this.txtMessageBoard = txtMessageBoard; this.start(); } public void run(){ try{ while(true){ //ui.in ra Messageboard
txtMessageBoard.setText(txtMessageBoard.getText()+ "Dang doi ket noi ....\n"); this.socket = sever.accept();
txtMessageBoard.setText(txtMessageBoard.getText() + "Co nguoi ket noi\n");
ClientHandle client = new ClientHandle(socket); } }catch (Exception e){ e.printStackTrace(); } } lOMoARcPSD| 36625228 Ứng dụng clinet-server } 1.3ServerFrame class package sever; import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class SeverJframe extends javax.swing.JFrame { Sever Server; public SeverJframe() { initComponents(); } /**
* This method is called from within the constructor to initialize the form. *
WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor. */
@SuppressWarnings("unchecked") //
private void initComponents() { lOMoARcPSD| 36625228 Ứng dụng clinet-server
jLabel1 = new javax.swing.JLabel();
jSeparator1 = new javax.swing.JSeparator();
jLabel2 = new javax.swing.JLabel(); btStart =
new javax.swing.JButton(); jScrollPane2 = new
javax.swing.JScrollPane(); txpMessageBoard = new javax.swing.JTextPane();
TxtPort = new javax.swing.JTextField();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jLabel1.setFont(new java.awt.Font("Segoe UI", 1, 18)); // NOI18N
jLabel1.setText("Chat Server"); jLabel2.setText("Port:");
btStart.setText("Start"); btStart.addActionListener(new
java.awt.event.ActionListener() { public void
actionPerformed(java.awt.event.ActionEvent evt) { btStartActionPerformed(evt); } });
jScrollPane2.setViewportView(txpMessageBoard);
TxtPort.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { TxtPortActionPerformed(evt); } lOMoARcPSD| 36625228 Ứng dụng clinet-server });
javax.swing.GroupLayout layout = new
javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout); layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jSeparator1)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGap(22, 22, 22)
.addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE,
38, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(TxtPort, javax.swing.GroupLayout.PREFERRED_SIZE,
71, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 198, Short.MAX_VALUE) .addComponent(btStart) .addGap(15, 15, 15))
.addGroup(layout.createSequentialGroup() .addContainerGap()
.addComponent(jScrollPane2))) .addContainerGap())
.addGroup(layout.createSequentialGroup() .addGap(165, 165, 165) lOMoARcPSD| 36625228 Ứng dụng clinet-server .addComponent(jLabel1)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGap(12, 12, 12) .addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE ) .addComponent(jLabel2)
.addComponent(TxtPort, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(btStart)) .addGap(18, 18, 18)
.addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE,
217, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(30, Short.MAX_VALUE)) ); lOMoARcPSD| 36625228 Ứng dụng clinet-server pack(); }//
private void TxtPortActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here: }
private void btStartActionPerformed(java.awt.event.ActionEvent evt) { try { //lay port
int port =Integer.parseInt(TxtPort.getText());
Server = new Sever(port,txpMessageBoard); } catch (IOException ex) {
Logger.getLogger(SeverJframe.class.getName()).log(Level.SEVERE, null, ex); } } /**
* @param args the command line arguments */
public static void main(String args[]) {
/* Set the Nimbus look and feel */ //
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. * For details see
http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/ try { for (javax.swing.UIManager.LookAndFeelInfo
info : javax.swing.UIManager.getInstalledLookAndFeels()) { if lOMoARcPSD| 36625228 Ứng dụng clinet-server
("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName()); break; } }
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(SeverJframe.class.getName()).log(java.util.logging.L evel.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(SeverJframe.class.getName()).log(java.util.logging.L evel.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(SeverJframe.class.getName()).log(java.util.logging.L evel.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(SeverJframe.class.getName()).log(java.util.logging.L evel.SEVERE, null, ex); } // //
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new
SeverJframe().setVisible(true); lOMoARcPSD| 36625228 Ứng dụng clinet-server } }); }
// Variables declaration - do not modify
public static javax.swing.JTextField TxtPort; private
javax.swing.JButton btStart; private
javax.swing.JLabel jLabel1; private
javax.swing.JLabel jLabel2; private
javax.swing.JScrollPane jScrollPane2; private
javax.swing.JSeparator jSeparator1; public static
javax.swing.JTextPane txpMessageBoard;
// End of variables declaration } lOMoARcPSD| 36625228 Ứng dụng clinet-server
2. Chương trình bên Client
2.1 Client class package
clieng; import java.io.*; import java.net.*; import
java.util.StringTokenizer; import java.util.Vector; import
java.util.logging.Level; import
java.util.logging.Logger; import javax.swing.*; import Message.Message;
public class Clieng extends Thread { Socket socket; String name; ObjectInputStream inObj; lOMoARcPSD| 36625228 Ứng dụng clinet-server ObjectOutputStream outObj; JTextField TxtMessage; JTextPane TxtMessageBoard; JTextPane txtOnlineBoard; JTextField txtName;
public Clieng(String Svid,int port,JTextPane TxtMessageBoard, JTextPane
OnlineBoard, JTextField txtName){
try { this.socket= new Socket(Svid,port);
outObj = new ObjectOutputStream(socket.getOutputStream()); outObj.flush();
inObj = new ObjectInputStream(socket.getInputStream()); } catch (IOException ex) {
Logger.getLogger(Clieng.class.getName()).log(Level.SEVERE, null, ex); }
this.TxtMessageBoard = TxtMessageBoard;
this.txtOnlineBoard = OnlineBoard;
this.txtName = txtName; this.name =
this.txtName.getText(); this.start(); } public void run(){ sendName(); read(); } public void read(){ lOMoARcPSD| 36625228 Ứng dụng clinet-server
Thread read = new Thread(new Runnable(){
public void run(){ Message msg; while(true){ try {
msg = (Message) inObj.readObject();
if(msg.recipient.equals("0")){ txtOnlineBoard.setText(""); } if(msg.msg.equals("")){
txtOnlineBoard.setText(txtOnlineBoard.get Text() + msg.sender + "\n"); } else{
TxtMessageBoard.setText(TxtMessageBoard.getText()+msg.sender + ":"+msg.msg + "\n"); } } catch (IOException ex) {
Logger.getLogger(Clieng.class.getName()).log(Level.SEVERE, null, ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(Clieng.class.getName()).log(Level.SEVERE, null, ex); } } } }); read.start(); lOMoARcPSD| 36625228 Ứng dụng clinet-server }
public void send(String msg){
Thread send = new Thread(new Runnable() { public void run(){ try{
StringTokenizer st = new StringTokenizer(msg,"#");
String mesg = st.nextToken();
String recipient = st.nextToken();
TxtMessageBoard.setText(TxtMessageBoard.getText() + "->" +
recipient +":" + mesg + "\n"); System.out.println(msg);
outObj.writeObject(new Message(name,mesg,recipient));
outObj.flush(); }catch(Exception e){ e.printStackTrace(); } } }); send.start(); } public void sendName(){
Thread send = new Thread(new Runnable() lOMoARcPSD| 36625228 Ứng dụng clinet-server { public void run(){ try{
outObj.writeObject(new Message(name,"",""));
outObj.flush(); }catch(Exception e){ e.printStackTrace(); } } }); send.start(); }
public void OnlineList(Message msg){ } public void close(){ try{ outObj.close(); inObj.close(); socket.close(); }catch(Exception e){ e.printStackTrace(); } }
private static Runnable newRunnable() { throw new
UnsupportedOperationException("Not supported yet."); // Generated from
nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody lOMoARcPSD| 36625228 Ứng dụng clinet-server } } 2.2 ClientFrame class package clieng; import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger; /** * * @author Admin */
public class ClientFrame extends javax.swing.JFrame { Clieng Client; /** * Creates new form NewJFrame */
@Override public void setUndecorated(boolean
undecorated) { super.setUndecorated(undecorated); // Generated from
nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/OverriddenMethodBody } lOMoARcPSD| 36625228 Ứng dụng clinet-server public ClientFrame() { initComponents(); } /**
* This method is called from within the constructor to initialize the form. *
WARNING: Do NOT modify this code. The content of this method is always *
regenerated by the Form Editor. */
@SuppressWarnings("unchecked") //
private void initComponents() {
jLabel1 = new javax.swing.JLabel();
jSeparator1 = new javax.swing.JSeparator();
jLabel2 = new javax.swing.JLabel(); btStart =
new javax.swing.JButton(); jScrollPane2 = new
javax.swing.JScrollPane(); txpMessageBoard =
new javax.swing.JTextPane(); SeverPort = new
javax.swing.JTextField(); jLabel3 = new
javax.swing.JLabel(); Severip = new
javax.swing.JTextField(); jLabel4 = new
javax.swing.JLabel(); TxtMessage = new
javax.swing.JTextField(); BtSend = new
javax.swing.JButton(); jLabel5 = new
javax.swing.JLabel(); jLabel6 = new
javax.swing.JLabel(); txtName = new lOMoARcPSD| 36625228 Ứng dụng clinet-server
javax.swing.JTextField(); jScrollPane1 = new
javax.swing.JScrollPane(); TxpOnlineBoard =
new javax.swing.JTextPane(); jLabel7 = new
javax.swing.JLabel(); ExitButton = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jLabel1.setFont(new java.awt.Font("Segoe UI", 1, 18)); // NOI18N
jLabel1.setText("Chat Client"); jLabel2.setText("Port:");
btStart.setText("Connect"); btStart.addActionListener(new
java.awt.event.ActionListener() { public void
actionPerformed(java.awt.event.ActionEvent evt) { btStartActionPerformed(evt); } });
jScrollPane2.setViewportView(txpMessageBoard);
SeverPort.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
SeverPortActionPerformed(evt); } }); lOMoARcPSD| 36625228 Ứng dụng clinet-server
jLabel3.setText("Server ip:");
Severip.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { SeveripActionPerformed(evt); } }); jLabel4.setText("Message:"); BtSend.setText("Send");
BtSend.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { BtSendActionPerformed(evt); } });
jLabel5.setText("Tin Nhan#Nguoi Nhan"); jLabel6.setText("Name:");
jScrollPane1.setViewportView(TxpOnlineBoard); jLabel7.setText("Online"); ExitButton.setText("Exit");
ExitButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { lOMoARcPSD| 36625228 Ứng dụng clinet-server
ExitButtonActionPerformed(evt); } });
javax.swing.GroupLayout layout = new
javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout); layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jSeparator1)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGap(17, 17, 17) .addComponent(jLabel3)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(Severip, javax.swing.GroupLayout.DEFAULT_SIZE, 143, Short.MAX_VALUE) .addGap(30, 30, 30)
.addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE,
26, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(12, 12, 12) .addComponent(SeverPort,
javax.swing.GroupLayout.PREFERRED_SIZE, 72,
javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) lOMoARcPSD| 36625228 Ứng dụng clinet-server .addComponent(jScrollPane2)
.addGroup(layout.createSequentialGroup() .addComponent(jLabel4)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addComponent(jLabel5) .addGap(147, 147, 147))
.addComponent(TxtMessage)))))) .addGap(34, 34, 34)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jScrollPane1,
javax.swing.GroupLayout.PREFERRED_SIZE, 81,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING,
layout.createSequentialGroup() .addComponent(btStart) .addGap(8, 8, 8)) .addComponent(BtSend)
.addGroup(layout.createSequentialGroup() .addGap(20, 20, 20)
.addComponent(jLabel7, javax.swing.GroupLayout.PREFERRED_SIZE,
37, javax.swing.GroupLayout.PREFERRED_SIZE))) .addGap(14, 14, 14))
.addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(jLabel1) .addGap(139, 139, 139) lOMoARcPSD| 36625228 Ứng dụng clinet-server
.addComponent(jLabel6, javax.swing.GroupLayout.PREFERRED_SIZE, 37,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(txtName,
javax.swing.GroupLayout.PREFERRED_SIZE, 72,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED,
javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(ExitButton) .addGap(22, 22, 22)) ); layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGap(9, 9, 9)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jLabel1)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE ) .addComponent(jLabel6) .addComponent(txtName,
javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE))))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING,
layout.createSequentialGroup() .addContainerGap() lOMoARcPSD| 36625228 Ứng dụng clinet-server .addComponent(ExitButton)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE ) .addComponent(jLabel2)
.addComponent(SeverPort, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(btStart) .addComponent(jLabel3)
.addComponent(Severip, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)) .addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addGroup(layout.createSequentialGroup() .addComponent(jLabel7)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED,
javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jScrollPane1,
javax.swing.GroupLayout.PREFERRED_SIZE, 187,
javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(jScrollPane2,
javax.swing.GroupLayout.PREFERRED_SIZE, 217,
javax.swing.GroupLayout.PREFERRED_SIZE)) lOMoARcPSD| 36625228 Ứng dụng clinet-server
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE ) .addComponent(jLabel4) .addComponent(TxtMessage,
javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(BtSend))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel5)
.addContainerGap(25, Short.MAX_VALUE)) ); pack(); }//
private void SeverPortActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here: }
private void btStartActionPerformed(java.awt.event.ActionEvent evt) {
String Serverip = this.Severip.getText(); int Port =
Integer.parseInt(this.SeverPort.getText());
Client = new Clieng(Serverip,
Port,this.txpMessageBoard,this.TxpOnlineBoard,this.txtName); lOMoARcPSD| 36625228 Ứng dụng clinet-server }
private void SeveripActionPerformed(java.awt.event.ActionEvent evt) { }
private void BtSendActionPerformed(java.awt.event.ActionEvent evt) {
String msg = this.TxtMessage.getText(); Client.send(msg); this.TxtMessage.setText(""); }
private void ExitButtonActionPerformed(java.awt.event.ActionEvent evt) {
Client.send("Exit#asdasdwqe"); this.TxpOnlineBoard.setText(""); System.exit(0); Client.close(); } /**
* @param args the command line arguments */
public static void main(String args[]) {
/* Set the Nimbus look and feel */ lOMoARcPSD| 36625228 Ứng dụng clinet-server //
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. * For details see
http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/ try { for (javax.swing.UIManager.LookAndFeelInfo
info : javax.swing.UIManager.getInstalledLookAndFeels()) { if
("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName()); break; } }
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(ClientFrame.class.getName()).log(java.util.logging.L evel.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(ClientFrame.class.getName()).log(java.util.logging.L evel.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(ClientFrame.class.getName()).log(java.util.logging.L evel.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(ClientFrame.class.getName()).log(java.util.logging.L evel.SEVERE, null, ex); } // lOMoARcPSD| 36625228 Ứng dụng clinet-server // // //
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new
ClientFrame().setVisible(true); } }); }
// Variables declaration - do not modify
private javax.swing.JButton BtSend; private
javax.swing.JButton ExitButton; private
javax.swing.JTextField SeverPort; private
javax.swing.JTextField Severip; private
javax.swing.JTextPane TxpOnlineBoard;
private javax.swing.JTextField TxtMessage;
private javax.swing.JButton btStart; private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2; private
javax.swing.JLabel jLabel3; private
javax.swing.JLabel jLabel4; private
javax.swing.JLabel jLabel5; private
javax.swing.JLabel jLabel6; private
javax.swing.JLabel jLabel7; private lOMoARcPSD| 36625228 Ứng dụng clinet-server
javax.swing.JScrollPane jScrollPane1; private
javax.swing.JScrollPane jScrollPane2; private
javax.swing.JSeparator jSeparator1; private
javax.swing.JTextPane txpMessageBoard;
private javax.swing.JTextField txtName;
// End of variables declaration }
3. Chương trình bên Messger
3.1 Messeger class (messeger class) package Message; import java.io.Serializable; /** * * @author Admin
*/ public class Message implements
Serializable{ private static final long
serialVersionUID = 1L; public String sender;
public String msg; public String recipient; lOMoARcPSD| 36625228 Ứng dụng clinet-server
public Message(String sender,String msg,String
recipient){ this.sender = sender; this.msg = msg; this.recipient = recipient;
} public String toString(){ return "{sender='"+sender+"',
message='"+msg+"', recipient='"+recipient+"'}"; } }