国产在线导航,欧美日本中文,黄色在线观看网站永久免费乱码,chinese国产在线视频,亚洲欧洲第一视频,天天做人人爱夜夜爽2020毛片,亚洲欧美中文字幕在线网站

現(xiàn)在位置:范文先生網(wǎng)>理工論文>計(jì)算機(jī)論文>C語言實(shí)的串行通信接口程序

C語言實(shí)的串行通信接口程序

時間:2023-02-20 22:50:18 計(jì)算機(jī)論文 我要投稿
  • 相關(guān)推薦

C語言實(shí)的串行通信接口程序

   摘 要 該文介紹了Sockets通信原理,從程序員角度著重討論了Windows Sockets為支持異步通信對Sockets的功能擴(kuò)充,并給出了應(yīng)用Windows Sockets實(shí)現(xiàn)網(wǎng)絡(luò)實(shí)時通信的一個程序?qū)嵗。關(guān)
IBM公司于1994年4月推出的TCP/IP for DOS V2.1.1所提供的開發(fā)軟件包Programmer's Tool Kit不僅帶有DOS下網(wǎng)絡(luò)編程接口,而且提供了Windows下網(wǎng)絡(luò)異步通信接口WINSOCK。
一、Socket網(wǎng)絡(luò)編程原理Socket是BSD UNIX提供的網(wǎng)絡(luò)應(yīng)用編程接口,它采用客戶機(jī)/服務(wù)器的通信機(jī)制,使網(wǎng)絡(luò)客戶機(jī)方和服務(wù)器方通過Socket實(shí)現(xiàn)網(wǎng)絡(luò)之間的連接和數(shù)據(jù)交換。Socket提供了一系列的系統(tǒng)調(diào)用,使用這些系統(tǒng)調(diào)用可以實(shí)現(xiàn)TCP、UDP、ICMP和IP等多種網(wǎng)絡(luò)協(xié)議之間的通信。
Socket有三種主要類型:stream sockets,datagram sockets和raw sockets。Streamsocket接口定義了一種可靠的面向連接的服務(wù),它實(shí)現(xiàn)了無差錯無重復(fù)的順序數(shù)據(jù)傳輸。它通過內(nèi)置的流量控制解決了數(shù)據(jù)的擁塞,應(yīng)用程序可以發(fā)送任意長度的數(shù)據(jù),將數(shù)據(jù)當(dāng)作字節(jié)流。Datagram socket接口定義了一種無連接的服務(wù),數(shù)據(jù)通過相互獨(dú)立的包進(jìn)行傳輸,包的傳輸是無序的,并且不保證是否出錯、丟失和重復(fù)。包長度是有限的(隱含長度為8192字節(jié),最大長度可設(shè)為32768字節(jié))。Raw socket接口允許對低層協(xié)議如IP和ICMP的直接存取,它主要用于新的網(wǎng)絡(luò)協(xié)議實(shí)現(xiàn)的測試等。
下面我們通過一個面向連接的傳輸發(fā)生的典型情況來說明socket網(wǎng)絡(luò)通信的實(shí)現(xiàn)。
由圖我們可以看出,客戶機(jī)和服務(wù)器的關(guān)系不是對稱的。服務(wù)器首先啟動,然后在某一時間啟動客戶機(jī)與服務(wù)器建立連接。服務(wù)器和客戶機(jī)開始都必須用調(diào)用socket ()建立一個套接字(socket),然后服務(wù)器調(diào)用bind()將套接字與一個本地網(wǎng)絡(luò)地址捆扎在一起,再用調(diào)用listen()使套接字處于一種被動的準(zhǔn)備接收狀態(tài),同時規(guī)定它的請求隊(duì)列長度,之后服務(wù)器就可以調(diào)用accept()來接收連接了?蛻魴C(jī)在建立套接字之后,便可以通過調(diào)用connect()和服務(wù)器建立連接。連接建立后,客戶機(jī)和服務(wù)器之間就可以通過連接發(fā)送和接收數(shù)據(jù)(調(diào)用read()和write())。最后,待數(shù)據(jù)傳送結(jié)束,雙方調(diào)用close()關(guān)閉套接字。
@@T8S10700.GIF;面向連接的協(xié)議實(shí)現(xiàn)的Socket調(diào)用圖@@
二、WINSOCK對Socket的擴(kuò)充
BSD Socket支持阻塞(blocking)和非阻塞(non-blocking)兩種工作方式。在阻塞方式下,connect()、accept()、read()和recv()等調(diào)用在執(zhí)行時都處于阻塞狀態(tài)直到它成功或出錯返回。在非阻塞方式下,這些調(diào)用是立即返回的,但是它們是否完成得靠查詢才能知道。對于Windows這種非搶先多任務(wù)操作系統(tǒng)來說,這兩種工作方式都是難以接受的,為此,WINSOCK在盡量與BSD Socket保持一致的前提下,又對它作了必要的擴(kuò)充。
WINSOCK對BSD Socket的擴(kuò)充主要是在基于消息、對網(wǎng)絡(luò)事件的異步存取接口上。表1列出了WINSOCK擴(kuò)充的函數(shù)功能。
從表1可以看出,WINSOCK的擴(kuò)充功能可以分為如下幾類。
(1)異步選擇機(jī)制
異步選擇函數(shù)WSAAsyncSelect()允許應(yīng)用程序提名一個或多個感興趣的網(wǎng)絡(luò)事件,所有非阻塞的網(wǎng)絡(luò)I/O例程(如send()和resv()),不管它是已經(jīng)使用還是即將使用,都可作為WSAAsyncSelect()函數(shù)選擇的候選。當(dāng)被提名的網(wǎng)絡(luò)事件發(fā)生時,Windows應(yīng)用程序的窗口函數(shù)將收到一個消息,消息附帶的參數(shù)指示被提名過的某一網(wǎng)絡(luò)事件。
@@T8S10701.GIF;表1 WINSOCK擴(kuò)充函數(shù)功能@@
(2)異步請求例程
異步請求例程允許應(yīng)用程序用異步方式獲取請求的信息,如WSAAsyncGetXByY()類函數(shù)允許用戶請求異步服務(wù),這些功能在使用標(biāo)準(zhǔn)Berkeley函數(shù)時是阻塞的。函數(shù)WSACancelAsyncRequest()允許用戶終止一個正在執(zhí)行的異步請求。
(3)阻塞處理方法
WINSOCK在調(diào)用處于阻塞時進(jìn)入一個叫“Hook”的例程,它負(fù)責(zé)處理Windows消息,使得Windows的消息循環(huán)能夠繼續(xù)。WINSOCK還提供了兩個函數(shù)(WSASetBlockingHook()和WSAUnhookBlockingHook())讓用戶能夠設(shè)置和取消自己的阻塞處理例程。另外,函數(shù)WSAIsBlocking()可以檢測調(diào)用是否阻塞,函數(shù)WSACancelBlockingCall()可以取消一個阻塞的調(diào)用。
(4)出錯處理
為了和以后的多線索環(huán)境(如Windows NT)兼容,WINSOCK提供了兩個出錯處理函數(shù)WSAGetLastError()和WSASetLastError()來獲取和設(shè)置本線索的最近錯誤號。
(5)啟動與終止
WINSOCK的應(yīng)用程序在使用上述WINSOCK函數(shù)前,必須先調(diào)用WSAStartup()函數(shù)對Windows Sockets DLL進(jìn)行初始化,以協(xié)商WINSOCK的版本支持,并分配必要的資源。在應(yīng)用程序退出之前,應(yīng)該先調(diào)用函數(shù)WSACleanup()終止對Windows Sockets DLL的使用,并釋放資源,以利下一次使用。
在這些函數(shù)中,實(shí)現(xiàn)Windows網(wǎng)絡(luò)實(shí)時通信的關(guān)鍵是異步選擇函數(shù)WSAAsyncSelect()的使用,其原型如下:int PASCAL FAR WSAAsyncSelect(SOCKET s,HWND hWnd, unsigned intwMsg, long lEvent);它請求Windows Sockets DLL在檢測到在套接字s上發(fā)生的lEvent事件時,向窗口hWnd發(fā)送一個消息wMsg。它自動地設(shè)置套接字s處于非阻塞工作方式。參數(shù)lEvent由表2所列事件的一個或多個組成。
@@T8S10702.GIF;表2 異步選擇網(wǎng)絡(luò)事件@@
例如,我們要在套接字s讀準(zhǔn)備好或?qū)憸?zhǔn)備好時接到通知,可以使用下面的語句:
rc=WSAAsyncSelect(s,hWnd,wMsg,FD-READ | FD-WRITE);
當(dāng)套接字s上被提名的一個網(wǎng)絡(luò)事件發(fā)生時,窗口hWnd將收到消息wMsg,變量lParam的低字指示網(wǎng)絡(luò)發(fā)生的事件,高字指示錯誤碼。應(yīng)用程序就可以通過這些信息來決定自己的下一步動作。
三、網(wǎng)絡(luò)實(shí)時通信的實(shí)現(xiàn)
我們來設(shè)計(jì)一個簡單的基于連接的點(diǎn)對點(diǎn)網(wǎng)絡(luò)實(shí)時通信程序。服務(wù)器首先啟動,它建立套接字之后等待客戶機(jī)的連接;客戶機(jī)在啟動后,建立套接字,然后和服務(wù)器建立連接;連接建立后,客戶機(jī)通過連接給服務(wù)器發(fā)送一段數(shù)據(jù),服務(wù)器接收后又將它發(fā)送回來,客戶機(jī)再發(fā)送,如此循環(huán),直至用戶命令客戶機(jī)退出或網(wǎng)絡(luò)出錯;客戶機(jī)關(guān)閉連接和套接字后退出,服務(wù)

