Kết nối mạng với Generic Connection Framework (GCF)

Một phần của tài liệu LẬP TRÌNH ỨNG DỤNG MOBILE BẰNG JAVA (Trang 99)

5.2.1. Giới thiệu GCF.

Trong bộ J2ME và J2EE chúng ta thực hiện các công việc liên quan đến truy xuất tài nguyên mạng qua hai packages chính là java.io và java.net. Với kích thước hơn 200 kbytes và bao gồm hơn 100 lớp và interfaces, cách thức truy xuất thông qua hai gói io và net này vượt quá khả năng của thiết bị J2ME. Ngoài ra, trong bộ J2EE và J2SE này, nhà phát triển còn chú trọng nhiều đến các phương thức mạng và hệ thống file system trong khi thiết bị J2ME lại không quan tâm nhiều đến các vấn đề này. Vì lý do trên, bộ thư viện Generic Connection Framework (GCF) đã được phát triển và nhắm đến các thiết bị di động J2ME.

Các nhà phát triển không đưa ra mục tiêu phát triển một bộ thư viện cung cấp các lớp, phương thức hoàn toàn mới mà họ muốn đưa ra một bộ thư viện con của các thư viện đã được phát triển khá tốt trên môi trường J2SE và J2EE. Bộ thư viện con này sẽ có một số thay đổi nhỏ để thích ứng với các hạn chế trên thiết bị di động cài đặt MIDP.

Hình 11:Lược đồ lớp trong thư việc GCF.

