You are on page 1of 17

LẬP TRÌNH SOCKET VỚI JAVA

Người viết: Hoàng Tuấn Hưng – K50CD ĐH Công Nghệ - ĐHQGHN

Email: hunght@fpt.vn

Nội dung:

I. Khái quát về Socket.


II. Socket là gì?
III. Lập trình Socket bằng Java.

• Cài đặt Java.


• Các hàm thao tác trên Java:
 Mở một socket bên phía server.
 Mở một socket bên phía client.
 Tạo đối tượng Input Stream.
 Tạo đối tượng Output Stream.
 Đóng kết nối.
IV. Một số ví dụ.

• Ví dụ 1: ứng dụng server-client đơn giản, server sẽ gửi


thông điệp cho client là: Hello World.
• Ví dụ 2: server và client sẽ nói chuyện với nhau theo một
protocol.
• Ví dụ 3: một server nói chuyện được với nhiều client.

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.

Sau đây là hướng dẫn lập trình socket trong Java.


III. LẬP TRÌNH SOCKET BẰNG JAVA.
Cài đặt Java.
Chúng ta sẽ cài đặt một bộ JDK và một môi trường phát triển.
Để cài đặt JDK chúng ta làm như sau:
Download JDK theo địa chỉ:

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.

Phía server mở một socket bằng cách sau:


ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(PortNumber);
} catch (IOException e) {
System.err.println("Could not listen on port: "+ PortNumber);
System.exit(1);
}
Phía server cần phải khởi tạo đối tượng của lớp ServerSocket để
lắng nghe và chấp nhận kết nối từ client. Trong đó PortNumber là số hiệu
cổng server mở ra để “lắng nghe” (chú ý: các cổng từ 0-1023 được sử
dụng cho các ứng dụng đặc biệt như: HTTP, FTP, SMTP nên chúng ta chỉ
chọn các cổng từ 1024 trở đi).
Socket clientSocket = null;

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.

Mã nguồn của Server.java:


import java.net.*;
import java.io.*;

public class Server {


public static void main(String[] args) throws IOException {

ServerSocket serverSocket = null;


String data= "Hello World";
try {
serverSocket = new ServerSocket(1234);
System.out.println("Server listening");
} catch (IOException e) {
System.err.println("Could not listen on port: 1234.");
System.exit(1);
}

Socket clientSocket = null;


try {
clientSocket = serverSocket.accept();

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().

Đây là mã nguồn của Client.java:


import java.io.*;
import java.net.*;

public class Client {


public static void main(String[] args) throws IOException {

Socket MySocket = null;


BufferedReader in = null;

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.*;

public class ChamSocServer {


public static void main(String[] args) throws IOException {

ServerSocket serverSocket = null;


try {
serverSocket = new ServerSocket(1234);
System.out.println("Server listening");
} catch (IOException e) {
System.err.println("Could not listen on port: 1234.");
System.exit(1);
}

Socket clientSocket = null;


try {
clientSocket = serverSocket.accept();
} catch (IOException e) {
System.err.println("Accept failed.");
System.exit(1);
}

PrintWriter out = new


PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(
clientSocket.getInputStream()));
String inputLine, outputLine;
ChamSocProtocol kkp = new ChamSocProtocol();

outputLine = kkp.processInput(null);
out.println(outputLine);

while ((inputLine = in.readLine()) != null) {


outputLine = kkp.processInput(inputLine);
out.println(outputLine);
if (outputLine.equals("Bye."))
break;
}
out.close();
in.close();
clientSocket.close();
serverSocket.close();
}
}

Mã nguồn file ChamSocProtocol.java:


import java.net.*;
import java.io.*;

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 static final int NUMJOKES = 5;

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!" };

public String processInput(String theInput) {


String theOutput = null;

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.*;

public class ChamSocClient {


public static void main(String[] args) throws IOException {

Socket csSocket = null;


PrintWriter out = null;
BufferedReader in = null;

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);
}

BufferedReader stdIn = new BufferedReader(new


InputStreamReader(System.in));
String fromServer;
String fromUser;

while ((fromServer = in.readLine()) != null) {


System.out.println("Server: " + fromServer);
if (fromServer.equals("Bye."))
break;

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.*;

public class ChamSocServer {


public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
boolean listening = true;

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.*;

public class ServerThread extends Thread {


private Socket socket = null;

public ServerThread(Socket socket) {


super("ServerThread");
this.socket = socket;
}

public void run() {

try {
PrintWriter out = new PrintWriter(socket.getOutputStream(),
true);
BufferedReader in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));

String inputLine, outputLine;


Protocol cs = new Protocol();
outputLine = cs.processInput(null);
out.println(outputLine);

while ((inputLine = in.readLine()) != null) {


outputLine = cs.processInput(inputLine);
out.println(outputLine);
if (outputLine.equals("Bye"))
break;
}
out.close();
in.close();
socket.close();

} catch (IOException e) {
e.printStackTrace();
}
}
}

Mã nguồn file Protocol.java:


import java.net.*;
import java.io.*;

public class Protocol {


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 static final int NUMJOKES = 5;

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!" };

public String processInput(String theInput) {


String theOutput = null;

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
}

Mã nguồn file ChamSocClient.java:

import java.io.*;
import java.net.*;

public class ChamSocClient {


public static void main(String[] args) throws IOException {

Socket csSocket = null;


PrintWriter out = null;
BufferedReader in = null;

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);
}

BufferedReader stdIn = new BufferedReader(new


InputStreamReader(System.in));
String fromServer;
String fromUser;

while ((fromServer = in.readLine()) != null) {


System.out.println("Server: " + fromServer);
if (fromServer.equals("Bye."))
break;

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

You might also like