Đồ án Nghiên cứu và xây dựng chương trình ứng dụng giao tiếp audio trong môi trường mạng nội bộ

Chương 1: Cơ sở lý thuyết 3

1.1 Tổng quan về giao tiếp audio trong môi trường mạng nội bộ 3

1.1.1 Một số mô hình giao tiếp audio 3

1.1.2 Một số giao thức truyền thông 3

1.1.2.1 Giao thức IP 3

1.1.2.2 Giao thức TCP 4

1.1.2.3 Giao thức UDP 4

1.1.2.4 Socket 4

1.2 Một số chuẩn mã hoá và nén âm thanh 5

1.2.1 Một số chuẩn mã hóa 5

1.2.1.1 Giới thiệu chung 5

1.2.1.2 Mã hoá dạng sóng(waveform codec) 5

1.2.1.3 Mã hoá nguồn (Source codec) 6

1.2.1.4 Mã hoá hỗn hợp (hybrid codec) 6

1.2.2 Một số phương pháp nén tiếng nói 7

1.2.2.1 Giới thiệu chung 7

1.2.2.2 Các phương pháp nén cụ thể 8

1.3 Tìm hiểu hỗ trợ của Windows SDK trong xử lý và truyền nhận âm thanh 9

1.3.1 Môi trường Windows SDK đối với truyền âm thanh 9

1.3.1.1 Cấu trúc file wave và hàm playsound 10

1.3.2 Kỹ thuật truyền nhận âm thanh trên mạng IP 13

1.3.2.1 Mô hình liên kết và trao đổi dữ liệu 13

3.2.1.2 Dùng 2 socket 15

3.2.2 Cơ chế gọi và xác lập liên kết 17

3.2.3 Cơ chế truyền nhận dữ liệu 18

 

Chương 2: Thiết kế và cài đặt chương trình 23

2.1 Thiết kế chương trình 23

2.1.1 Môi trường và công cụ lập trình 23

2.1.1.1 Môi trường WINDOWS 23

2.1.1.2 Công cụ lập trình 23

2.1.2 Thiết kế chương trình 24

2.1.2.2 Thiết kế kiến trúc 24

2.1.2.3 Thiết kế chức năng 24

2.1.2.4 Thiết kế Modul 25

2.2 Cài đặt chương trình 26

2.3 Hướng dẫn cài đặt 29

2.3.1 Yêu cầu phần cứng 29

2.3.2 Yêu cầu phần mềm 29

2.3.3 Hướng dẫn sử dụng 30

Chương 3: Kết luận 32

3.1.Kết quả đạt được 32

3.2. Hướng phát triển 32

 

 