Mục tiêu chung được đề ra là chúng ta cần có một lớp chính: lớp Connector. Lớp này sẽ có khả năng tạo các loại kết nối khác nhau: http, datagram, file…  Phương thức mở kết nối sẽ có dạng: Connector.Open(“protocol:address;parameters”); Ví dụ: Connector.Open(“http://www.some_web_address.com”); Connector.Open(“socket://someaddress:1234”); Connector.Open(“file://testdata.txt”); Datagram Connection StreamConnection Notifier Connector.open Connetion InputConnection OutputConnection StreamConnection ContentConnection HttpConnection

Lúc này, GCF cho thấy khả năng linh hoạt của mình. Tùy theo các protocol khác nhau mà một kết nối loại tương ứng sẽ được mở và trả lại cho người dùng. Lớp Connector sẽ tìm kiếm các lớp cài đặt cho loại protocol được yêu cầu, việc này được thực hiện thông qua phương thức Class.forName().

Ví dụ: Khi có một yêu cầu mở kết nối HTTP, lớp Connector sẽ thực thi hàm: Class.forName(“com.sun.midp.io.j2me.http.Protocol”);

Sau khi kiểm tra, nếu lớp này tồn tại thì người dùng sẽ được trả lại một đối tượng cài đặt (implements) interface Connection và các thao tác truy cập sẽ được thực hiện thông qua đối tượng này. Lớp Connector và interface Connection được định nghĩa trong CLDC.

Thực chất CLDC chỉ cung cấp các định nghĩa cho các interface. Quá trình cài đặt (implements) các protocols được thực hiện trong Profiles. Ví dụ, trong MIDP 1.0 lớp HTTPConnection sẽ được cài đặt (chỉ một phần) bộ giao thức HTTP 1.1 Lớp HTTPConnection cài đặt interface ConnectConnection và có hơn 20 phương thức để hỗ trợ người dùng tương tác với giao thức HTTP.

Mặc dù, DatagramConnection được biểu diễn trên lược đồ nhưng MIDP 1.0 chỉ yêu cầu bắt buộc hỗ trợ giao thức HTTP. Các nhà phát triển có thể hỗ trợ thêm các giao thức khác nhau nhưng điều này không bắt buộc.

Trong MIDP 2.0, chúng ta được hỗ trợ các phương thức kết nối thông qua TCP và UDP. Tuy nhiên, ở phần này chúng ta chú trọng đến việc kết nối thông qua HTTP, đây là yêu cầu bắt buộc vói các thiết bị MIDP.

Sau đây là danh sách các lớp, interface và hàm chính trong bộ GCF. Connection(public abstract interface Connection)

public void close().

InputConnection(public abstract interface InputConnection extends Connection) public InputStream openInputStream().

public DataInputStream openDataInputStream().

OutputConnection(public abstract interface OutputConnection extends Connection)

public OutputStream openoutputStream().

StreamConnection (public abstract interface StreamConnection extends InputConnection, OutputConnection)

ContentConnection (public abstract interface ContentConnection extends StreamConnection)

public long getLength(). public String getEncoding(). public String getType().

HttpConnection (public interface HttpConnection extends ContentConnection) // Hơn 20 phương thưc hỗ trợ truy cập HTTP.

Connector(public class Connector)

public static Connection open(String name)

public static Connection open(String name, int mode)

public static Connection open(String name, int mode, boolean timeouts) public static DataInputStream openDataInputStream(String name) public static DataOutputStream openDataOutputStream(String name) public static InputStream openInputStream(String name)

public static OutputStream openOutputStream(String name)

Kết nối HTTP:

Trong MIDP 1.0, phương thức duy nhất bảo đảm được cài đặt là HTTP. Thông qua lớp HttpConnection chúng ta có thể liên lạc với web server hoặc một thiết bị có hỗ trợ HTTP.

HTTP được xem như là một giao thức dạng yêu cầu/phản hồi. Máy client sẽ gởi một yêu cầu đến server (địa chỉ server được định nghĩa theo đặc tả Uniform Resource Locator (URL)) và một địa chỉ phản hồi sẽ được phát sinh từ phía server gửi trả cho client. Đây cũng chính là cách thức liên lạc giữa các trình duyệt web và web server.

Khởi tạo kết nối:

Bảng 40:Dang sách 7 phương thức để khởi tạo kết nối với server.

Phương thức Mô tả

static Connection open(String name)

Đây là một phương thức khởi tạo, dùng để mở và trả về một kết nối mới tới một URL có tên làm name.

static Connection open(String name, int mode)

Đây là một phương thức khởi tạo, dùng để mở và trả về một kết nối mới tới một URL có tên làm name và có chỉ rõ phương thức truy cập.

static Connection open(String name, int mode, boolean timeouts)

Đây là một phương thức khởi tạo, dùng để mở và trả về một kết nối mới tới một URL có tên làm name, có chỉ rõ phương thức truy cập và có xử lý ngoại lệ khi thời gian kết nối vượt giới hạn.

static Connection

openDataInputStream(String name)

Mở một connection, kế đến xây dựng và trả về một dữ liệu dạng input stream.

static Connection

openDataOutputStream(String name)

Mở một connection, kế đến xây dựng và trả về một dữ liệu dạng output stream.

static Connection openInputStream(String name) Mở một connection, kế đến xây dựng và trả về một input stream. static Connection openOutputStream(String name) Mở một connection, kế đến xây dựng và trả về một output stream.

Bảng 41:Có 3 modes được hỗ trợ khi ta tạo mới một connection.

Mode Ý nghĩa

READ_WRITE Connection có hỗ trợ cả việc đọc và ghi.

READ Connection chỉ cho phép đọc dữ liệu.

WRITE Connection chỉ cho phép ghi dữ liệu.

Trong 7 phương thức trên có 3 phương thức open() khác nhau: Phương thức đầu tiên chỉ đòi hỏi địa chỉ của server, phương thức thứ hai cho phép người dùng chọn kiểu connection có hỗ trợ reading/writing. Phương thức cuối cùng cho phép người dùng tùy chọn có xử lý timeouts exception hay không. Các phương thức còn lại dùng để mở các dòng dữ liệu đọc/ghi.

Sau đây là đoạn lệnh dùng để thiết lập một kết nối kiểu ContentConnection(đây là interface cài đặt interface Connection và chứa các định nghĩa cho lớp HttpConnection, xin xem lại lược đồ bên trên).

String url = http://www.mydomain.com

ContentConnection connection = (ContentConnection) Connector.open(url);

Chúng ta không thể thiết lập một đối tượng kiểu Connector mà dùng hàm static open() để tạo một connection. Kết quả trả về là một đối tượng Connecton, ta có thể “ép kiểu” về dạng Connection chúng ta mong muốn (ở đây là ContentConnection).

Sau đó có thể tạo một InputStream từ connection này để phục vụ việc đọc dữ liệu. InputStream iStrm = connection.openInputStream();

//ContentConnection hỗ trợ phương thức getLength int length = (int) connection.getLength();

if( length > 0) {

Byte imageData[] = new byte[length]; //Đọc dữ liệu vào mảng.

iStrm.read(imageData); }

Sau khi tạo thành công connection, ta có tạo các dòng dữ liệu đọc/ghi dựa trên connection này. Interface ContentConnection còn hỗ trợ cả phương thức getLength() cho biết chiều dài dữ liệu hiện hành. Cấu trúc và phong cách thiết kế hướng đối tượng của Java hỗ trợ cho các nhà phát triển ứng dụng rất nhiều.

Thậm chí chúng ta còn có thể lấy ra dòng dữ liệu Input mà không cần tạo đối tượng connection như sau:

// Tạo một InputStream connection

String url = http://www.mydomain.com/picture.png

InputStream iStrm = (InputStream)Connector.openInputStream(url); try

{

byte imageData[] = new byte[2500]; // Đọc dữ liệu vào mảng

int length = iStrm.read(imageData); }

Tuy nhiên, ở cách tiếp cận vừa nêu chúng ta không tận dụng được phương thức getLength() của interface ContentConnection. Việc chọn lựa cách tiếp cận nào là tùy thuộc vào hoàn cảnh và sở thích của người lập trình.

Đến thời điểm này chúng ta chỉ mới khởi tạo kết nối và đọc, ghi dữ liệu mà chưa tận

dụng các tính năng liên quan đến giao thức HTTP. HTTP là một giao thức dạng yêu

cầu / phản hồi: client gửi một yêu cầu (request), server sẽ gửi lại một thông điệp phản hồi. Tiếp theo chúng ta sẽ nghiên cứu những chức năng hỗ trợ giao thức HTTP.

Các đặc điểm của kết nối HTTP bằng J2ME.

Các phương thức yêu cầu (Request):

Một yêu cầu từ phía client gồm 3 phần: phương thức re quest, header và phần thân (body). HttpConnection hỗ trợ 3 phương thức request: GET, POST, HEAD. Cả 3 phương thức này nằm trong lớp javax.microedition.io.HttpConnection.

Bảng 42:Mô tả các phương thức yêu cầu chính của lớp HttpConnection.

Phương thức Mô tả

GET Thông tin yêu cầu được gởi kèm theo URL

POST Thông tin yêu cầu được gửi theo một stream riêng biệt.

HEAD Gói tin yêu cầu truy vấn thông tin về một tài nguyên.

Cả 3 phương thức trên thông báo cho server biết client cần truy vấn một thông tin. Phương thức GET và POST khác nhau ở cách thông tin truy vấn được gửi lên server. Chúng ta có thể chỉ định rõ phương thức sử dụng trong HttpConnection bằng hàm setRequestMethod():

HttpConnection http = null;

http = (HttpConnection)Connector.open(url); http.setRequestMethod(HttpConnection.GET);

Bảng 43:Các phương thức trong lớp HttpConnection.

Phương thức Mô tả

void setRequestMethod(String method)

Sét phương thức request là Get, Post, Head.

Void setRequestProperty(String key, String value)

Sét thuộc tính cho request.

String getRequestMethod() Lấy phương thức request đang sử dụng.

String getRequestProperty(String key) Lấy giá trị hiện thời của thuộc tính

request.

Sử dụng GET, phần thân của yêu cầu được đưa vào chung trong URL. Điều này được gọi là URL encoding. Ví dụ, chúng ta có một web form gồm 2 trường color và một font. Tên của hai trường này, giả sử lần lượt là userColor = blue và userFont = courier và submit lên trang web http://www.mydomain.com/formscipt thì URL thực chất có dạng sau:

http://www.mydomain.com/formscript?userColor=blue&userFont=courier

(dấu ? dùng để phân cách vùng địa chỉ trang và phần thông tin yêu cầu gửi lên server). Sau đó, trang web trên server sẽ dùng các hàm riêng để tách vùng thông tin yêu cầu và thực hiện xử lý, sau đó sẽ trả kết quả cho client.

Với phương thức POST, thông tin truy vấn không được phép thêm vào vùng URL mà được chuyển lên server trong vùng BODY của gói tin HTTP.

Phương thức POST không giới hạn kích thước vùng dữ liệu request gửi lên server, ngược lại phương thức GET do gửi dữ liệu dạng gắn kèm vào URL nên không thể gửi kèm dữ liệu quá lớn.

POST gửi theo một dòng riêng (trong phần BODY) nên không thể hiện thông tin cho người dùng thấy trong URL.

HEAD là một phương thức gần giống với GET. Client gửi dữ liệu lên server và đính kèm nó với URL. Tuy nhiên, trong gói tin hồi đáp của server cho gói tin dạng HEAD này sẽ không có vùng BODY. HEAD được dùng chủ yếu để lấy thông tin về một tài nguyên nào đó trên server. Ví dụ, chúng ta lấy thông tin về lần cập nhật mới nhất của một tập tin trên server, do đó, trong gói tin trả lời chúng ta thực chất không cần nội dung của file trên (gói tin không có vùng BODY).

Ghi chú: Luận văn không có mục tiêu trình bày về giao thức HTTP mà chỉ giới thiệu như một phương thức kết nối được J2ME hỗ trợ, do đó phần lý thuyết giới thiệu về

HTTP sẽ rất hạn chế. Nếu người đọc muốn tìm hiểu sâu hơn về HTTP có thể tìm đọc RFC 2616 về HTTP 1.1

Thông tin Header:

Phần thứ hai của một gói tin yêu cầu từ client là phần header. Giao thức HTTP định nghĩa hơn 40 trường header. Một số trường thông dụng như Accept, Cache-Control, Content-Type, Expires, If-Modified-Since và User-Agent Header được đặt giá trị bằng hàm setRequesProperty() đã được đề cập bên trên.

HttpConnection http = null;

http = (HttpConnection)Connector.open(url); http.setRequestMethod(HttpConnection.GET);

http.setRequestProperty(“If-Modified-Since”, “Mon, 16 Jul 2001 22:54:26 GMT”);

Phần thân (body):

Dữ liệu được chuyển từ client lên server thông qua phần body của gói tin request. Như đã đề cập, GET gộp phần body vào URL còn POST gửi phần body trong một stream riêng biệt.

Gói tin trả lời của Server:

Khi client đã đóng gói một gói tin gồm phương thức yêu cầu, phần header, phần body và gửi đến server, server có trách nhiệm phân tích gói tin, xử lý và hồi đáp client. Phần hồi đáp của server cũng gồm 3 phần: status line, header và phần body.

Status line:

Phần status line cho biết kết quả của gói tin yêu cầu từ phía client. Trong HttpConnection, chúng ta có hơn 35 mã status hồi đáp. HTTP chia các mã hồi đáp này thành 5 mục lớn:

1xx – thông tin.

2xx – mã báo thành công. 3xx – chuyển hướng. 4xx – lỗi từ phía client. 5xx – lỗi tại server.

Ví dụ: HTTP_OK (mã 200) thông báo yêu cầu từ phía client đã được xử lý thành công. Khi gửi gói tin hồi đáp (response), server thường gửi cả version của giao thức kèm theo status line.

Đây là một số ví dụ về status line: HTTP/1.1 200 OK

HTTP/1.1 400 Bad Request.

HTTP/1.1 500 Internal Server Error.

Khi đọc mã status, ta có hai lựa chọn: lấy về mã hay câu thông báo: http.getResponseCode();

http.getResponseMessage();

Phần Header:

Cũng tương tự như client, gói tin trả về của server cũng bao gồm phần header. Phần header bao gồm nhiều cặp Key – giá trị có thể được truy xuất thông qua nhiều cách:

Bảng 44:Các phương thức truy xuất thông tin Header.

Phương thức Mô tả

String getHeaderField(int n) Lấy giá trị trường header theo n.

String getHeaderField(String name) Lấy giá trị của một trường header

theo tên. long getHeaderfielDate(String name, long

def)

Lấy trường có tên name và trả về giá trị kiểu long.

int getHeaderfielInt(String name, int def) Lấy trường có tên name và trả về giá trị kiểu int.

String getHeaderFieldKey(int n) Gọi lại khóa của header theo chỉ số n

của trường.

long getDate() Gọi lại trường ngày của header.

long getExpiration() Gọi lại trường giới hạn của header.

long getLastModified() Trả về lần cuối cùng mà trường

header được sửa đổi.

Xét một ví dụ, giả sử trong vùng header ta có một cặp khóa/giá trị như sau: content- type = text/plain (“content-type” là một trường được HTTP định nghĩa, “text-plain” để chỉ giá trị trả về có dạng text đơn thuần). Chúng ta giả sử thêm trường content-type này là trường content-type này là trường đầu tiên (index = 0) trong vùng header. Để lấy giá trị của trường này ta dùng có những cách sau:

http.getHeaderField(0);//”text-plain”

http.getHeaderField(“content-type”);//”text-plain” http.getHeaderFieldKey(0);//”content-type”

Phần thân (Body):

Phần thân là dữ liệu gửi từ server trả về cho client. Chúng ta không có một phương thức đặc biệt nào để đọc phần body mà cách thông dụng nhất là đọc phần body này thông qua một stream. Chúng ta xét một ví dụ download một file text từ server:

Trong ví dụ này, chúng ta sẽ tạo ra một MIDlet yêu cầu nội dung một file từ phía server. URL của file trên có dạng.

url = http://localhost/getHeaderInfo.txt; Đầu tiên chúng ta cũng sẽ khởi tạo kết nối: HttpConnection http = null;

……… // Tạo kết nối

http = (HttpConnection)Connector.open(url); Ứng dụng Client sẽ trải qua một số bước: // Gửi yêu cầu lên server:

//1) Xác định phương thức yêu cầu là GET http.setRequestMethod(HttpConnection.GET); //2) Gửi các thông tin header.

http.setRequestProperty(“User-Agent”, “Profile/MIDP – 1.0 Configuration/CLDC – 1.0”);

//3) Gửi dữ liệu, vì dùng Get nên không có phần body – data.

Sau đó chúng ta phải xử lý phần thông tin trả về của server để lấy ra nội dung file. // Thông tin trả về của server.

//1) Lấy status line:

System.out.println(“Msg: ” + http.getResponseMessage()); System.out.println(“Code: ” + http.getResponseCode()); // 2) Lấy thông tin header:

if(http.getResponseCode() == HttpConnection.HTTP_OK) {

System.out.println(“field 0: ” + http.getHeaderField(0)); System.out.println(“key 0: ” + http.getHeaderFieldKey(0)); System.out.println(“content: ” + http.getHeaderFieldKey(0)); } //3) Lấy dữ liệu. String str; iStrm = http.openInputStream(); int length = (int) http.getLength(); if(length != -1)

{

// Đọc dữ liệu vào mảng nếu có gía trị length byte serverData[] = new byte[length];

iStrm.read(serverData); str = new String(serverData); }

// Nếu không có length thì đọc từng byte. else {

ByteArrayOutputStream bStrm = new ByteArrayOutputStream(); // Mỗi lần chỉ đọc một ký tự. int ch; while((ch == iStrm.read()) != -1) bStrm.write(ch); str = new String(bStrm.toByteArray()); bStrm.close(); } System.out.println(“File Contents: ” + str);

Sau khi đọc được nội dung file ta xuất ra màn hình console, nếu là ví dụ trên điện thoại thật ta có thể dùng các thành phần đồ họa cấp cao để xuất nội dung.

Ghi chú: Ở đây có một vấn đề gây khó hiểu cho người đọc, xin hãy theo dõi đoạn code sau:

http.setRequestMethod(HttpConnection.GET);

http.setRequestProperty(“User-Agent”, “Profile/MIDP-1.0 Configuration/CLDC – 1.0”);

System.out.println(“Msg: ” + http.getResponseMessage()); System.out.println(“Code: ” + http.getResponseCode());

Ban đầu chúng ta dùng hàm Connector.open(url) để tạo ra một đối tượng kiểu HttpConnection vì không có phương thức khởi tạo trực tiếp đối tượng HttpConnection. Theo như ý nghĩa của hàm open(url) thì lúc này một kết nối lên server đã được khởi tạo, nhưng sau đó ta mới đặt các thông số về phương thức yêu cầu là GET và đặt các

Một phần của tài liệu LẬP TRÌNH ỨNG DỤNG MOBILE BẰNG JAVA (Trang 99)

Tải bản đầy đủ (PDF)

(156 trang)