器在檢測到連接關(guān)閉后,關(guān)閉套接字自動結(jié)束。
我們的實(shí)例是UNIX下基于BSD Socket的服務(wù)器程序和Windows下基于WINSOCK的客戶機(jī)程序之間的通信。服務(wù)器在主機(jī)UNIX下直接運(yùn)行,前臺和后臺均可;客戶機(jī)在Windows下運(yùn)行,帶一個參數(shù),即主機(jī)的名字。如win client rs6000,rs6000是在HOSTS文件中已定義好的主機(jī)名。
我們先看客戶機(jī)程序,首先定義幾個宏、菜單資源和部分全局變量。
程序1:部分Windows程序源代碼(宏、菜單和變量)
#define USERPORT 3333/*用戶定義端口號*/
#define IDM-START101/*“啟動”菜單項(xiàng)標(biāo)志*/
#define IDM-EXIT102/*“退出”菜單項(xiàng)標(biāo)志*/
#define UM-SOCKWM-USER+0x100/*用戶定義網(wǎng)絡(luò)消息*/
ClientMenu MENU/*客戶機(jī)菜單*/
BEGIN
POPUP "&Server"
BEGIN
MENUITEM "&Start...", IDM-START
MENUITEM "&Stop",IDM-STOP
END
END
#include <winsock.h>/*必須包含winsock.h頭文件*/
HANDLEhInst;
charserver-address = {0};/*服務(wù)器地址緩沖區(qū)*/
charbuffer;/*接收發(fā)送緩沖區(qū)*/
char FAR *lpBuffer=&buffer;
SOCKETs=0;/*套接字*/
struct sockaddr-in dst-addr;/*目標(biāo)地址*/
struct hostent *hostaddr;/*主機(jī)地址*/
struct hostent hostnm;
intcount=0;/*發(fā)送接收循環(huán)計(jì)數(shù)器*/
客戶機(jī)程序的窗口主函數(shù)很簡單,它在注冊窗口類、建立窗口后,只是給主窗口函數(shù)發(fā)送一個用戶消息,然后就進(jìn)入Windows消息處理循環(huán)。
程序2:部分Windows程序源代碼(窗口主函數(shù))
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lp
CmdL
ine, int nCmdShow)
{
HWND hWnd;
MSGmsg;
lstrcpy((LPSTR) server-address, lpCmdLine);/*取主機(jī)名字*/
if (!hPrevInstance)
if (!InitApplication(hInstance))
return (FALSE);
hInst=hInstance;
hWnd=CreateWindow("ClientClass","Windows ECHO Client",
WS-OVERLAPPEDWINDOW,CW-USEDEFAULT,CW-USEDEFAULT,
CW-USEDEFAULT,CW-USEDEFAULT,
NULL,NULL,hInstance,NULL);
if (!hWnd)
return (FALSE);
ShowWindow(hWnd,nCmdShow);
UpdateWindow(hWnd);
/*給主窗口函數(shù)發(fā)送WM-USER消息*/
PostMessage(hWnd,WM-USER,(WPARAM) 0,(LPARAM) 0);
while (GetMessage(&msg,NULL,NULL,NULL)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (msg.wParam);
}
主窗口函數(shù)ClientProc是程序的主要部分,它處理相關(guān)的消息:在接到消息WM-USER后,它調(diào)用函數(shù)WSAStartup()初始化Windows Sockets DLL,并檢查其版本號,然后通過主機(jī)名獲取主機(jī)地址;在接到消息WM-COMMAND時,如果是命令I(lǐng)DM-START,則調(diào)用子程序Client()建立套接字,并試圖和服務(wù)器建立連接,如果是命令I(lǐng)DM-STOP,則調(diào)用函數(shù)WSACleanup()終止Windows Sockets DLL,并發(fā)出終止應(yīng)用程序的消息;在接到消息UM-SOCK時,它根據(jù)參數(shù)lParam指示的網(wǎng)絡(luò)事件,進(jìn)行相應(yīng)的操作,然后選擇下一個期望的網(wǎng)絡(luò)事件。
程序3:部分Windows程序源代碼(主窗口函數(shù))
long FAR PASCAL
ClientProc(HWND hWnd, unsigned message, UINT wParam, LONG lParam)
{
int length,i;
WSADATAwsaData;/*描述Windows Sockets實(shí)現(xiàn)細(xì)節(jié)的數(shù)據(jù)結(jié)構(gòu)*/
intStatus;
switch (message) {
case WM-USER:
Status=WSAStartup(0x101,&wsaData);
if (Status !=0) {
AlertUser(hWnd,"WSAStartup() failed");
PostQuitMessage(0);
}
if (LOBYTE(wsaData.wVersion) !=1 || HIBYTE(wsaData.wVersion) !=1)
{ /*現(xiàn)在支持的版本是WINSOCK.DLL 1.1*/
AlertUser(hWnd, "WSAStartup() Version not match");
WSACleanup();
PostQuitMessage(0);
}
hostaddr=gethostbyname(server-address);
if (hostaddr==NULL) {
AlertUser(hWnd, "gethostbyname ERROR ");
WSACleanup ();
PostQuitMessage(0);
}
memcpy(&hostnm,hostaddr,sizeof(struct hostent));
break;
case WM-COMMAND:
switch (wParam) {
case IDM-START:
if (!Client(hWnd)) {
closesocket(s);
AlertUser(hWnd, "Start Failed");
}
break;
case IDM-

STOP:
WSACleanup();
PostQuitMessage(0);
break;
}
break;
case UM-SOCK:
switch (lParam) {
case FD-CONNECT:/*網(wǎng)絡(luò)事件:連接建立*/
if (!set-select(hWnd, FD-WRITE))/*選擇:期望發(fā)送*/
closesocket(s);
break;
case FD-READ:/*網(wǎng)絡(luò)事件:讀準(zhǔn)備好*/
if (!receive-pkt(hWnd)) {/*接收數(shù)據(jù)*/
AlertUser(hWnd, "Receive Packet Failed");
closesocket(s);
break;
}
if (!set-select(hWnd, FD-WRITE))/*選擇:期望發(fā)送*/
closesocket(s);
break;
case FD-WRITE:/*網(wǎng)絡(luò)事件:寫準(zhǔn)備好*/
for (i=0;i<1024;i++)
buffer=(char) 'A'+i % 26;
length=1024;
if (!(send-pkt(hWnd,length))) {/*發(fā)送數(shù)據(jù)*/
AlertUser(hWnd, "Packet Send Failed");
closesocket(s);
break;
}
if (!set-select(hWnd, FD-READ)) /*選擇:期望接收*/
closesocket(s);
break;
case FD-CLOSE:/*網(wǎng)絡(luò)事件:連接關(guān)閉。操作:停止異步選擇*/
if (WSAAsyncSelect(s,hWnd,0,0)==SOCKET-ERROR)
AlertUser(hWnd, "WSAAsyncSelect Failed");
AlertUser(hWnd, "Socket has been closed");
break;
default:/*網(wǎng)絡(luò)出錯則警告,其他事件忽略*/
if (WSAGETSELECTERROR(1Param) !=0) {
AlertUser(hWnd, "Socket Report Failure");
closesocket(s);
break;
}
break;
}
break;
case WM-DESTROY:
closesocket(s);/*關(guān)閉窗口前應(yīng)該關(guān)閉套接字,并*/
WSACleanup();/*終止Windows Sockets DLL*/
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, message,
wParam, lParam));
}
return (NULL);
}
程序4:部分Windows程序源代碼(子程序)
BOOL Client(HWND hWnd)/*客戶機(jī)子程序*/
{
if (!make-skt(hWnd))/*建立套接字*/
return(FALSE);
if (!set-select(hWnd,FD-CONNECT))/*設(shè)置異步連接*/
return(FALSE);
if (!connect-skt(hWnd))/*建立連接*/
return(FALSE);
return(TRUE);
}
BOOL receive-pkt(HWND hWnd)/*接收數(shù)據(jù)子程序*/
{
HDC dc;
intlength;
int11,12,13;
charlinel,line2,line3;
count ++;/*循環(huán)計(jì)數(shù)器加1*/
if ((length=recv(s,lpBuffer,1024,0))==SOCKET-ERROR)
return(FALSE); /*如果接收數(shù)據(jù)出錯,則返回FALSE*/
if (length==0) /*接收數(shù)據(jù)長度為零,表示連接中斷*/
return(FALSE);
if (dc=GetDC(hWnd)) { /*接收數(shù)據(jù)成功,顯示信息*/
11=wsprintf((LPSTR) line1,"TCP Echo Client No.%d",count);
12=wsprintf((LPSTR) line2,"Received %d bytes", length);
13=wsprintf((LPSTR) line3,"Those are:%c,%c,%c,%c,%c,%c",
buffer,buffer,buffer,buffer,buffer,buffer
);
TextOut(dc, 10, 2, (LPSTR) linel, 11);
TextOut(dc, 10, 22, (LPSTR) line2, 12);
TextOut(dc, 10, 42, (LPSTR) line3, 13);
ReleaseDC(hWnd, dc);
}
return(TRUE);
}
BOOL set-select(HWND hWnd, long lEvent)/*異步選擇子程序*/
{
if (WSAAsyncSelect(s,hWnd, UM-SOCK, lEvent)==SOCKET-ERROR) {
AlertUser(hWnd, "WSAAsyncSelect Failed");
return (FALSE);
}
return (TRUE);
}
BOOL make-skt(HWND hWnd)/*建立套接字子程序*/
{
if ((s=socket(AF-INET,sock-type,0))==INVALID-SOCKET) {
AlertUser(hWnd, "Socket Failed");
return (FALSE);
}
return (TRUE);
}
BOOL connect-skt(HWND hWnd)/*建立連接子程序*/
{
memset((void*) &dst-addr, sizeof(dst-addr),0);
dst-addr.sin-family=AF-INET;
dst-addr.sin-port=htons(USERPORT);
dst-addr.sin-addr.s-addr=*((unsigned long *)hostnm.h-addr-list );
if (connect(s, (stru

ct sockaddr *) & dst-addr,
sizeof(dst-addr))==SOCKET-ERROR) {
AlertUser(hWnd, "Connect Failed");
return (FALSE);
}
return (TRUE);
}
BOOL send-pkt(HWND hWnd, int len)/*發(fā)送數(shù)據(jù)子程序*/
{
int length;
if ((length=send(s,lpBuffer,len,0))==SOCKET-ERROR)
return (FALSE);
else if (length !=len) {
AlertUser(hWnd, "Send Length NOT Match!");
return(FALSE);
}
return (TRUE);
}
我們用最簡單的語句編制一個UNIX下基于BSD SOCKET的服務(wù)器程序,它在建立連接后,只負(fù)責(zé)將收到的數(shù)據(jù)發(fā)回去,在連接斷開后,服務(wù)器關(guān)閉套接字返回。要編制在Windows下的服務(wù)器程序,可參照客戶機(jī)程序,使用WINSOCK的異步選擇機(jī)制。
程序5:UNIX下服務(wù)器程序源代碼
/*TCP/IP必要的頭文件*/
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define USERPORT 3333/*用戶定義端口號,與客戶機(jī)相同*/
#define HOST-IP-ADDR "166.111.8.80"/*我們的主機(jī)地址*/
main(int argc, char **argv)
{
char buf;/*buffer for sending and receiving data*/
struct sockaddr-in client;/*client address information*/
struct sockaddr-in server;/*server address information*/
int s;/*socket for accepting connections*/
int ns;/*socket connected to client*/
int namelen;/*length of client name*/
int pktlen;/*length of packet received or sended*/
if ((s = socket(AF-INET,SOCK-STREAM, 0))<0) {
perror("Socket()");
return;
}
/*Bind the socket to the server address.*/
bzero((char*)&server, sizeof(server));
server.sin-len =sizeof(struct sockaddr-in);
server.sin-family=AF-INET;
server.sin-port =htons(USERPORT);
server.sin-addr.s-addr=INADDR-ANY;
if (bind(s, (struct sockaddr *)&server, sizeof(server)) <0) {
perror ("Bind()");
return;
}
/*Listen for connections. Specify the backlog as 1. */
if (listen(s,1)!=0) {
perror("Listen()");
return;
}
/*Accept a connection.*/
namelen=sizeof(client);
if ((ns = accept(s, (struct sockaddr *)&client,&namelen))==-1) {
perror("Accept()");
return;
}
/*Receive the message on the newly connected socket.*/
for (;;){
if ((pktlen = recv(ns, buf, 1024, 0))<0) {
perror("Recv()");
break;
}
else if (pktlen==0) {
printf("Recv():return FAILED,connection is shut down! ");
break;
}
else printf("Recv():return SUCCESS, packet length = %d ",pktlen);
sleep(1); /*Sleep() 1秒鐘是為了減慢數(shù)據(jù)交換速度*/
/*Send the message back to the client.*/
if (send(ns, buf, pktlen, 0) <0) {
perror("Send ()");
break;
}
else printf("Send():return SUCCESS, packet length = %d ",pktlen);
}
close(ns);
close(s);
printf("Server ended successfully");
}
四、結(jié)束語
本文試圖通過一個實(shí)例來說明如何使用WINSOCK實(shí)現(xiàn)Windows下網(wǎng)絡(luò)實(shí)時通信。從上面的討論可以看出,使用WINSOCK編制Windows下網(wǎng)絡(luò)軟件是比較方便的,WINSOCK提供的異步選擇機(jī)制使Socket強(qiáng)大的網(wǎng)絡(luò)編程功能能夠在Windows下得到應(yīng)用。相信隨著INTERNET的推廣,TCP/IP網(wǎng)絡(luò)協(xié)議的廣泛使用,使用WINSOCK編制Windows網(wǎng)絡(luò)實(shí)時通信軟件將會有一個大的發(fā)展。

參考文獻(xiàn)
1 Martin Hall等.Windows Sockets-An Open Interface for Network Programm ing under Microsoft Windows.WINSOCK Document, 1993(6).2 孫義等.UNIX環(huán)境下的網(wǎng)絡(luò)程序設(shè)計(jì).北京:希望公司,1991.3 梁振軍等.新編TCP/IP協(xié)議與計(jì)算機(jī)網(wǎng)絡(luò)互聯(lián)技術(shù).北京:希望公司,1992.


【C語言實(shí)的串行通信接口程序】相關(guān)文章:

C語言實(shí)現(xiàn)串行通信接口程序08-06

異步串行通信接口的IP核設(shè)計(jì)08-06

ADμC812的串行外設(shè)接口(SPI)及其應(yīng)用08-06

一種用于單片機(jī)的紅外串行通信接口08-06

一種多協(xié)議串行通信接口的設(shè)計(jì)方法08-06

串行通信控制器85C30及其應(yīng)用08-06

基于Z85C30的多協(xié)議串行通信設(shè)計(jì)08-06

c語言實(shí)習(xí)心得09-03

AT93C46/56/55串行EEPROM及單片機(jī)程序08-06