Với nhu cầu trao đổi thông tin, liên lạc và trò chuyện trên mạng ngày càng tăng hiện nay, việc nghiên cứu và xây dựng thành công một chương trình Chat sẽ đáp ứng được những nhu cầu cần t
Trang 1ĐẠI HỌC ĐÀ NẴNG TRƯỜNG ĐẠI HỌC SƯ PHẠM
KHOA TIN HỌC - -
Đề tài:
VIẾT CHƯƠNG TRÌNH TRÒ CHUYỆN ĐƠN GIẢN
BẰNG NGÔN NGỮ LẬP TRÌNH C#
Muc Lục
LỜI MỞ ĐẦU 4
Trang 2LỜI CẢM ƠN 6
NHẬN XÉT CỦA GIÁO VIÊN HƯỚNG DẪN 7
Chương 1: CƠ SỞ LÝ THUYẾT VÀ ỨNG DỤNG 8
1.1 Sơ lược về lập trình Socket 8
1.1.1 Khái niệm IPAddress và Port 8
1.1.2 Lớp IPAddress 8
1.1.3 Lớp IPEndPoint 9
1.1.4 Lập trình Socket hướng kết nối(TCP) 9
1.1.5 Lập trình Socket phi kết nối(UDP) 10
1.1.6 Lớp Helper của C# Socker 11
1.2 Xử lý tiến trình trong lập trình đa luồng 15
1.2.1 Khái niệm luồng 15
1.2.2 Khảo sát namespace System.Threading 16
1.3 Đồng bộ và bất đồng bộ trong lập trình đa luồng 19
1.4 Hệ mã hóa công khai RSA 22
1.4.1 Khái niệm mã hóa 22
1.4.2 Hệ mã khóa 24
1.4.3 Thuật toán mã hóa RSA 25
Chương 2: PHÂN TÍCH VÀ THIẾT KẾ CHƯƠNG TRÌNH 30
2.1 Phân tích 30
2.1.1 Phân tích nhu cầu thực tiễn 30
2.1.2 Yêu cầu đề ra 30
2.1.3 Phân tích các thành phần xử lý 30
2.1.4 Quá trình xử lý của chương trình 39
2.2 Thiết kế 45
2.2.1 Thiết kế Server 45
2.2.2 Thiết kế Client 48
2.3 Cài đặt và sử dụng chương trình 52
KẾT LUẬN 54
DANH MỤC TÀI LIỆU THAM KHẢO 55
Trang 3DANH SÁCH HÌNH VẼ SƠ ĐỒ
Hình 1.1 Lập trình socket hướng kết nối (TCP) 6
Hình 1.2 Lập trình socket phi kết nối (UDP) 8
Hình 1.3 Kết quả chương trình đồng bộ hóa 18
Hình 1.4 Kết quả chương trình bất đồng bộ 18
Hình 1.5 Mã hóa 20
Hình 1.6 Mã hóa và giải mã thông tin được truyền đi 20
Hình 1.7 Mã hóa bí mật 21
Hình 1.8 Mã hóa công khai 21
Hình 2.1 Sơ đồ xử lý đăng nhập 28
Hình 2.2 Sơ đồ xử lý đăng xuất 29
Hình 2.3 Sơ đồ xử lý gởi tin nhắn 30
Hình 2.4 Giao diện Server thu gọn 42
Hình 2.5.a Giao diện Server mở rộng chưa kết nối 43
Hình 2.5.b Giao diện Server mở rộng đã có kết nối 43
Hình 2.6 Giao diện Client đăng nhập 44
Hình 2.7 Giao diện Client chính thu gọn 45
Hình 2.8 Giao diện Client Chat room 46
Hình 2.9 Giao din Client tin nhắn riêng tư 47
Hình 2.10 Giao diện Client Chat riêng tư 48
Trang 4LỜI MỞ ĐẦU
Với sự phát triển của Internet hiện nay, việc viết các phần mềm ứng dụng mạng đã trở nên cần thiết và phổ biến với những nhà phát triển phần mềm Với nhu cầu trao đổi thông tin, liên lạc và trò chuyện trên mạng ngày càng tăng hiện nay, việc nghiên cứu và xây dựng thành công một chương trình Chat sẽ đáp ứng được những nhu cầu cần thiết đó và làm phong phú thêm các phương thức trao đổi
và trò chuyện của các chương trình Chat hiện nay
Với những lý do đặt ra như thế, việc nghiên cứu và xây dựng nên một chương trình Chat phải đảm bảo các tính năng cần thiết và cơ bản nhất như: có thể gởi tin nhắn riêng tư hoặc trong một nhóm giữa các user, tạo ra danh sách bạn bè Việc nghiên cứu và xây dựng một chương trình Chat cần thực hiện một cách kĩ lưỡng và khoa học nhất để có thể tạo ra một ứng dụng có với những tính năng cần thiết, dể dàng sử dụng, tao tác linh hoạt
Để xây dựng thành công một chương trình Chat, em đã tập trung vào những đối tượng và phạm vi nghiên cứu sau:
Đối tượng nghiên cứu: tìm hiểu cơ chế hoạt động của Socket, các nguyên lý
về lập trình đa luồng, đồng bộ và bất đồng bộ trong lập trình đa luồng, cuối cùng là ứng dụng hệ mã hóa công khai RSA để mã hóa thông tin truyền đi trên mạng
Phạm vi nghiên cứu: tạo ra một chương trình Chat với tính năng như gởi và nhận tin nhắn dạng text giữa các users với dạng riêng tư hoặc trong một nhóm, mã hóa và giải mã thông tin trước khi truyền đi và sau khi nhận được nhằm đảm bảo tính riêng tư và bảo mật khi truyền dữ liệu Tất cả các quá trình đó được giám sát và điều khiển bởi một Server
Trong báo cáo này em xin trình bày theo kết cấu:
Chương I: CƠ SỞ LÝ THUYẾT VÀ ỨNG DỤNG
Tìm hiểu về lập lập trình Socket bằng ngôn ngữ C#, đưa ra cái nhìn tổng quan
cơ sở lý thuyết chủ đạo trong quá trình xây dựng chương trình Tìm hiểu và ứng dụng hệ mã hóa công khai RSA trong quá trình mã hóa thông tin trước khi truyền
đi
Chương II: PHÂN TÍCH VÀ THIẾT KẾ CHƯƠNG TRÌNH
Trang 5Đưa ra các bước phân tích từ nghiên cứu thực tế của người sử dụng và vận dụng cơ sở lý thuyết trong việc quá trình xây dựng chương trình Đề ra các giải pháp trong từng bước phân tích nhằm xây dựng chương trình một cách khoa học nhất
Chương III: CÀI ĐẶT VÀ SỬ DỤNG CHƯƠNG TRÌNH
Là cách hướng dẫn người sử dụng phương pháp cài đặt chương trình, yêu cầu hệ thống và các phương pháp sử dụng chương trình
Trang 6LỜI CẢM ƠN
Bằng sự nổ lực và kiên trì học hỏi trong quá trình thực hiện khóa luận ,
em đã hoàn thành đề tài “ Viết chương trình trò chuyện đơn giản bằng
ngôn ngữ lập trình C#” với sự hướng dẫn của Thầy Ngô Đình Thưởng
Em xin chân thành cảm ơn Thầy Ngô Đình Thưởng đã hướng dẫn em thực hiện khóa luận tốt nghiệp Thầy đã nhiệt tình hướng dẫn và giải đáp mọi thắc mắc trong quá trình thực hiện khóa luận
Em cũng xin chân thành gởi lời cảm ơn đến Thầy Cô trong khoa Tin học, các Anh chị và các bạn đã giúp đỡ trong suốt quá trình thực hiện khóa luận tốt nghiệp Cuối cùng xin chúc quý Thầy cô, các Anh chị cùng các bạn sức khỏe và công tác tốt!
Đà Nẵng, ngày 15 tháng 05 năm 2013
Sinh viên thực hiện
Nguyễn Văn Xuân
Trang 7NHẬN XÉT CỦA GIÁO VIÊN HƯỚNG DẪN
………
………
………
………
………
………
………
………
………
………
………
………
………
………
Đà Nẵng, ngày tháng năm 2013
Giáo viên hướng dẫn
Ngô Đình Thưởng
Trang 8Chương 1: CƠ SỞ LÝ THUYẾT VÀ ỨNG DỤNG
1.1 Sơ lược về lập trình Socket
1.1.1 Khái niệm IPAddress và Port
Nguyên lý: Trong một máy có rất nhiều ứng dụng muốn trao đổi với các ứng dụng khác thông qua mạng (VD: Có 2 ứng dụng trên máy A muốn trao đổi với 2 ứng dụng trên máy B) Mỗi máy tính chỉ có duy nhất một đường truyền dữ liệu (để gửi và nhận dữ liệu cho nhau)
Vấn đề: Rất có thể xảy ra "nhầm lẫn" khi dữ liệu từ máy A gửi đến máy B thì không biết là dữ liệu đó gửi cho ứng dụng nào trên máy B? Trong quá trình truyền
và nhận dữ liệu có thể xảy ra lỗi làm mất mát hoặc sai lệch dữ liệu
Giải quyết: Mỗi ứng dụng trên máy B sẽ được gán một số hiệu (gọi là cổng Port), số hiệu cổng này từ 1 65535 Khi ứng dụng trên máy A muốn gửi cho ứng dụng nào trên máy B thì chỉ việc điền thêm số hiệu cổng (vào trường RemotePort) vào gói tin cần gửi Trên máy B, các ứng dụng sẽ việc kiểm tra giá trị cổng trên mỗi gói tin xem có trùng với số hiệu cổng của mình (đã được gán – chính
là giá trị Localport) hay không? Nếu bằng thì xử lý, còn trái lại thì không làm gì Như vậy khi cần trao đổi dữ liệu cho nhau thì hai ứng dụng cần phải biết thông tin tối thiểu là địa chỉ IP (IP Address) và số hiệu cổng (Port) của ứng dụng kia
1.1.2 Lớp IPAddress
IPAddress : là một lớp dùng để mô tả một địa chỉ IP, lớp này có thể sử
dụng trong nhiều phương thức của Socket Một số phương thức của lớp IPAddress
Phương thức:
Equals: So sánh 2 địa chỉ IP
GetHashCode: Lấy giá trị hash cho một đối tượng IPAddress
GetType: Trả về kiểu của một thể hiện địa chỉ IP
HostToNetworkOrder: Chuyển một địa chỉ IP từ host byte order thành network dy order IsLoopBack Cho biết địa chỉ IP có phải là địa chỉ LoopBack hay không
Trang 91.1.3 Lớp IPEndPoint
IPEndPoint là một một lớp các đối tượng mô tả sự kết hợp của một địa chỉ IP
và Port Đối tượng trong lớp IPEndPoint được dùng để gắn kết các Socket với các địa chỉ cục bộ hoặc các địa chỉ từ xa
Hai thuộc tính của IPEndPoint có thể được dùng để lấy được vùng các port trên
Trang 10trình viên nên NET Framework cung cấp một số lớp để giảm gánh nặng lập trình Một trong những lớp đó là NetworkStream, và 2 lớp dùng để gởi và nhận Text
sử dụng giao thức TCP là StreamWriter và StreamReader
Lớp NetworkStream nằm trong namespace System.Net.Sockets, lớp này có nhiều phương thức tạo lập để tạo một thể hiện của lớp NetworkStream nhưng phương thức tạo lập sau hay được dùng nhất:
SocketType.Stream, ProtocolType.TCP);
NetworkStream ns = new NetworkStream(server);
Namespace System.IO chứa 2 lớp StreamReader và StreamWriter điều khiển việc đọc và ghi các thông điệp text từ mạng Cả 2 lớp đều có thể được triển khai với một đối tượng NetworkStream để xác định các hệ thống đánh dấu cho các thông điệp TCP
Lớp StreamReader có nhiều phương thức tạo lập, trong đó phương thức tạo lập đơn giản nhất của lớp StreamReader là :
public StreamReader(Stream stream);
Biến stream có thể được tham chiếu đến bất kì kiểu đối tượng Stream nào kể cả đối tượng NetworkStream Có nhiều phương thức và thuộc tính có thể được dùng với đối tượng StreamReader sau khi nó được tạo ra
Tương tự đối tượng StreamReader, đối tượng StreamWriter có thể được tạo
ra từ một đối tượng NetworkStream:
public StreamWriter(Stream stream);
StreamWriter cũng có nhiều phương thức và thuộc tính kết hợp với nó, một
số phương thức và thuộc tính của lớp StreamReader cũng có trong đối tượng của StreamWrite
1.1.5 Lập trình Socket phi kết nối(UDP)
Các Socket phi kết nối cho phép gởi các thông điệp mà không cần phải thiết lập kết nối trước Một phương thức đọc toàn bộ thông điệp được gởi bởi một phương thức gởi, điều này làm tránh được các rắc rối, phức tạp với biên dữ liệu Thật không may mắn là giao thức phi kết nối UDP không đảm bảo dữ liệu được truyền tới đích Nhiều yếu tố như mạng bận, mạng bị ngắt kết nối giữa chừng có thể ngăn cản các
Trang 11gói tin được truyền tới đích
Nếu một thiết bị chờ dữ liệu từ một thiết bị ở xa, nó phải được gán một địa chỉ
và port cục bộ, dùng hàm Bin() để gán Một khi đã thực hiện xong, thiết bị có thể dùng Socket để gởi dữ liệu ra ngoài hay nhận dữ liệu từ Socket
Bởi vì thiết bị Client không tạo ra kết nối đến một địa chỉ Server cụ thể do đó phương thức Connect() không cần dùng trong chương trình UDP Client Mô hình bên dưới mô tả các bước lập trình Socket phi kết nối
Hình 1.2 Lập trình socket phi kết nối (UDP)
1.1.6 Lớp Helper của C# Socker
1.1.6.1 Lớp TCPClient:
Lớp TCPClient nằm trong namespace System.Net.Sockets được thiết kế để
hổ trợ cho việc viết ứng dụng của TCP Client được dể dàng Lớp TcpClient cho phép tạo ra một đối tượng Tcp Client, sử dụng một trong ba phương thức tạo lập như sau:
TcpClient(): Là phương thức tạo lập đầu tiên, đối tượng được tạo ra bởi phương thức tạo lập này sẽ gắn kết với một địa chỉ cục bộ và một Port TCP ngẫu nhiên Sau khi đối tượng TcpClient được tạo ra, nó phải được kết nối đến thiết bị ở xa thông qua phương thức Connect như ví dụ dưới đây:
Trang 12TcpClient newcon = new TcpClient();
Newcon.Connect(“192.168.1.1”,2011);
TcpClient(IPEndPoint localEP): Phương thức tạo lập này cho phép chúng ta chỉ ra địa chỉ IP cục bộ cùng với port được dùng Đây là phương thức tạo lập thường được sử dụng khi thiết bị có nhiều hơn một card mạng và chúng ta muốn dữ liệu được gởi trên card mạng nào Phương thức Connect() cũng được dùng để kết nối với thiết bị ở xa:
IPEndPoint iep = new
xa có thể là một chuỗi hostname hoặc một địa chỉ IP Phương thức tạo lập của TcpClient sẽ tự động phân giải hostname thành địa chỉ IP:
TcpClient newcon = new TcpClient(“http://www.cit.udn.vn”,2011);
Mỗi khi đối tượng TcpClient được tạo ra, nhiều thuộc tính và phương thức có thể được dùng để xử lý việc truyền dữ liệu qua lại giữa các thiết bị Mỗi khi đối tượng TcpClient được tạo ra, nhiều thuộc tính và phương thức
có thể được dùng để xử lý việc truyền dữ liệu qua lại giữa các thiết bị
Phương thức:
Close(): Đóng kết nối TCP
Connect(): Thành lập kết nối TCP Client với các thiết bị ở xa
Equals(): So sánh hai đối tượng TcpClient
GetHashCode(): Lấy mã hash code
GetStream(): Lấy đối tượng Stream nó có thể dùng để gởi và nhận dữ liệu
GetType(): Lấy kiểu của thể hiện hiện tạo
ToString() Chuyển thể hiện hiện tại sang kiểu chuỗi
Mỗi khi kết nối được thành lập, phương thức GetStream() gán một đối tượng NetworkStream để gởi và nhận vào phương thức Read() và Write() Lớp
Trang 13TcpClient còn có nhiều thuộc tính được mô tả như sau
ReceiveBufferSize: Lấy hoặc thiết lập kích thước bộ đệm TCP nhận
ReceiveTimeout: Lấy hoặc thiết lập thời gian timeout của Socket
SendBufferSize: Lấy hoặc thiết lập kích thước bộ đệm TCP gởi
SendTimeout: Lấy hoặc thiết lập giá trị timeout của Socket
đó thì ta dùng một đối tượng IPEndPoint để chỉ ra địa chỉ IP của card mạng cùng với số port dùng để lắng nghe
Trang 14 Equals(): So sánh hai đối tượng TcpListener
GetHashCode(): Lấy hash code
GetType(): Lấy kiểu của thể hiện hiện tại
Panding(): Kiểm tra xem yêu cầu đang chờ kết nối hay không
Start(): Bắt đầu lắng nghe kết nối
Stop(): Ngừng lắng nghe kết nối
ToString(): Chuyển đối tượng TcpLisener thành chuỗi
Sau khi đối tượng TcpClient được tạo ra, tất cả các truyền thông với thiết
bị ở xa đó được thực hiện với đối tượng TcpClient mới chứ không phải với đối tượng TcpListener có thể chấp nhận kết nối khác Để đóng đối tượng TcpListener ta dùng phương thức Stop()
UdpClient(int port): Gán đối tượng UdpClient mới tạo vào một port
UdpClient(IPEndPoint iep): Gán đối tượng UdpClient mới tạo vào một địa chỉ IP cục bộ và một port
UdpClient(string host, string port): Gán đối tượng UdpClient mới tạo vào một địa chỉ IP và một port bất kỳ và kết hợp nó với một địa IP ở xa Các phương thức tạo lập của lớp UdpClient tương tự như các phương pháp tạo lập của lớp TcpClient, chúng ta có thể cho hệ thống chọn port thích hợp cho ứng dụng hoặc ta có thể chỉ ra port được dùng trong ứng dụng Nếu ứng dụng UDP phải chấp nhận dữ liệu trên một port nào đó, ta phải định nghĩa port đó trong phương thức tạo lập của lớp UdpClient
Phương thức:
Close(): Đóng Socket ở bên dưới
Connect(): Cho phép chỉ ra IPEndPoint ở xa để gởi và nhận dữ liệu
DropMulticastGroup(): Gở bỏ Socket từ một nhóm UDP Multicast
Trang 15 Equals(): So sánh hai đối tượng UdpClient
GetHashCode(): Lấy hash code
GetType(): Lấy kiểu của đối tượng hiện tại
JoinMulticastGroup(): Thêm Socket vào một nhóm UDP Multicast
Receive(): Nhận dữ liệu từ Socket
Send(): Gởi dữ liệu đến thiết bị ở xa từ Socket
ToString(): Chuyển đối tượng UdpClient thành chuỗi
1.2 Xử lý tiến trình trong lập trình đa luồng
1.2.1 Khái niệm luồng
Một luồng (Thread) là một chuỗi liên tiếp những sự thực thi trong chương trình Trong một chương trình C#, việc thực thi bắt đầu bằng phương thức main() và tiếp tục cho đến khi kết thúc hàm main() Cấu trúc này rất hay cho những chương trình có một chuỗi xác định những nhiệm vụ liên tiếp Nhưng thường thì một chương trình cần làm nhiều công việc hơn vào cùng một lúc Ví
dụ trong Internet Explorer khi ta đang tải một trang web thì ta nhấn nút back hay một link nào đó, để làm việc này Internet Explorer sẽ phải làm ít nhất là 3 việc:
Lấy dữ liệu được trả về từ Internet cùng với các tập tin đi kèm
Thể hiện trang Web
Xem người dùng có nhập để làm thứ gì khác không
Để đơn giản vấn đề này ta giả sử Internet Explorer chỉ làm hai công việc:
Xem người dùng có nhập để làm thứ gì khác không
Xem người dùng có nhập gì không
Để thực hành việc này ta sẽ viết một phương thức dùng để lấy và thể hiện trang Web Giả sử rằng việc trình bày trang Web mất nhiều thời gian (do phải thi hành các đoạn javascript hay các hiệu ứng nào đó …) Vì vậy sau một khoảng thời gian ngắn khoảng 1/12 giây, phương thức sẽ kiểm tra xem người dùng có nhập gì không Nếu có thì nó sẽ đuơc xử lí, nếu không thì việc trình bày trang sẽ được tiếp tục Và sau 1/12 giây việc kiểm tra sẽ được lặp lại Tuy
Trang 16nhiên viết phương thức này thì rất phức tạp do đó ta sẽ dùng kiến trúc event trong Window nghĩa là khi việc nhập xảy ra hệ thống sẽ thông báo cho ứng dụng bằng cách đưa ra một event Ta sẽ cập nhật phương thức để cho phép dùng các event:
Ta sẽ viết một bộ xử lí event để đáp ứng đối với việc nhập của người dùng
Ta sẽ viết một phương thức để lấy và trình bày dữ liệu Phương thức này được thực thi khi ta không làm bất cứ điều gì khác
Ta hãy xem cách phương thức lấy và trình bày trang web làm việc: đầu tiên
nó sẽ tự định thời gian Trong khi nó đang chạy, máy tính không thể đáp ứng việc nhập của người dùng Do đó nó phải chú ý đến việc định thời gian để gọi phương thức kiểm tra việc nhập của người dùng, nghĩa là phương thức vừa chạy vừa quan sát thời gian Bên cạnh đó nó còn phải quan tâm đến việc lưu trữ trạng thái trước khi nó gọi phương thức khác để sau khi phương thức khác thực hiện xong nó sẽ trả về đúng chỗ nó đã dừng Vào thời Window 3.1 đây thực sự
là những gì phải làm để xử lí tình huống này Tuy nhiên ở NT3.1 và sau đó là Windows 95 trở đi đã có việc xử lí đa luồng điều này làm việc giải quyết vấn
đề tiện lợi hơn Dưới đây chúng ta sẽ tìm hiểu một vài lớp cơ bản trong ngôn ngữ lập trình C# và vấn đề đồng bộ hóa (Synchronization) trong lập trình đa luồng
1.2.2 Khảo sát namespace System.Threading
Namespace System.Threading cung cấp một số kiểu dữ liệu cho phép bạn thực hiện lập trình đa luồng Ngoài việc cung cấp những kiểu dữ liệu tượng trưng cho một luồng cụ thể nào đó, namespace này còn định nghĩa những lớp
có thể quản lý một collection các luồng (ThreadPool), một lớp Timer đơn giản (không dựa vào GUI) và các lớp cung cấp truy cập được đồng bộ vào dữ liệu được chia sẽ sử dụng
Trang 17Một số lớp của namespace System.Threading:
Các lớp thành viên Mô tả
Interlocked Lớp này dùng cung cấp truy cập đồng bộ hóa vào dữ
liệu được chia sẽ sử dụng (shared data)
Moniter Lớp này cung cấp việc đồng bộ hóa các đối tượng luồng
sử dụng khóa chốt (lock) và tín hiệu chờ (wait signal)
Mutex Lớp này cung cấp việc đồng bộ hóa sơ đẳng có thể
được dùng đối với inter process synchronization
Thread Lớp này tượng trưng cho một luồng được thi hành trong
lòng Common Language Runtime Sử dụng lớp này bạn
có khả năng bổ sung những luồng khác trong cùng AppDomain
ThreadPool Lớp này quản lý những luồng có liên hệ với nhau trong
cùng một Process nào đó
Timer Cho biết một delegate có thể được triệu gọi vào một lúc
được khai báo nào đó Tác vụ wait được thi hành bởi luồng trong thread pool
WaitHandle Lớp này tượng trưng cho tất cả các đối tượng đồng bộ
hóa (cho phép multiple wait) vào lúc chạy
ThreadStart Lớp này là một delegate chỉ về hàm hành sự nào đó
phải được thi hành đầu tiên khi một luồng bắt đầu
TimerCallBack Delegate đối với Timer
WaitCallBack Lớp này là một delegate định nghĩa hàm hành sự kêu
gọi lại (callback) đối với ThreadPool user work item.Lớp đơn giản nhất trong tất cả các lớp thuộc Namespace System.Threading
là lớp Thread Lớp này tượng trưng cho một vỏ bọc hướng đối tượng bao quanh một lộ trình thi hành trong lòng một AppDomain nào đó Lớp này định nghĩa một số hàm thực thi (cả static lẫn shared) cho phép bạn tạo mới những luồng từ luồng hiện hành, cũng như cho Sleep, Stop hay Kill một luồng nào đó
Trang 18Các thành phần static của lớp Thread:
Các thành phần Static Mô tả
CurrentThread Thuộc tính read-only này trả về một quy chiếu về
luồng hiện đang chạy
GetData() Đi lấy vị trí từ slot được khai báo trên luồng hiện
hành đối với domain hiện hành trong luồng
SetData() Cho đặt để trị lên slot được khai báo trên luồng hiện
hành đối với domain hiện hành trong luồng
GetDomain()
GetDomainID()
Đi lấy một qui chiếu về AppDomain hiện hành (hoặc mã nhận diện ID của domain này) mà luồng hiện đang chạy trên đó
Sleep() Cho ngưng luồng hiện hành trong một thời gian nhất
định được khai báo
Ngoài ra lớp Thread cũng hổ trợ các thành viên cấp đối tượng
Các lớp thành viên Mô tả
IsAlive Thuộc tính này trả về một trị boolean cho biết liệu xem
luồng đã khởi đông hay chưa
IsBackground Đi lấy hoặc đặt để giá trị cho biết liệu xem luồng là một
luồng nền hay không
mang tính thân thiện đối với luồng
Priority Đi lấy hoặc đặt để ưu tiên của một luồng Có thể được
gán một trị lấy từ enumeration ThreadPriority (chẳng hạn Normal, Lowest, Highest, BelowNormal, AboveNormal).
ThreadState Đi lấy hoặc đặt để tình trạng của luồng Có thế được
gán từ enumeration ThreadState (chẳng hạn Unstarted, Running, WaitSleepJoin, Suspended, SuspendRequested, AbortRequested, Stopped).
Interrup() Cho ngưng chạy luồng hiện hành
Join() Yêu cầu luồng chờ đối với luồng bị ngưng chạy
Resume() Tiếp tục lại đối với một luồng bị ngưng chạy
Trang 19Start() Cho bắt đầu thi hành luồng được khai báo bởi delegate
ThreadStart
Suspend() Cho ngưng chạy một luồng Nếu luồng đã bị ngưng rồi,
một triệu gọi hàm Suspend() sẽ không có tác dụng.
Abort() Kết thúc một tiểu trình bằng cách nén ngoại lệ
System.Threading.ThreadAbortException trong mã lệnh đang được chạy
Mã lệnh của tiểu trình bị hủy có thể tắt ngoại lệ ThreadAbortException để thực hiện việc dọn dẹp, nhưng bộ thực thi sẽ tự động nén ngoại lệ này lần nữa
để đảm bảo tiểu trình kết thúc, trừ khi ResetAbort được gọi
1.3 Đồng bộ và bất đồng bộ trong lập trình đa luồng
Đôi khi có thể bạn muốn điều khiển việc truy cập vào một nguồn lực, chẳng hạn các thuộc tính hoặc các hàm của một đối tượng, làm thế nào chỉ một mạch trình được phép thay đổi hoặc sử dụng nguồn lực đó mà thôi Việc đồng
bộ hóa được thể hiện thông qua một cái khóa được thiết lập trên đối tượng, ngăn không cho luồng nào đó truy cập khi mạch trình đi trước chưa xong công việc
Trong phần này, ta sẽ là quen với cơ chế đồng bộ hóa mà Common Language Runtime cung cấp: lệnh lock Nhưng trước tiên, ta cần mô phỏng một nguồn lực được chia sẽ sử dụng bằng cách sử dụng một biến số nguyên đơn giản counter
Để bắt đầu, ta khai báo biến thành viên và khởi gán về zero:
int counter = 0;
Bài toán được đặt ra ở đây như sau: Luồng thứ nhất sẽ đọc trị counter (0) rồi gán giá trị này cho biến trung gian (temp) Tiếp đó tăng trị của temp rồi Sleep một khoảng thời gian Luồng thứ nhất xong việc thì gán trị của temp trả
về cho counter và cho hiển thị trị này Trong khi nó làm công việc, thì luồng thứ hai cũng thực hiện một công việc giống như vậy Ta cho việc này lập này khoảng 1000 lần Kết quả mà ta chờ đợi là hai luồng trên đếm lần lượt tăng biến counter lên 1 và in ra kết quả 1,2, 3, 4 … tuy nhiên ta sẽ xét đoạn chương trình dưới đây và thấy rằng kết quả hoàn toàn khác với những gì mà chúng ta mong đợi
Trang 20private int counter = 0;
static void Main(string[] args)
Console.WriteLine("Start thread {0}", t1.Name);
Thread t2 = new Thread(new ThreadStart(Incrementer)); t2.IsBackground = true;
t2.Name = "Thread Two"; t2.Start();
Console.WriteLine("Start thread {0}", t2.Name);
{ while (counter < 1000)
{
nt temp = counter; temp++;
Thread.Sleep(1); counter = temp;
Console.WriteLine("Thread {0}
Incrementer: {1}", Thread.CurrentThread.Name, counter); }
Trang 21} catch (ThreadInterruptedException) {
Console.WriteLine("Thread {0} interrupted! Cleaning up ", Thread.CurrentThread.Name); }
finally {
Console.WriteLine("Thread {0} Existing.", Thread.CurrentThread.Name);
} } } }
Kết quả đạt được là :
Hình 1.3 Kết quả chương trình đồng bộ hóa
Do đó ta cần phải đồng bộ hóa việc truy cập đối tượng counter C# cung cấp đối tượng Lock để thưc hiện công việc đồng bộ hóa này Một lock sẽ đánh dấu một critical section trên đoạn mã đồng thời cung cấp việc đồng bộ hóa đối với đối tượng được chỉ định khi lock có hiệu lực Cú pháp sử dụng một Lock yêu cầu khóa chặt một đối tượng rồi thi hành một câu lệnh hoặc một khối lệnh rồi sẽ mở khóa ở cuối câu hoặc khối lệnh đó C# cung cấp hổ trợ trực tiếp khóa chặt thông qua từ chốt lock Ta sẽ tra qua theo một đối tượng qui chiếu
và theo sau từ chốt là một khối lệnh
lock(expression) statement-block
Trong ví dụ trên, để có được kết quả như mong muốn, ta sẽ sửa hàm
Trang 22Incrementer lại như sau:
try
{
lock (this) {
while (counter < 1000) {
int temp = counter;
Kết quả thu được sẽ là:
Hình 1.4 Kết quả chương trình bất đồng bộ 1.4 Hệ mã hóa công khai RSA
1.4.1 Khái niệm mã hóa
Trong mọi lĩnh vực kinh tế, chính trị, xã hội, quân sự… luôn có nhu cầu trao đổi thông tin giữa các cá nhân, các công ty, tổ chức, hoặc giữa các quốc gia với nhau Ngày nay, với sự phát triển của công nghệ thông tin đặt biệt là mạng internet thì việc truyền tải thông tin đã dể dàng và nhanh chóng hơn
Trang 23Hình 1.5 Mã hóa
Và vấn đề đặt ra là tính bảo mật trong quá trình truyền tải thông tin, đặt biệt quan trọng đối với những thông tin liên quan đến chính trị, quân sự, hợp đồng kinh tế… Vì vậy nghành khoa học nghiên cứu vế mã hóa thông tin được phát triển Việc mã hóa là làm cho thông tin biến sang một dạng khác khi đó chỉ
có bên gửi và bên nhận mới đọc được, còn người ngoài dù nhận được thông tin nhưng cũng không thể hiểu được nôi dung
Hình 1.6 Mã hóa và giải mã thông tin được truyền đi
Như chúng ta thấy ở mô hình 1.5 : Việc trao đổi thông tin được thực hiện qua các bước sau:
Tạo ra thông tin cần gởi đi
Gởi thông tin này cho đối tác
Ở mô hình 1.6: Việc trao đổi thông tin được thực hiện:
Tạo thông tin cần gởi
Mã hóa và gởi thông tin đã được mã hóa đi
Đối tác nhận và giải mã thông tin
Đối tác có được thông tin ban đầu của người gởi
Với 2 thao tác mã hóa và giải mã ta đã đảm bảo thông tin được gửi an toàn
và chính xác
Chúng ta có nhiều phương pháp để mã hóa thông tin: Ở đây ta tìm hiểu về
hệ mã hóa công khai RSA
Trang 241.4.2 Hệ mã khóa
Mã khóa bí mật: thông tin sẻ được mã hóa theo một phương pháp ứng với một key, key này dùng để lập mã và đồng thời cũng để giải mã Vì vậy key phải được giữ bí mật, chỉ có người lập mã và người nhận biết được, nếu key
bị lộ thì người ngoài sẽ dể dàng giải mã và đọc được thông tin
Hình 1.7 Mã hóa bí mật
Mã khóa công khai: sử dụng 2 key public key- private key
Public key: Được sử dụng để mã hoá những thông tin mà ta muốn chia sẻ với bất cứ ai Chính vì vậy ta có thể tự do phân phát nó cho bất cứ ai mà ta cần chia sẻ thông tin ở dạng mã hoá
Private key: Đúng như cái tên, Key này thuộc sở hữu riêng tư của bạn(ứng với public key) và nó được sử dụng để giải mã thông tin Chỉ mình bạn sở hữu
nó, Key này không được phép và không nên phân phát cho bất cứ ai
Nghĩa là mỗi người sẽ giữ 2 key 1 dùng để mã hóa key này được công bố rộng rãi, 1 dùng để giải mã key này giữ kín
Khi ai đó có nhu cầu trao đổi thông tin với bạn, sẽ dùng public key mà bạn công bố để mã hóa thông tin và gửi cho bạn, khi nhận được bạn dùng private key để giải mã Những người khác dù có nhận được thông tin nhưng không biết được private key thì cũng không thể giải mã và đọc được thông tin
Hình 1.8 Mã hóa công khai
Trang 251.4.3 Thuật toán mã hóa RSA
Thuật toán RSA có hai khóa: Khóa công khai (hay khóa công cộng) và khóa bí mật (hay khóa cá nhân) Mỗi khóa là những số cố định sử dụng trong quá trình mã hóa và giải mã Khóa công khai được công bố rộng rãi cho mọi người và được dùng để mã hóa Những thông tin được mã hóa bằng khóa công khai chỉ có thể được giải mã bằng khóa bí mật tương ứng Nói cách khác, mọi người đều có thể mã hóa nhưng chỉ có người biết khóa cá nhân (bí mật) mới có thể giải mã được
Ta có thể mô phỏng trực quan một hệ mật mã khoá công khai như sau :
B muốn gửi cho A một thông tin mật mà B muốn duy nhất A có thể đọc được
Để làm được điều này, A gửi cho B một chiếc hộp có khóa đã mở sẵn và giữ lại chìa khóa B nhận chiếc hộp, cho vào đó một tờ giấy viết thư bình thường
và khóa lại (như loại khoá thông thường chỉ cần sập chốt lại, sau khi sập chốt khóa ngay cả B cũng không thể mở lại được-không đọc lại hay sửa thông tin trong thư được nữa) Sau đó B gửi chiếc hộp lại cho A A mở hộp với chìa khóa của mình và đọc thông tin trong thư Trong ví dụ này, chiếc hộp với khóa mở đóng vai trò khóa công khai, chiếc chìa khóa chính là khóa bí mật
Tạo khóa:
Giả sử A và B cần trao đổi thông tin bí mật thông qua một kênh không an toàn (ví dụ như Internet) Với thuật toán RSA, A đầu tiên cần tạo ra cho mình cặp khóa gồm khóa công khai và khóa bí mật theo các bước sau:
a) Chọn 2 số nguyên tố lớn p và q với p≠q , lựa chọn ngẫu nhiên và độc
lập
b) Tính: n=pq
c) Tính: giá trị hàm số: (n)=(p-1)(q-1)
d) Chọn một số tự nhiên e sao cho 1<e<(n) và là số nguyên tố cùng nhau
với (n) Công bố (n,e).
e) Tính: d sao cho de1(mod (n))
Một số lưu ý:
Các số nguyên tố thường được chọn bằng phương pháp thử xác suất
Các bước 4 và 5 có thể được thực hiện bằng giải thuật Euclide mở rộng
Trang 26 Bước 5 có thể viết cách khác: Tìm số tự nhiên x sao cho
Khi đó sử dụng giá trị d=mod(p-1)(q-1)
Từ bước 3 sử dụng =LCM(p-1,q-1) thay cho (n)=(p-1)(q-1)
Khóa công khai bao gồm:
Một dạng khác của khóa bí mật bao gồm:
p and q, hai số nguyên tố chọn ban đầu,
d mod (p-1) và d mod (q-1) (thường được gọi là dmp1 và dmq1),
(1/q) mod p (thường được gọi là iqmp)
Dạng này cho phép thực hiện giải mã và ký nhanh hơn với việc sử dụng định
lý số dư Trung Quốc (tiếng Anh: Chinese Remainder Theorem - CRT) Ở dạng
này, tất cả thành phần của khóa bí mật phải được giữ bí mật
A gửi khóa công khai cho B, và giữ bí mật khóa cá nhân của mình Ở đây, p
và q giữ vai trò rất quan trọng Chúng là các phân tố của n và cho phép tính d khi biết e Nếu không sử dụng dạng sau của khóa bí mật (dạng CRT) thì p và q sẽ
được xóa ngay sau khi thực hiện xong quá trình tạo khóa
Mã hóa
Giả sử B muốn gửi đoạn thông tin M cho A Đầu tiên B chuyển M thành một
số m < n theo một hàm có thể đảo ngược (từ m có thể xác định lại M) được thỏa
thuận trước Quá trình này được mô tả ở phần sau “Chuyển đổi văn bản rõ”
Lúc này B có m và biết n cũng như e do A gửi B sẽ tính c là bản mã hóa của
m theo công thức:
c≡m e mod n
Hàm trên có thể tính dễ dàng sử dụng phương pháp tính hàm mũ (theo
môđun) bằng (thuật toán bình phương và nhân) Cuối cùng B gửi c cho A
Trang 27p = 61 (số nguyên tố thứ nhất (giữ bí mật hoặc hủy sau khi tạo khóa))
q = 53 ( số nguyên tố thứ hai (giữ bí mật hoặc hủy sau khi tạo khóa))
n = pq = 3233 (môđun (công bố công khai))
e = 17 (số mũ công khai)
d = 2753 ( số mũ bí mật)
Khóa công khai là cặp (e, n) Khóa bí mật là d Hàm mã hóa là:
encrypt(m) = m e mod n = m 17 mod 3233
với m là văn bản rõ Hàm giải mã là:
decrypt(c) = c d mod n = c2753 mod 3233