lẬp TrÌnh Socket vỚi Java

22
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: [email protected] 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. 1

Transcript of lẬp TrÌnh Socket vỚi Java

Page 1: lẬp TrÌnh Socket vỚi Java

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: [email protected]

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

Page 2: lẬp TrÌnh Socket vỚi Java

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

nối client sẽ thử hẹn gặp server ở trên máy của server thông qua port

2

Page 3: lẬp TrÌnh Socket vỚi Java

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

Page 4: lẬp TrÌnh Socket vỚi Java

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

Page 5: lẬp TrÌnh Socket vỚi Java

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

Page 6: lẬp TrÌnh Socket vỚi Java

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

Page 7: lẬp TrÌnh Socket vỚi Java

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

Page 8: lẬp TrÌnh Socket vỚi Java

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

Page 9: lẬp TrÌnh Socket vỚi Java

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

Page 10: lẬp TrÌnh Socket vỚi Java

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

Page 11: lẬp TrÌnh Socket vỚi Java

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 count++; state = SENT;

11

Page 12: lẬp TrÌnh Socket vỚi Java

} 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

Page 13: lẬp TrÌnh Socket vỚi Java

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

Mã nguồn file ServerThread.java:

13

Page 14: lẬp TrÌnh Socket vỚi 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;

private int state = WAITING; private int count = 0;

14

Page 15: lẬp TrÌnh Socket vỚi Java

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

Page 16: lẬp TrÌnh Socket vỚi Java

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.

Good Luck!

16