doc32 trang | Chia sẻ: lynhelie | Ngày: 13/01/2016 | Lượt xem: 700 | Lượt tải: 1download
Bạn đang xem trước 20 trang tài liệu Đồ án Nghiên cứu và xây dựng chương trình ứng dụng giao tiếp audio trong môi trường mạng nội bộ, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
kết quả gần đúng nhất với âm thanh ban đầu. Bộ giải mã chỉ cần biết chỉ số của xung kích thước đó, sau đó tra codebook rồi tái tạo lại âm thanh. Một số các tham số khác nhau như năng lượng của sự kích thích và giá trị chu kỳ cũng cần đến khi giải mã. Các phương pháp mã đi theo cách này đòi hỏi quá trình tính toán phức tạp, có thể tất cả các mục trong từ điển đều phải thử để đưa ra giá trị tốt nhất. Hình 2.1 Biểu đồ so sánh các phương pháp mã hoá 1.2.2 Một số phương pháp nén tiếng nói 1.2.2.1 Giới thiệu chung Ý tưởng nén tiếng nói là để giảm kích thước nhằm giúp ít tốn băng thông truyền qua mạng. Dòng dữ liệu tiếng nói được giải nén ở tốc độ lấy mẫu mặc định ( 8bits/mẫu, 8 khz, kênh mono) sẽ yêu cầu đường truyền có tốc độ 8000 mẫu/giây * 8 bits/ mẫu = 64 Kbits/giây để truyền dữ liệu qua mạng. Do đó, tùy theo tốc độ đường truyền thực tế trên mỗi mạng mà chọn giải pháp nén hay không nén dữ liệu trước khi truyền dữ liệu âm thanh qua mạng, cũng như chọn tỉ lệ nén là bao nhiêu cho phù hợp (chọn giải thuật nén). Vì nếu dữ liệu được nén thì phải giải nén khi được truyền đến máy nhận. Do đó cũng tốn thời gian để nén và giải nén dữ liệu, điều này dẫn đến ảnh hưởng thời gian thực của hệ thống. Đối với các mạng cục bộ, thường có tốc độ truyền của mạng cao nên có thể không cần phải nén tiếng nói trước khi truyền. Ngược lại, đối với mạng Internet, hệ thống được kết nối với Internet thông qua các modem chuẩn có tốc độ thấp 14,4 Kbits/s hoặc 28,8Kbits/s thì nhất thiết phải nén tiếng nói trước khi truyền và giải nén trước khi phát. Hai phương pháp nén âm thanh thường được dùng nhất để giảm băng thông là GSM và ADPCM. 1.2.2.2 Các phương pháp nén cụ thể a. Phương pháp nén tiếng nói theo chuẩn GSM GSM (Global System for Mobile comunications - Hệ thống truyền thông di động toàn cầu). GSM là một chuẩn điện thoại được Viện Tiêu Chuẩn Viễn Thông Châu Âu ETSI đề ra. Phát triển tại Đại học Kỹ thuật Berlin vào năm 1992, GSM là một trong những phương pháp nén âm thanh phức tạp nhất đang được sử dụng, cho tỉ lệ nén 1:10. Giải thuật GSM dựa trên giao thức truyền thông Mobile Phone, hiện tại là giao thức phổ biến nhất tại Châu Âu đối với điện thoại di động. Đầu vào của GSM bao gồm các frames 160 tín hiệu, những tín hiệu PCM tuyến tính 13 bits lấy mẫu ở 8 Khz. GSM có sẵn trong thư viện C có thể được dùng để tạo ra một đối tượng gsm giữ trạng thái cần thiết hoặc để mã hóa những mẫu PCM tuyến tính thành các frames GSM, hoặc giải mã các frames GSM thành các frames PCM tuyến tính. Bộ mã hóa nén 160 frames PCM 16 bits thành các frames GSM 260 bits. Tương ứng một giây tiếng nói thành 1625 bytes. Bởi vì mẫu 260 bits không chẵn để gắn vào các bytes 8 bits, nên bộ mã hóa sẽ mã hóa frame 160 bytes thành frame GSM 264 bits. Một buffer GSM nén 1 Mb có thể lưu tiếng nói gần 10 phút. Một dòng dữ liệu tiếng nói giải nén 16 bits/mẫu ở 8Khz yêu cầu băng thông tốc độ 128 Kbits/s, trong khi đó băng thông để truyền qua mạng nếu dùng giải thuật nén GSM , tiếng nói 16 bits/mẫu chỉ cần: ( 264 bits * 8.000 mẫu/giây)/160 mẫu = 13,2 Kbits/giây Cho tỉ lệ nén 128/13,2 = 9,7 tương đương 10 :1. b. Phương pháp nén ADPCM Nguyên tắc : Là một phương pháp có thể được dùng để nén các khối dữ liệu tiếng nói trước khi chúng được truyền đến các máy nhận và giải nén chúng để phát lại sau khi được nhận từ đường truyền. Giải thuật nén IMA_ ADPCM: Trong phạm vi luận văn này em tìm hiểu và ứng dụng giải thuật đưa ra bởi IMA (Interactive Multimedia Association) . Giải thuật IMA ADPCM nén những mẫu PCM tuyến tính thành các mức lượng hóa 4 bits, trong đó mỗi mẫu DPCM được biểu diễn bằng các giá trị âm thanh 16 bits, do đó giải thuật này cung cấp một tỉ lệ nén là 4:1. Quá trình thực hiện của giải thuật IMA_ADPCM là đọc từ những bộ đệm có giá trị kiểu nguyên và nén chúng thành một mẫu âm thanh 16 bits được biểu diễn bằng các mức lượng hóa 4 bit. Bởi vì không có giới hạn trong kích thước buffers tiếng nói nên những mã ADPCM được kết hợp một cách dễ dàng với các phần còn lại của chương trình để nén tiếng nói khi thu và giải nén trở lại khi phát 1.3 Tìm hiểu hỗ trợ của Windows SDK trong xử lý và truyền nhận âm thanh 1.3.1 Môi trường Windows SDK đối với truyền âm thanh Môi trường Windows SDK là môi trường lập trình đa phương tiện dưới Windows, cung cấp các hàm cấp thấp rất thích hợp cho các ứng dụng trên mạng. Một cách thức đơn giản nhất trong việc xuất dữ liệu waveform ra loa là dùng hàm PlaySound. Chúng ta có thể thao tác với dạng dữ liệu waveform bằng các hàm cấp thấp do hệ thống cung cấp. Ngoài ra hệ thống còn cung cấp một cơ chế giúp người lập trình giao tiếp dễ dàng hơn với thiết bị, đó là các hàm MCI. 1.3.1.1 Cấu trúc file wave và hàm playsound a. Cấu trúc file wave Một file wave thật sự là một phần của một lớp file lớn hơn dùng bởi các hàm multimedia của windows là các file RIFF ( Resource Interchange File Format). Một file RIFF bao gồm một hoặc nhiều chunk. Trong mỗi chunk có con trỏ chỉ đến chunk kế tiếp. Mỗi chunk có một mô tả kiểu theo sau bởi một số dữ liệu. Một ứng dụng để đọc các file RIFF có thể bước qua một số chunk, đọc các chunk cần quan tâm và bỏ qua các chunk không liên quan. Chunk file RIFF luôn luôn bắt đầu bằng header sau: Typedef struct { FOURCC ckID; DWORD cksize; }CK; Trong đó: FOURCC là một vùng 4 bytes định nghĩa loại chunk. Vùng này sẽ chứa từ WAVE đối với file wave. ckSize đặc tả kích thước dữ liệu trong chunk, sau header này chúng ta sẽ tìm thấy cSize bytes dữ liệu. Các chunk có thể chứa các subchunks. Cấu trúc thật sự một file wave cơ bản bao gồm một chunk fmt theo sau là một chunk dữ liệu. Có thể có những chunk khác phía sau chunk WAVE nhưng thiết bị sử dụng file WAVE sẽ bỏ qua các chunk này. Hình sau mô tả cấu trúc file RIFF chứa dữ liệu WAVE. ID SIZE FROM TYPE "fmt" SIZE "data" SIZE Hai subchunk trong chunk wave đặc tả thông tin về một âm thanh file wave và sau đó là chính dữ liệu âm thanh. Chunk fmt chứa chủ yếu đối tượng WAVEFORMAT và một số dữ liệu thêm vào gắn ở cuối chunk. Một đối tượng WAVEFORMAT được định nghĩa như sau : Typedef struct waveformar_tag{ WORD wFormatTag; WORD nChannels; DWORD nSamplesPerSec; DWORD nAvgBytesPerSec; WORD nBlockAlign; } WAVEFORMAT; Trong đó: wFormatTag : Chứa hằng WAVE_FORMAT_PCM được định nghĩa trong MMSYSTEM.H như sau: # define WAVE_FORMAT_PCM 1 Giá trị WAVE_FORMAT_PCM báo cho phần mềm đọc file wave biết cách âm thanh trong nó được mã hoá. nChannels : Của đối tượng WAVEFORMAT có 2 giá trị : 1 đối với âm thanh mono. 2 đối với âm thanh stereo. nSamplePerSec : Cho biết tần số lấy mẫu của âm thanh để có thể thu và phát cùng một tốc độ, giá trị thông thường của field này nhận một những giá trị sau: 11025 - 11,025Khz 22055 - 22,050Khz 44100 - 44,1 Khz nAvgBytesPerSec : Cho biết số bytes trung bình trong mỗi giây để thu và phát dữ liệu wave. nBlockAlign : Xác định số bytes yêu cầu chứa trong một mẫu Những mẫu có độ phân giải nhỏ hơn hoặc bằng 8 bits có thể lưu vào 1 bytes. Những mẫu có độ phân giải từ 9 đến 16 bits yêu cầu 2 bytes. Những mẫu stereo yêu cầu số bytes gấp đôi những mono. Trong cấu trúc trên không định nghĩa số bits thật sự trong một mẫu dữ liệu âm thanh file wave, để định nghĩa số bits trong một mẫu ta dùng cấu trúc sau: Typedef struct pvmwaveformat_tag{WAVEFORMAT wf; WORD wBitsPerSample; } PCMWAVEFORMAT; Trong đó: wf: Đối với dữ liệu subchunk fmt của một chunk WAVE chúng ta thật sự làm việc với đối tượng PCMWAVEFORMAT. nBitsPerSample: Xác định số bits thật sự trong một mẫu . Trong một mẫu mono 8 bits dữ liệu của chunk dữ liệu gồm một chuỗi dài có giá trị 1 byte. Những mẫu stereo được chia ra với byte đầu dùng cho kênh bên trái và byte thứ hai dùng cho kênh bên phải, như vậy mỗi mẫu stereo 8 bits sẽ cần 2 bytes. Để làm việc với một file RIFF bao gồm các bước sau : Mở file. Vào chunk cần thiết. Chuyển con trỏ file vào vị trí bắt đầu dữ liệu của chunk. Hoàn tất, ra khỏi chunk. Vào chunk kế tiếp. b. Hàm Playsound Chúng ta dùng hàm PlaySound để play dữ liệu dạng waveform hoặc chúng ta có thể dùng hàm sndPlaySound. Tuy nhiên trong môi trường Win32 thì nên dùng hàm PlaySound. Hàm PlaySound cho phép chúng ta chỉ định các thông số nguồn âm thanh theo các cách sau: Dùng tên alias khai báo trong file WIN.INI Dùng tên file. Dùng chỉ số nhận dạng tài nguyên Waveform-Audio Files Trong môi trường Windows, phần lớn các file âm thanh dạng waveform đều có phần mở rông là .WAV Ví dụ dưới đây minh họa cho việc phát file âm thanh “AmThanh.WAV” PlaySound("C:\\SOUNDS\\AmThanh.WAV", NULL, SND_SYNC); Play sound theo các hiện tượng Hàm PlaySound còn cho phép chúng ta xuất âm thanh tùy theo một sự kiện nào đó xảy ra trong hệ thống như click mouse hay nhấn một phím nào đó. Hệ thống sẽ phát âm thanh tùy theo hiệc tượng xảy ra để cảnh báo người sử dụng. Âm thanh dạng này được gọi là sound events. Để xác định sound event, hàm PlaySound sẽ được gọi với thông số pszSound trỏ đến bảng đăng ký sự kiện. Ví dụ chúng ta sẽ gọi hàm PlaySound ứng với sự kiện mouse click như sau: PlaySound("MouseClick", NULL, SND_SYNC); 1.3.2 Kỹ thuật truyền nhận âm thanh trên mạng IP 1.3.2.1 Mô hình liên kết và trao đổi dữ liệu Chương trình dùng giao thức TCP/IP làm giao thức giao tiếp. Việc thiết lập liên kết cũng như trao đổi dữ liệu đều tuân theo các cấp của giao thức này. Việc gọi và thiết lập liên kết được thực hiện theo mô hình client/server, việc trao đổi dữ liệu được thực hiện thông qua socket theo giao thức TCP. Có hai ý tưởng được đưa ra trong việc dùng socket để trao đổi dữ liệu. a. Dùng 1 socket Mỗi máy dùng một socket để truyền nhận dữ liệu. Theo giao thức TCP sau khi hai socket connect được với nhau thì việc tiến hành trao đổi dữ liệu sẽ bắt đầu. Chúng ta sẽ dùng cặp socket này. Như vậy, một socket trên một máy đồng thời đảm nhận việc truyền dữ liệu đi cũng như nhận dữ liệu về. Hình 3.1 Mô hình dùng 1 socket Cách dùng này có đặc điểm là việc tạo liên kết đơn giản, quá trình tạo liên kết hoàn toàn giống như các bước trong việc tạo liên kết giữa các socket dùng giao thức TCP. Chương trình chạy và lắng nghe ở một port xác định. Khi có một yêu cầu gọi liên kết đến, chương trình sẽ tạo ra một socket để nối kết với socket gọi. Sau khi thiết lập liên kết thì các socket bắt đầu gửi nhận dữ liệu. Socket sẽ gửi dữ liệu âm thanh đi đồng thời nhận dữ liệu truyền tới và chuyển cho hệ thống xử lý. Socket làm việc theo cách này sẽ nhận hai thông báo cùng một lúc. Khi có dữ liệu từ mạng truyền tới, hệ thống sẽ thông báo cho socket để tiến hành việc nhận dữ liệu. Cũng tương tự như vậy, khi có dữ liệu âm thanh sẵn sàng, hệ thống cũng sẽ gọi socket để truyền đi. Như vậy, khi thực thi socket sẽ nhận được hai thông báo của hệ thống. Vì việc truyền nhận dữ liệu âm thanh là dạng dữ liệu liên tục cho nên tần suất mà hệ thống thông báo cho socket là rất thường xuyên. Vì vậy, socket trong cùng một lúc có thể nhận được cả hai yêu cầu truyền dữ liệu đi và nhận dữ liệu về. Thêm vào đó các hoạt động truyền nhận dữ liệu là các hoạt động bị tắc nghẽn. Do đó chúng ta phải lưu ý đến hiện tượng này, socket có thể đáp ứng không kịp nhu cầu của hệ thống. Chúng ta lấy một trường hợp ví dụ. Khi socket nhận được yêu cầu truyền dữ liệu đi, nó sẽ lấy dữ liệu từ các buffer và truyền đi. Do quá trình truyền dữ liệu có thể bị tắc nghẽn, socket sẽ phải chờ. Đồng thời trong lúc này, nó lại nhận được tín hiệu thông báo có buffer kế tiếp cần truyền đi và tín hiệu thông báo có dữ liệu trên mạng truyền về. Với các yêu cầu dồn dập như vậy, hệ thống có thể sẽ đáp ứng không kịp và chương trình có thể bị treo. Vì vậy, khi dùng một socket để truyền nhận dữ liệu, chúng ta phải tính toán cân đối thời gian giữa việc truyền dữ liệu đi và việc nhận dữ liệu về sao cho hợp lý để hệ thống có thể làm việc liên tục được. Chúng ta có thể qui định thời gian cho việc truyền nhận. Trong một thời điểm socket có thể chỉ làm việc truyền dữ liệu đi, các yêu cầu nhận dữ liệu sẽ bị ngưng lại. Sau đó socket sẽ chỉ xử lý các yêu cầu nhận dữ liệu. Chiến lược này giúp giảm nhẹ hoạt động của socket. Tuy nhiên, chúng ta cần áp dụng cho cả hai socket liên kết. Trong một thời điểm, một socket sẽ truyền còn socket còn lại sẽ nhận dữ liệu, và thời điểm sau thì quá trình sẽ diễn ra theo chiều ngược lại. 3.2.1.2 Dùng 2 socket Xuất phát từ ý tưởng trên, chúng ta có thể dùng hai socket trong việc trao đổi dữ liệu. Một liên kết hình thành giữa hai máy sẽ gồm hai cặp socket liên kết với nhau. Một socket chỉ đảm nhận việc truyền dữ liệu trong khi socket còn lại đảm nhận việc nhận dữ liệu. Hình 3.2 Mô hình dùng 2 socket Vì mỗi socket chỉ nhận một tín hiệu nhất định. Socket truyền sẽ chỉ chú ý tới tín hiệu báo có dữ liệu của hệ thống để tiến hành truyền dữ liệu đi. Trong khi đó, socket nhận sẽ chỉ lưu ý đến tín hiệu báo có dữ liệu của hệ thống. Hai socket sẽ hoạt động độc lập với nhau và công việc của một socket sẽ nhẹ nhàng hơn mô hình trên. Tuy nhiên, trong mô hình này, việc thiết lập liên kết giữa hai máy sẽ trở nên phức tạp hơn. Theo mô hình client/server, khi một socket gọi và thiết lập liên kết với chương trình ở máy remote xong thì máy remote cũng phải tạo ra một socket và tiến hành liên kết ngược lại. Sau khi cặp socket hoàn toàn liên kết xong thì hai máy mới coi như đã connect và tiến hành truyền nhận dữ liệu. Một khía cạnh khác cần lưu ý là tuy hai socket hoạt động độc lập với nhau nhưng chúng đều thuộc cùng một chương trình và chúng đều tiến hành việc gửi nhận dựa trên các giao thức lớp dưới chung. Do đó, trong một thời điểm chỉ có một hoạt động diễn ra hoặc là truyền dữ liệu hoặc là nhận dữ liệu. Vì vậy thật ra hai socket cũng phải hoạt động phụ thuộc nhau. Socket gửi dữ liệu phải chờ socket nhận nhận xong dữ liệu rồi mới bắt đầu truyền đi và ngược lại việc truyền dữ liệu phải được hoàn tất thì việc nhận dữ liệu mới có thể tiến hành được. Một vấn đề khác nảy sinh do đặc điểm của dữ liệu. Dữ liệu tiếp nhận là dạng dữ liệu liên tục do đó, các tín hiệu mà hệ thống báo cho hai socket cũng xảy ra liên tục, vì vậy thực sự rằng tuy chỉ làm một công việc nhưng khối lượng công việc mà socket phải đảm nhận là rất lớn. Thêm vào đó, hai socket đều phụ thuộc vào một process do đó thật sự xét về mặt thực thi của quá trình thì khả năng giảm nhẹ công việc là không bao nhiêu. Và khả năng hệ thống bị treo do quá tải cũng vẫn có thể xảy ra. Chúng ta có những cách giải quyết để giảm nhẹ việc thực thi của chương trình như dùng cơ chế xử lý song song (thread) hay dùng cơ chế phân chia thời gian cho các hoạt động như đã nói ở trên. 3.2.2 Cơ chế gọi và xác lập liên kết Khi liên kết được xác lập, chúng ta sẽ bắt đầu tiến hành trao đổi dữ liệu. Tuy nhiên trước hết chúng ta cần khảo sát phương pháp gọi cũng như thiết lập liên kết. Chương trình được hiện thực dựa trên cơ chế client/server cho nên việc tạo liên kết cũng dựa trên cơ chế này. Ý tưởng chính là: khi chương trình bắt đầu thực thi, nó cũng bắt đầu lắng nghe lời gọi liên kết ở một port xác định. Thực sự, trong chương trình chúng ta sẽ tạo ra một socket server và lắng nghe ở một port qui ước trước. Khi một socket khác muốn tạo liên kết, nó sẽ tiến hành gọi liên kết với socket server ở giá trị port này. Trong giao thức TCP/IP, một quá trình giao tiếp thông qua môi trường mạng phải có một chỉ số port xác định. Các quá trình khác nhau phải có port khác nhau. Khi thiết kế mô hình client/server, các nhà thiết kế đã tạo ra một số dịch vụ thông dụng trên mạng như: finger, echo, mail, ftp . . . Các server của các dịch vụ này được dành sẵn các port xác định mà không một quá trình nào được phép sử dụng. Các port này được gọi là well-known port và do hệ thống cấp phát và quản lý. Thông thường, các chỉ số well-known port có giá trị từ 0 đến 1023. Các ứng dụng không được phép sử dụng giá trị port trong khoảng này. Ứng dụng có thể dùng các giá trị port từ 1024 trở đi. Ví dụ: khi chúng ta cần tạo một socket mà không cần quan tâm đến giá trị port, chúng ta có thể nhờ hệ thống cấp cho một giá trị port còn trống. Thông thường các giá trị port mà hệ thông cung cấp cho ứng dụng khi có yêu cầu nằm trong khoảng từ 1024 đến 5000. Còn khi chúng ta muốn chỉ định một giá trị port cho socket, chúng ta sẽ có thể chọn giá trị từ 5000 trở đi. Vì trong vùng này xác suất mà port đó đã bị chiếm là rất hiếm. Vì vậy, khi thiết kế chúng ta muốn tạo một port cố định thì nên chọn socket lắng nghe ở một port có giá trị lớn hơn 5000. Giá trị được chọn là 7699 nhưng mô hình của chúng ta là : trong một chương trình vừa có đóng vai trò là client vừa là server nên ta chọn port có thể thay đổi được trong khoảng từ 1024 đến 5000. Khi muốn tạo liên kết, chúng ta sẽ tạo một socket và tiến hành connect vào socket đang lắng nghe ở một địa chỉ và port lắng nghe. Khi socket listen nhận thấy có yêu cầu liên kết, nó sẽ thông báo cho người sử dụng biết. Nếu nguời sử dụng đồng ý thì nó sẽ tiến hành connect và việc trao đồi dữ liệu bắt đầu. Nếu người sử dụng từ chối thì ứng dụng sẽ thông báo cho phía gọi lời từ chối và đóng liên kết lại. Chúng ta nói thêm về địa chỉ khi liên kết. Do chương trình hiện thực trên môi trường mạng Windows là môi trường mạng workgroup. Mỗi máy được xem như một host riêng lẻ. Nếu trên mạng không có các server như server novell hay server NT thì chúng ta không thể biết được các thông tin về một máy remote nếu chúng ta không tạo liên kết với máy đó. Vì vậy, trong cơ chế liên kết, chúng ta chọn việc định địa chỉ để liên kết. Một máy muốn thiết lập liên kết với một máy khác thì phải nhập thông số là địa chỉ IP của máy đó. 3.2.3 Cơ chế truyền nhận dữ liệu Khi viết một ứng dụng trên môi trường Windows, chúng ta phải lưu ý đến đặc điểm của môi trường Windows là môi trường có kiến trúc message-driven. Windows được xem là một môi trường có kiến trúc message-driven hay event-driven vì không một chương trình nào trên windows có thể thực thi nếu không có một Thông báo hay một sự kiện kích khởi nó. Trong môi trường Windows luôn tồn tại một vòng lặp message loop. Vòng message loop này sẽ truy xuất các Thông báo từ các hàng chờ của các chương trình và tùy theo loại Thông báo hay sự kiện, nó cho phép window procedure tương ứng thực thi. Vì vậy trên môi trường Windows có thể tồn tại nhiều ứng dụng cùng một lúc mỗi ứng dụng có một hàng chờ Thông báo riêng. Khi có một sự kiện xảy ra, hệ thống sẽ xác định xem sự kiện đó tuơng ứng với ứng dụng nào và chuyển Thông báo đến hàng chờ của ứng dụng tương ứng đó. Tùy theo loại Thông báo mà ứng dụng sẽ gọi chương trình tương ứng thực thi. Môi trường windows 16 bits là môi trường nonpreemptive, có nghĩa là khi một ứng dụng đang xử lý một Thông báo thì không một ứng dụng nào có thể thực thi được. Phải chờ cho đến khi procedure của ứng dụng tiến hành xong công việc và trả về thì lúc đó procedure tương ứng với Thông báo tiếp theo trong hàng chờ mới được thực thi. Trong khi đó các môi trường Win32 như Windows98, WindowsNT lại thực thi theo cơ chế preemptive. Trong môi trường này, việc procedure nào được thực thi là do hệ thống quyết định. Thật ra, trong môi trường Win32, hệ thống định thời cho các thread thực thi. Thread chính là đoạn mã thực thi của một chương trình nên các chương trình đều có cơ hội thực thi. Khi winsock được thiết kế lần đầu tiên, các mô hình thiết kế được làm cho phù hợp với cơ chế “ message-driven” và “ nonpreemptive” của Windows 16bits. Một số hàm socket nguyên thủy khi thực thi cần một khoảng thời gian tương đối. Khi hàm thực thi rơi vào tình trạng này, nó được gọi là bị tắc nghẽn. Khi một hàm bị tắc nghẽn, nó sẽ ngăn trở việc thực thi của các hàm khác trong hệ thống. Trong hệ thống UNIX, môi trường mà socket được thiết kế đầu tiên, các hàm blocking này không gây trở ngại cho hệ thống vì hệ thống sẽ chiếm giữ các quá trình bị blocking và cho phép các quá trình khác thực thi. Trong khi đó, hệ thống Windows 16 bits không có khả năng chiếm giữ các quá trình blocking. Dẫn đến việc hệ thống không tiếp tục thực thi được vì các quá trình khác không có cơ hội thực thi. Hệ thống phải chờ cho đến khi quá trình blocking hoàn tất công việc thì mới tiếp tục thực thi được. Khi thiết kế winsock, các nhà thiết kế đã tính đến khả năng này. Vì vậy họ có một giải pháp là đưa một đoạn mã đặc biệt vào hàm blocking để cho phép các quá trình khác kiểm tra được hàng chờ Thông báo của mình. Tuy nhiên đây không phải là một giải thuật hiệu quả. Trong hệ thống socket của Berkeley các nhà nghiên cứu cũng đã lưu ý đến vấn đề này khi thiết kế, và họ đã thiết kế các hàm nonblocking bên cạnh các hàm blocking. Chúng ta xét một ví dụ là hàm send() của socket. Khi hoạt động ở chế độ blocking, hàm send() sẽ gửi dữ liệu đi, hàm sẽ bị tắc nghẽn và nó chỉ trả về khi hoàn tất việc truyền dữ liệu, tức là dữ liệu đã được nhận hoàn toàn. Còn nếu socket được tạo ra ở cơ chế bất đồng bộ, hàm send() sẽ hoạt động ở chế độ non-blocking. Hàm send() sau khi gửi dữ liệu đi sẽ trả về ngay lập tức. Và hệ thống sẽ phải gọi một hàm khác như select() để quan sát tình trạng của việc gửi dữ liệu. Trên môi trường Windows chúng ta cũng có thể sử dụng các hàm non-blocking. Tuy nhiên các nhà thiết kế winsock còn đưa ra các hàm bất đồng bộ. Các hàm bất đồng bộ được đưa ra dựa trên cơ chế hoạt động message-driven của môi trường Windows. Chúng ta lấy ví dụ là các hàm gửi nhận dữ liệu. Việc gửi dữ liệu không nhất thiết phải diễn ra ngay lập tức, và việc nhận dữ liệu sẽ bắt buộc chương trình phải chờ trừ phi nó nhận được một hằng đặc biệt. Bằng cách tạo socket ở chế độ non-blocking để dùng các hàm non-blocking và kết hợp với hàm WSAAssyncSelect(), ứng dụng sẽ nhận được các message thông báo sự kiện để báo cho chương trình biết khi nào chương trình có thể gửi dữ liệu đi hoặc đã có dữ liệu truyền đến cần đọc ra từ socket. Trong các khoảng thời gian còn lại, khi không có thông báo các phần khác của hệ thống có thể thực thi được. Các hàm bất đồng bộ rất phù hợp cho các hoạt động diễn ra trên môi trường Windows 16 bits là môi trường nonpreemptive. Trong môi trường Win32 như Windows NT hay Windows98 là môi trường preemptive các hàm blocking vẫn có thể sử dụng được. Tuy nhiên việc dùng các hàm bất đồng bộ trên môi trường Win32 giúp chương trình đáp ứng tốt hơn cho việc tương tác với người sử dụng. Một hàm blocking sẽ ngăn trở hệ thống đáp ứng kịp thời cho các thao tác của người sử dụng. Điều này rất quan trọng trên một môi trường giao diện như Windows. Vì vậy các hàm bất đồng bộ vẫn được sử dụng. Vì môi trường Windows98 có hỗ trợ cơ chế lập trình song song thông qua việc định thời thực thi cho các thread, do đó trong việc thiết kế, chúng ta chọn dùng cơ chế blocking và thực hiện việc lập trình socket bằng các đối tượng do MFC cung cấp là các lớp CAsyncSocket, CSocket, CSocketFile, CArchive. Việc chọn lập trình bằng công cụ này vì có nhữnh đặc điểm sau: Các lớp đối tượng đều do MFC hỗ trợ, phù hợp với cấu trúc chương trình được xây dựng dựa trên các lớp đối tượng MFC. Ứng dụng được xây dựng trên các lớp đối tượng MFC bằng các công cụ AppWizard, ClassWizard. Việc viết ứng dụng sẽ dễ dàng và đơn giản hơn. Và khi ứng dụng có hỗ trợ socket thông qua các lớp đối tượng socket của MFC ở trên, việc lập trình sẽ trở nên tiên lợi hơn. Việc lập trình socket trên các lớp đối tượng thường dễ dàng và đơn giản hơn so với việc lập trình bằng các hàm socket nguyên thủy được hỗ trợ bởi Windows SDK. Chúng ta lấy một ví dụ như sau: tạo một socket và lắng nghe ở một port xác định. Lập trình bằng công cụ do Windows SDK hỗ trợ: // Tạo Socket SOCKET hSocket = socket ( int af = PF_INET, int type = SOCK_STREAM, int protocol = 0); // Ràng buộc socket vào một port cố định SOCKADDR_IN sin; u_short alport = IPPORT_RESERVED; sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; for (;;) { sin.sin_port = htons(alport); if (bind(hSocket, (LPSOCKADDR)&sin, sizeof (sin)) == 0){ /* it worked */ } if ( GetLastError != WSAEADDRINUSE) { /* fail */ } alport--; if (alport == IPPORT_RESERVED/2 ) { /* fail--all unassigned reserved ports are */ /* in use. */ } } // Lắng nghe lời gọi liên kết int listen ( hSocket, int backlog = 5 ); Trong khi đó nếu chúng ta lập trình bằng các lớp socket do MFC hỗ trợ thì các công việc phải thực thi như sau: // Tạo lớp đối tượng CSocket* m_pSocket = new CSocket; // Tạo socket và ràng buộc vào một port xác định m_pSocket->Create(nPort); // Lắng nghe lời gọi liên kết m_pSocket->Listen(); Khi lập trình chúng ta chọn các hàm blocking vì chúng được hỗ trợ bởi công cụ lập trình serialize. Cơ chế serialize cho phép hệ thống đảm bảo việc truyền nhận dữ liệu trên socket. Việc lập trình socket thông qua cơ chế serialize cũng đơn giản và dễ dàng hơn. Vì chương trình được hiện thực trên môi trường Windows98 cho nên chúng ta sẽ thực thi vào các hàm truyền nhận của socket. Vì vậy các hàm blocking cũng không ảnh hưởng nhiều đến việc thực thi của chương trình cũng như trong toàn hệ thống. Ngoài ra việc dùng cơ chế Serialize (Serialization là một quá trình đọc một đối tượng dữ liệu từ đĩa hay ngược lại, ghi chúng lên dĩa. MFC hỗ trợ cơ chế serialization trong class obj

Các file đính kèm theo tài liệu này:

  • docNguyenMinhTan_tom tat.doc
  • pptNguyenMinhTan.ppt