Professional Documents
Culture Documents
Email: hunght@fpt.vn
Nội dung:
1
I. KHÁI QUÁT VỀ SOCKET
Như chúng ta đã biết kết nối URLs và URL cung cấp cho chúng ta
một cơ cấu để truy xuất vào các tài nguyên trên Internet ở một mức tương
đối cao, nhưng đôi khi chương trình của chúng ta lại yêu cầu một giao tiếp
ở tầng mạng mức thấp. Ví dụ khi chúng ta viết một ứng dụng client-
server.
Trong một ứng dụng client-server thì phía server sẽ cung cấp một số
dịch vụ, như: xử lí cơ sở dữ liệu, các yêu cầu bên phía client đưa ra, sau
đó sẽ gửi lại cho phía client. Sự giao tiếp như vậy gọi là tin cậy bởi vì dữ
liệu sẽ không bị mất mát, sai lệch trong quá trình truyền, server gửi cho
client thông điệp gì thì phía client sẽ nhận được thông điệp nguyên như
vậy. Giao thức TCP sẽ cung cấp cho chúng ta một cách thức truyền tin
cậy. Để có thể nói chuyện được trên TCP thì chương trình client và
chương trình server phải thiếp lập một đường truyền, và mỗi chương trình
sẽ phải kết nối lại với socket là điểm cuối để kết nối, client và server muốn
nói chuyện với nhau thì sẽ phải thông qua socket, mọi thông điệp sẽ phải
đi qua socket. Chúng ta cứ mường tượng socket ở đây là một cái cửa mọi
người muốn đi ra hay đi vào đều phải thông qua cái cửa này.
II. SOCKET LÀ GÌ?
Một socket là một điểm cuối của thông tin hai chiều liên kết giữa hai
chương trình đang chạy trên mạng. Những lớp socket được dùng để đại
diện cho kết nối giữa một chương trình client và một chương trình server.
Trong Java gói Java.net cung cấp hai lớp Socket và ServerSocket để thực
hiện kết nối giữa client và server.
Thông thường thì server sẽ chạy trên một máy đặc biệt và có một
socket giới hạn trong một Port number đặc biệt.
Phía client: client được biết hostname của máy mà server đang
chạy và port number mà server đang lắng nghe. Để tạo một yêu cầu kết
2
nối client sẽ thử hẹn gặp server ở trên máy của server thông qua port
number. Client cũng cần xác định chính nó với server thông qua local
port number.
Nếu mọi thứ tốt đẹp thì server sẽ đồng ý kết nối. khi đồng ý kết nối
thì server sẽ tạo ra một socket mới để nói chuyện với client và cũng tạo ra
một socket khác để tiếp tục lắng nghe.
https://sdlc6d.sun.com/ECom/EComActionServlet/DownloadPage:~:com.s
un.sunit.sdlc.content.DownloadPageInfo;jsessionid=8768F4A376837F76F
224EEF98B2103B1;jsessionid=8768F4A376837F76F224EEF98B2103B1
Trong đó sẽ có các bản dành cho Windows và Linux.
Để cài đặt một môi trường phát triển(ở đây ta dùng Netbean)
Địa chỉ download:
https://sdlc2b.sun.com/ECom/EComActionServlet/DownloadPage:~:com.s
un.sunit.sdlc.content.DownloadPageInfo;jsessionid=FB9B3F6AB51ADD29
3
6B99E6C8A54F69B1;jsessionid=FB9B3F6AB51ADD296B99E6C8A54F69
B1
Trong đó sẽ có các bản dành cho Windows và Linux.
Các hàm thao tác trên Socket:
Đầu tiên chương trình phía server phải chạy và lắng nghe trên một
cổng nào đó để chờ phía client kết nối tới, nếu kết nối thành công thì cả
hai phía đều có hai thể hiện của lớp socket và dữ liệu sẽ được truyền qua
hai lớp socket này.
Thông thường trong một chương trình sẽ có các bước cơ bản sau:
1. Mở một socket.
2. Mở một input stream và output stream tới socket.
3. Đọc và viết tới stream thông qua giao thức của server.
4. Đóng Stream.
5. Đóng socket.
4
try {
clientSocket = serverSocket.accept();
} catch (IOException e) {
System.err.println("Accept failed.");
System.exit(1);
}
Phía client mở socket bằng cách sau:
Socket MySocket = null;
try {
MySocket = new Socket("Machine name", PortNumber);
} catch (UnknownHostException e) {
System.err.println("Don't know about host: ");
System.exit(1);
}
Trong đó Machine name là tên (hoặc địa chỉ IP) của máy tính
server, PortNumber là số hiệu cổng mà server đang lắng nghe.
Sau khi đã mở được socket để cho client và server có thể gửi và
nhận thông điệp với nhau thì chúng ta phải khởi tạo đối tượng Input
Stream và Output Stream.
Tạo đối tượng Input Stream
Phía client phải sử dụng lớp BufferedReader để tạo input với mục
đích để nhận thông điệp từ server.
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(
MySocket.getInputStream()));
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to: local
host.");
5
System.exit(1);
}
Phía server cũng sử dụng lớp BufferedReader để nhận thông điệp
tương tự như ở phía client.
Tạo đối tượng Output Stream.
Phía client sẽ sử dụng lớp PrintWriter để gửi thông điệp qua cho
server.
PrintWriter out = null;
try {
out = new PrintWriter(MySocket.getOutputStream(), true);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to: local
host.");
System.exit(1);
}
Bên phía server muốn gửi thông điệp cho client thì cũng làm tương
tự như bên client đã hướng dẫn ở trên.
Đóng kết nối.
Sau khi việc gửi và nhận thông điệp giữa client và server đã thực
hiện xong thì ta sẽ phải ngắt kết nối, chú ý: phải đóng input và output
stream trước khi đóng socket.
Phía client:
Try {
Out.close();
In.close();
MySocket.close();
} catch (IOException e) {
System.out.println(e);
}
6
Phía server:
Try {
Out.close();
In.close();
clientSocket.close();
serverSocket.close();
} catch (IOException e) {
System.out.println(e);
}
IV. Một số ví dụ:
1. ví dụ 1:
Tạo một project trong đó có 2 file là: Server.java và Client.java, đây
là một ví dụ đơn giản ban đầu để minh họa một ứng dụng server/client.
Server sẽ mở cổng 1234 để cho client kết nối tới, sau khi kết nối thành
công thì server sẽ gửi cho client thông điệp là: Hello World sau khi client
nhận được thông điệp này thì sẽ đóng kết nối lại.
7
PrintWriter out = new
PrintWriter(clientSocket.getOutputStream(),
true);
out.print(data);
out.close();
} catch (IOException e) {
System.err.println("Accept failed.");
System.exit(1);
}
clientSocket.close();
serverSocket.close();
}
}
Đầu tiên đối tượng ServerSocket được khởi tạo để lắng nghe ở
cổng 1234. Sau khi khởi tạo thì đối tượng socket sử dụng phương thức
accept() để đợi client kết nối tới. Khi việc kết nối thành công ta sẽ tạo đối
tượng output stream để gửi thông điệp cho client, thông điệp sẽ được gửi
sang cho client thông qua phương thức Print().
try {
MySocket = new Socket("localhost", 1234);
in = new BufferedReader(new
InputStreamReader(MySocket.getInputStream()));
String fromServer= in.readLine();
System.out.print(fromServer);
in.close();
} catch (UnknownHostException e) {
System.err.println("Don't know about host:");
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection
to:");
System.exit(1);
}
MySocket.close();
}
}
8
Một đối tượng socket được tạo ra để kết nối với server. Ở đây client
sẽ kết nối với server qua địa chỉ localhost ở cổng 1234- cổng mà server
đã mở và đang lắng nghe. Sau đó sẽ tiếp tục tạo đối tượng input stream
để nhận thông điệp từ server, khi đã nhận được thông điệp từ server thì
client sẽ in thông điệp đó ra và đóng kết nối lại.
2. ví dụ 2:
Bên trên chỉ là ví dụ đơn giản để minh họa cho ứng dụng
server/client. Sau đây sẽ là một ví dụ khác trong đó server và client sẽ nói
chuyện với nhau nhiều hơn, server sẽ tạo ra một protocol để nói chuyện,
client sẽ phải tuân thủ theo protocol đó.
Tạo một project có chứa 3 file là: ChamSocServer.java,
ChamSocClient.java, ChamSocProtocol.java. Trong đó
ChamSocProtocol.java sẽ quy định quy tắc nói chuyện giữa client và
server. Trong ví dụ này thì ta giải định đây là một hệ thống trả lời chăm sóc
khách hàng của một công ty nào đó. Khi kết nối thành công server sẽ gửi
lại cho client thông điệp là: “Xin chao mung cac ban den voi dich vu cham
soc khach hang!!” tức là kết nối đã thành công, khi đó client sẽ bắt đầu
đưa ra các yêu cầu để server trả lời. Ở đây quy định là client chỉ được hỏi
hai câu hỏi và sau đó phải trả lời một câu và phải theo đúng thứ tự sau:
câu 1: "day la dv gi?", sau khi được server trả lời thì client sẽ phải tiếp tục
hỏi tiếp câu thứ 2 là: "la gi?" khi đó server sẽ trả lời bạn và hỏi lại bạn một
câu: “Ban co muon chuyen den dv khac? (y/n)” nếu client trả lời “y” thì
server sẽ chuyển đến dịch vụ tiếp theo và client sẽ lại thực hiện lại quá
trình như trên, còn nếu client trả lời là “n” thì quá trình nói chuyện kết thúc
và đóng kết nối lại.
Sau đây là mã nguồn chương trình:
9
Mã nguồn file ChamSocServer.java:
import java.net.*;
import java.io.*;
outputLine = kkp.processInput(null);
out.println(outputLine);
10
public class ChamSocProtocol {
private static final int WAITING = 0;
private static final int SENT = 1;
private static final int SENTCLUE = 2;
private static final int ANOTHER = 3;
private String[] clues = { "Gioi thieu", "Cac san pham", "Cac tinh
nang", "Tu van", "Thong tin lien lac" };
private String[] answers = { "Se gioi thieu ve cong ty cua chung
toi!",
"Trung bay mot so san pham cua cong
ty!",
"Neu nhung tinh nang noi bat cua cac
san pham!",
"Chung toi se tu van cho cac ban nhung
san pham phu hop voi cac ban!",
"O day se la dia chi lien lac cua cong
ty chung toi!" };
if (state == WAITING) {
theOutput = "Xin chao mung cac ban den voi dich vu cham soc
khach hang!!";
state = SENT;
} else if (state == SENT) {
if (theInput.equalsIgnoreCase("day la dv gi?")) {
theOutput = clues[count];
state = SENTCLUE;
} else {
theOutput = "Ban phai hoi la: \"day la dv gi?\"! " +
"Try again.";
}
} else if (state == SENTCLUE) {
if (theInput.equalsIgnoreCase("la gi?")) {
theOutput = answers[count] + " Ban co muon chuyen den
dv khac? (y/n)";
state = ANOTHER;
} else {
theOutput = "Ban phai hoi la: \"la gi?\"! " +
"Try again.";
state = SENTCLUE;
}
} else if (state == ANOTHER) {
if (theInput.equalsIgnoreCase("y")) {
theOutput = "Xin chao ban den voi DV tiep theo";
if (count == (NUMJOKES - 1))
count = 0;
else
11
count++;
state = SENT;
} else {
theOutput = "Bye.";
state = WAITING;
}
}
return theOutput;
}
}
Đây là quy tắc nói chuyện giữa client và server như đã nói ở trên.
Mã nguồn file ChamSocClient.java:
import java.io.*;
import java.net.*;
try {
csSocket = new Socket("localhost", 1234);
out = new PrintWriter(csSocket.getOutputStream(), true);
in = new BufferedReader(new
InputStreamReader(csSocket.getInputStream()));
} catch (UnknownHostException e) {
System.err.println("Don't know about host: localhost.");
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to:
localhost.");
System.exit(1);
}
fromUser = stdIn.readLine();
if (fromUser != null) {
System.out.println("Client: " + fromUser);
out.println(fromUser);
12
}
}
out.close();
in.close();
stdIn.close();
csSocket.close();
}
}
Trên đây là một chương trình ứng dụng client/server khá điển hình,
trong ví dụ trên chỉ là một server phục vụ một client mà thôi, tuy nhiên
trong thực tế lại không phải là một server phục vụ một client nữa mà là
một server phục vụ nhiều client. Để có thể làm được điều đó thì trong
Java có cơ chế đa luồng, tức là mỗi khi có một yêu cầu kết nối từ một
client nào đó thì server sẽ tạo ra một luồng để kết nối với client đó. Như
vậy thì trong cùng một lúc sẽ có thể có rất nhiều client cùng kết nối đến
một server. Sau đây sẽ là mã nguồn của chương trình:
3. Ví dụ 3:
Mã nguồn file ChamSocServer.java:
import java.net.*;
import java.io.*;
try {
serverSocket = new ServerSocket(1234);
System.out.println("Server is listening");
} catch (IOException e) {
System.err.println("Could not listen on port: 1234.");
System.exit(-1);
}
while (listening)
new ServerThread(serverSocket.accept()).start();
serverSocket.close();
}
}
13
Mã nguồn file ServerThread.java:
import java.net.*;
import java.io.*;
try {
PrintWriter out = new PrintWriter(socket.getOutputStream(),
true);
BufferedReader in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
14
private int state = WAITING;
private int count = 0;
private String[] clues = { "Gioi thieu", "Cac san pham", "Cac tinh
nang", "Tu van", "Thong tin lien lac" };
private String[] answers = { "Se gioi thieu ve cong ty cua chung
toi!",
"Trung bay mot so san pham cua cong
ty!",
"Neu nhung tinh nang noi bat cua cac
san pham!",
"Chung toi se tu van cho cac ban nhung
san pham phu hop voi cac ban!",
"O day se la dia chi lien lac cua cong
ty chung toi!" };
if (state == WAITING) {
theOutput = "Xin chao mung cac ban den voi dich vu cham soc
khach hang!!";
state = SENT;
} else if (state == SENT) {
if (theInput.equalsIgnoreCase("day la dv gi?")) {
theOutput = clues[count];
state = SENTCLUE;
} else {
theOutput = "Ban phai hoi la: \"day la dv gi?\"! " +
"Try again.";
}
} else if (state == SENTCLUE) {
if (theInput.equalsIgnoreCase("la gi?")) {
theOutput = answers[count] + " Ban co muon chuyen den
dv khac? (y/n)";
state = ANOTHER;
} else {
theOutput = "Ban phai hoi la: \"la gi?\"! " +
"Try again.";
state = SENTCLUE;
}
} else if (state == ANOTHER) {
if (theInput.equalsIgnoreCase("y")) {
theOutput = "Xin chao ban den voi DV tiep theo";
if (count == (NUMJOKES - 1))
count = 0;
else
count++;
state = SENT;
} else {
theOutput = "Bye.";
state = WAITING;
}
}
return theOutput;
}
15
}
import java.io.*;
import java.net.*;
try {
csSocket = new Socket("localhost", 1234);
out = new PrintWriter(csSocket.getOutputStream(), true);
in = new BufferedReader(new
InputStreamReader(csSocket.getInputStream()));
} catch (UnknownHostException e) {
System.err.println("Don't know about host: localhost.");
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to:
localhost.");
System.exit(1);
}
fromUser = stdIn.readLine();
if (fromUser != null) {
System.out.println("Client: " + fromUser);
out.println(fromUser);
}
}
out.close();
in.close();
stdIn.close();
csSocket.close();
}
}
Hy vọng đọc đến đây mọi người có thể hiểu một phần nào đó về
Socket, và có thể tự viết được một số ứng dụng client-server.
16
Good Luck!
17