1. 程式人生 > >基於TCP。UDP的socket程式設計

基於TCP。UDP的socket程式設計

UDP(User Data Protocol,使用者資料報協議)是與TCP相對應的協議。它是面向非連線的協議,它不與對方建立連線,而是直接就把資料包傳送過去!與現在風行的手機簡訊非常相似:你在發簡訊的時候,只需要輸入對方手機號就OK了。UDP適用於一次只傳送少量資料、對可靠性要求不高的應用環境。是tcp/ip參考模型中一種無連線的傳輸層協議,它主要用於不要求分組順序到達的傳輸應用中,分組傳輸順序的檢查與排序由應用層完成,提供面向物件事物的簡單不可靠資訊傳送服務,udp協議基本上是ip協議與上層協議的介面,udp協議適用埠分別是執行在同一臺裝置上的多個應用程式

udp不屬於連線型協議,因而具有資源消耗小,處理速度快的優點,所以通常語音,視訊和普通資料在傳送時使用udp較多,因為偶爾丟失一兩個資料也不會對接收結果產生太大的影響,比如聊天和視訊通話通常就是使用udp

udp:不可靠,無連線地資料報協議

在OSI的各層所使用的協議:
1.應用層:telnet,FTP,HTTP,DNS,SMTP,POP3
2.傳輸層:TCP,UDP
 TCP:面向連線的、可靠的、基於位元組流的傳輸層協議,通訊前建立三次握手,握手成功後才能通訊,對資料準確性要求較高的場合使用,如從網上載的安裝檔案,不能缺少任何資訊
UDP:是無連線的,不可靠的資料報協議,不需要建立連線,也沒有重傳和確認的機制,在實時性要求較高,但對資料準確度要求不是很高的場合使用,如視訊會議,線上觀看電影,當中丟失個別資料包並不影響整體的效果。
3.網路層:IP
因為OSI七層結構較為複雜,所以使用較多的是TCP/IP模型,現在TCP/IP已經成為Internet上通用的工業標準
TCP/IP模型包括4個層次:應用層,傳輸層,網路層,網路介面
埠:
1. 為了標識通訊實體中進行通訊的程序(應用程式),TCP/IP協議提出了協議埠的概念
2. 埠是一種抽象的軟體結構(包括一些資料結構和I/O緩衝區)。應用程式通過系統呼叫和某埠建立連線(binding)後,傳輸層傳給該埠的資料都被相應的程序所接收,相應程序發給傳輸層的資料都通過該埠輸出
3. 埠用一個整數型識別符號來表示,即埠號。埠號跟協議相關,TCP/IP傳輸層的兩個協議TCP和UDP是完全獨立的的兩個軟體模組,因此各自的埠號也相互獨立
4. 埠使用一個16位的數字來表示,它的範圍是0~65535,1024以下的埠號保留給預定義的服務,例如,http使用80埠
套接字(Socket)
1. Socket的出現,使得程式設計師可以很方便的訪問TCP/IP,從而開發各種網路應用的程式
2. 套接字存在於通訊區域中,通訊區域也叫地址族,他是一個抽象的概念,主要用於通過套接字通訊的程序的共有特性綜合在一起。套接字通常只與同一個區域的套接字交換資料。
套接字的型別
1. 流式套接字(SOCK_STREAM)
提供面向連線的,可靠的資料傳輸服務,資料無差錯,無重複的傳送,且按傳送的順序接收,基於TCP協議
2. 資料保式套接字(SOCK_DGRAM)
提供無連線的服務,資料包以獨立包形式傳送,不提供無錯誤的保證,資料可能丟失或重複,且接收順序混亂,基於UDP協議
3. 原始套接字(SOCK_RAW)
基於TCP(面向連線)的Socket程式設計
伺服器端順序:
1. 載入套接字型檔WSAStartup( ,)
2. 建立套接字(socket)
3. 將套接字繫結到一個本地地址和埠上(bind)
4. 將套接字設為監聽模式,準備接收客戶請求(listen)
5. 等待客戶請求的到來;當請求帶來後,接受連線請求,返回一個新的對應於此次連線的套接字(accept)
6. 用返回的套接字和客戶端進行通訊(send/recv)
7. 返回,等待另一個客戶請求
8. 關閉套接字(closesocket)
客戶端程式:
1. 載入套接字型檔
2. 建立套接字(socket)
3. 向伺服器傳送連線請求(connect)
4. 和伺服器端進行通訊(send/receive)
5. 關閉套接字(closesocket)
基於UDP(面向無連線)的socket程式設計
伺服器端(接收端)程式:
1. 載入套接字型檔
2. 建立套接字(socket)
3. 將套接字繫結到一個本地地址和埠上(bind)
4. 等待接收資料(recvfrom)
5. 關閉套接字(closesocket)
客戶端(傳送端)程式
1. 載入套接字型檔
2. 建立套接字(socket)
3. 向伺服器傳送資料(sendto)
4. 關閉套接字(closesocket)
建立基於TCP協議的CS程式的Server端所涉及的相關函式說明(按使用的先後順序排列):
1. int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData):作用是載入套接字型檔和進行套接字型檔的版本協商
a. 引數wVersionRequested:用於指定準備載入的Winsock庫的版本,高位位元組指定所需要的Winsock庫的副版本,低位位元組則是主版本,可用MAKEWORD(X,Y)(其中,x為高位位元組,y為低位位元組)方便獲得wVersionRequested的正確值。
b. 引數lpWSAData:指向WSADATA結構的指標,WSAStartup用其載入的庫版本有關的資訊填在這個結構中
2. SOCKET socket(int af,int type,int protocol):
a. 引數af指定地址族,對於TCP/IP協議的套接字,它只能是AF_INET(也可寫成PF_INET)。
b. 引數type指定Socket型別,對於1.1版本的Socket,它只支援兩種型別的套接字,SOCK_STREAM指定產生流式套接字,SOCK_DGRAM產生資料報套接字。
c. 引數protocol與特定的地址家族相關的協議,如果指定為0,那麼他就會根據地址格式和套接字類別,自動為你選擇一個合適的協議。這是推薦使用的一種選擇協議的方式。
3. int bind(SOCKET s,const struct sockaddr FAR *name,int namelen) :
a. 第一個引數指定要繫結的套接字,第二個引數指定該套接字的本地地址資訊,是指向sockaddr結構的指標變數,由於該地址結構是為了所有的地址家族準備使用的,這個結構可能(通常會)隨使用的網路協議不同而不同,所以,要用第三個引數指定該地址結構的長度。sockaddr機構定義如下:
struct sockaddr
{
u_short sa_family;
char sa_data[14];
}
b. 上述結構第一個欄位指定該地址家族,在這裡必須設為AF_INET。sa_data僅僅是表示要求一塊記憶體分配區,起到佔位的作用,該區域中指定與協議相關的具體地址資訊。由於實際要求的只是記憶體區,所以對於不同的協議家族,用不同的結構來替換sockaddr。在TCP/IP中,我們可以用SOCKADDR_IN結構來代替sockaddr,以方便我們填寫地址資訊。
c. struct SOCKADDR_IN{
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
sin_family表示地址族,對於IP地址,sin_family成員將一直是AF_INET;成員sin_port指定的將要分配給套接字的埠;成員sin_addr給出的是套接字的主機IP地址;sin_zero只是一個填充數,以使sockaddr_in結構和sockaddr結構的長度一樣,一般不用設定。
除了sin_family外,SOCKADDR_IN其他成員是按網路位元組順序表示的。所以需要進行轉換:htonl(INADDR_ANY),htons(6000),其中6000是埠號。
另外結構體的名稱大寫和小寫指的是同一個。
將IP地址指定為INADDR_ANY,允許套接字向任何分配給本機器的IP地址傳送或接收資料。一般一臺機器一個網絡卡,但對於多網絡卡的機器,INADDR_ANY將簡化應用程式的編寫。將地址指定為INADDR_ANY,允許一個獨立的應用接受發自多個介面的迴應。如果我們只想讓150
套接字使用多個IP中的一個地址,必須指定實際地址,要做到這一點,可以用inet_addr()函式,這個函式需要一個IP地址(如192.168.80.88),返回一個適合分配給S_addr的u_long型別的數值。Inet_ntoa()函式完成相反的轉換,它接受一個in_addr結構體型別的引數並返回一個以點分十進位制的IP地址字串。
htonl把一個u_long型別從主機位元組序轉換為網路位元組序。
htons把一個u_short型別從主機位元組序轉換為網路位元組序。
struct in_addr {
union {
struct {
u_char s_b1,s_b2,s_b3,s_b4;
} S_un_b; //An IPv4 address formatted as four u_chars.
struct {
u_short s_w1,s_w2;
} S_un_w; //An IPv4 address formatted as two u_shorts
u_long S_addr; //An IPv4 address formatted as a u_long
} S_un;
};
4. int listen(SOCKET s, int backlog):將套接字設定為監聽模式,其中第二個引數設定等待請求連線的最大的值,即如果設定為n,則前n個請求會放置在系統的請求連線佇列中,應用程式會依次對這些請求進行服務,但第n+1個連線請求則會被拒絕。
5. SOCKET accept(SOCKET s, const struct sockaddr FAR * addr, int FAR* addrlen):
從客戶端接收請求,並建立連線,如果連線成功,則會返回一個當前成功建立連線的套接字,該套接字不是上面建立的監聽套接字,而是僅僅適用於當前的一個請求連線,如果建立連線失敗,則返回值是INVALID_SOCKET,並且可以適用WSAGetLastError()函式得到相關的失敗資訊,具體的error code具體意義見MSDN中accept函式的最後部分的介紹
6. send 函式:向客戶端傳送指定資訊
7. recv函式:得到從客戶端傳遞過來的資訊
8. closesocket(SOCKET s):將指定的套接字關閉,從而釋放資源
9. WSACleanup():終止對winsocket庫的使用
10. hello
The send function sends data on a connected socket.
int send(
SOCKET s,
const char FAR *buf,
int len,
int flags
);
Parameters
s
[in] Descriptor identifying a connected socket.
buf
[in] Buffer containing the data to be transmitted.
len
[in] Length of the data in buf.
flags
[in] Indicator specifying the way in which the call is made.

The recv function receives data from a connected or bound socket.
int recv(
SOCKET s,
char FAR *buf,
int len,
int flags
);
Parameters
s
[in] Descriptor identifying a connected socket.
buf
[out] Buffer for the incoming data.
len
[in] Length of buf.
flags
[in] Flag specifying the way in which the call is made.

伺服器端的實現過程(Win32控制檯程式):
說明:
1. 對於winsock庫的類的使用,必須包含winsock2.h標頭檔案
2. 在setting中的link下的object/library modules中新增“ws2_32.lib”,注意和前面的欄位之間用空格分隔
3. 本伺服器程式先於伺服器端啟動

#include "winsock2.h"
#include "stdio.h"
void main()
{
      //載入套接字(winsock)庫,載入這段程式碼拷貝於MSDN中WSAStartup的介紹
      WORD wVersionRequested;
      WSADATA wsaData;
      int err;

      wVersionRequested = MAKEWORD( 1, 1 ); //版本號為1.1

      err = WSAStartup( wVersionRequested, &wsaData );
      if ( err != 0 ) {                              
           return;
      }                                    

      if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1 ) {
           WSACleanup( );
           return;
      }
      //建立套接字
      SOCKET sockServer=socket(AF_INET,SOCK_STREAM,0); //SOCK_STREAM引數設定為TCP連線
      SOCKADDR_IN addrServer; //設定伺服器端套接字的相關屬性
      addrServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY); //設定IP
      addrServer.sin_family=AF_INET;
      addrServer.sin_port=htons(1234); //設定埠號
      //將套接字繫結到本地地址和指定埠上
      bind(sockServer,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));

      //將套接字設定為監聽模式,並將最大請求連線數設定成5,超過此數的請求全部作廢
      listen(sockServer,5);
      SOCKADDR_IN addrClient; //用來接收客戶端的設定,包括IP和埠
      int len=sizeof(SOCKADDR);
      while(1) //不斷監聽
      {
           //得到建立連線後的一個新的套接字,用來和客戶端進行溝通,原套接字繼續監聽客戶的連線請求
           SOCKET sockConn=accept(sockServer,(SOCKADDR*)&addrClient,&len);
           if(sockConn!=INVALID_SOCKET) //建立成功
           {
                 char sendInfo[100];
                 //inet_ntoa將結構轉換為十進位制的IP地址字串
sprintf(sendInfo,"welcome %s to this test!",inet_ntoa(addClient.sin_addr));
                 //成功建立連線後向客戶端傳送資料,結果將顯示在客戶端上
                 send(sockConn,sendInfo,strlen(sendInfo)+1,0);

                 //從客戶端接收資料,結果顯示在伺服器上
                 char recvInfo[100];
                 recv(sockConn,recvInfo,100,0);
                 printf("%s\n",recvInfo);

                 //將本次建立連線中得到套接字關閉
                 closesocket(sockConn);
           }
           else
           {
                 int errCode=WSAGetLastError();
                 printf("the errcode is:%d\n",errCode);
           }

      }
      //如果本程式不是死迴圈,那麼在此處還應新增以下程式碼:
      closesocket(sockServer);  //對一直處於監聽狀態的套接字進行關閉
      WSACleanup(); //終止對winsocket庫的使用

}

建立基於TCP協議的CS程式的Client端所涉及的相關函式說明(按使用的先後順序排列):
1. int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData):說明同上
2. SOCKET socket(int af,int type,int protocol):說明同上
3. connect函式:同伺服器建立連線
4. send/recv:傳送與接收,同上
5. closesocket:關閉套接字,同上
客戶端的實現過程(win32 console程式):
說明:
1. 對於winsock庫的類的使用,必須包含winsock2.h標頭檔案
2. 在setting中的link下的object/library modules中新增“ws2_32.lib”,注意和前面的欄位之間用空格分隔
3. 啟動客戶端程式之前必須先啟動伺服器端的程式

#include "winsock2.h"
#include "stdio.h"
void main()
{
      //載入套接字型檔
      WORD wVersionRequested;
      WSADATA wsaData;
      int err;

      wVersionRequested = MAKEWORD( 1, 1 ); //版本好為1.1

      err = WSAStartup( wVersionRequested, &wsaData );
      if ( err != 0 ) {                              
           return;
      }                                    

      if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1 ) {
           WSACleanup( );
           return;
      }
      SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0); //SOCK_STREAM引數設定為TCP連線
      SOCKADDR_IN addrServer; //伺服器地址結構
      addrServer.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); //伺服器地址
      addrServer.sin_port=htons(1234); //伺服器埠號
      addrServer.sin_family=AF_INET;
      //與伺服器端建立連線,進行通訊
      int connReult=connect(sockClient,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
      if(connReult!=WSAEADDRNOTAVAIL) //訪問成功
      {
           //成功建立連線後向伺服器端傳送資料,結果將顯示在伺服器端上
           send(sockClient,"this is lisi!",strlen("this is zhangsan!")+1,0);
           //接收來自伺服器端傳送來的資訊
           char recvInfo[100];
           recv(sockClient,recvInfo,100,0);
           printf("%s\n",recvInfo);
      }
      else
      {
           int errCode=WSAGetLastError();
           printf("the errcode is:%d\n",errCode);
      }
      closesocket(sockClient);
      WSACleanup();
}

建立基於UPD的CS程式伺服器端原始碼:
說明:伺服器端的程式碼在XP下執行recvfrom函式執行不成功,返回一個10022的錯誤程式碼,參看網上資源,得知10022代表某個引數設定錯誤。
花了大半天的時間研究後終於發現原來是6000埠在作怪,可能本機的UDP的6000埠在防火牆中被遮蔽了,通不過,但在TCP的CS程式中6000埠是通過的,程式除錯一切正常,將埠修改為其他埠即可
The recvfrom function receives a datagram and stores the source address.
int recvfrom(
SOCKET s,
char FAR* buf,
int len,
int flags,
struct sockaddr FAR *from,
int FAR *fromlen
);
Parameters
s
[in] Descriptor identifying a bound socket.
buf
[out] Buffer for the incoming data.
len
[in] Length of buf.
flags
[in] Indicator specifying the way in which the call is made.
from
[out] Optional pointer to a buffer that will hold the source address upon return.
fromlen
[in, out] Optional pointer to the size of the from buffer

The sendto function sends data to a specific destination.
int sendto(
SOCKET s,
const char FAR *buf,
int len,
int flags,
const struct sockaddr FAR *to,
int tolen
);
Parameters
s
[in] Descriptor identifying a (possibly connected) socket.
buf
[in] Buffer containing the data to be transmitted.
len
[in] Length of the data in buf.
flags
[in] Indicator specifying the way in which the call is made.
to
[in] Optional pointer to the address of the target socket.
tolen
[in] Size of the address in to.

#include "winsock2.h"
#include "stdio.h"
void main()
{
      //載入套接字(winsock)庫,載入這段程式碼拷貝於MSDN中WSAStartup的介紹
      WORD wVersionRequested;
      WSADATA wsaData;
      int err;

      wVersionRequested = MAKEWORD( 1, 1 ); //版本號為1.1

      err = WSAStartup( wVersionRequested, &wsaData );
      if ( err != 0 ) {                              
           return;
      }                                    

      if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1 ) {
           WSACleanup( );
           return;
      }
      //建立套接字
      //注意第二個引數和TCP設定不同
      SOCKET sockServer=socket(AF_INET,SOCK_DGRAM,0);

      SOCKADDR_IN addrServer; //設定伺服器端套接字的地址結構的相關屬性
      addrServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY); //設定IP
      addrServer.sin_family=AF_INET;
      addrServer.sin_port=htons(6000); //設定埠號
      //將套接字和伺服器地址結構繫結
      bind(sockServer,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
      SOCKADDR_IN addrClient;
      int len=sizeof(SOCKADDR);
      char buffer[100];
      //從客戶端接收資料
      int result=recvfrom(sockServer,buffer,100,0,(SOCKADDR*)&addrClient,&len);
      if(result==SOCKET_ERROR)
      {
           int errCode=WSAGetLastError();
           printf("error:%d",errCode);
      }
      else
      {
           printf("this message from client:%s\n",buffer);
      }
      closesocket(sockServer);
      WSACleanup(); 
}

建立基於UDP的CS程式客戶端的程式碼:

#include "winsock2.h"
#include "stdio.h"
void main()
{
      //載入套接字(winsock)庫,載入這段程式碼拷貝於MSDN中WSAStartup的介紹
      WORD wVersionRequested;
      WSADATA wsaData;
      int err;

      wVersionRequested = MAKEWORD( 1, 1 ); //版本號為1.1

      err = WSAStartup( wVersionRequested, &wsaData );
      if ( err != 0 ) {                              
           return;
      }                                    

      if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1 ) {
           WSACleanup( );
           return;
      }

      //建立套接字
      SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0); //注意第二個引數和TCP設定不同
      SOCKADDR_IN addrServer; //伺服器地址結構
      addrServer.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); //伺服器地址
      addrServer.sin_family=AF_INET;
      addrServer.sin_port=htons(6000); //伺服器埠號

sendto(sockClient,"this message from client",strlen("this message from client")+1,0,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
      closesocket(sockClient);
      WSACleanup();
}

基於UDP的聊天程式伺服器端源程式:
說明:
1. 程式碼基本和上述UDP伺服器端的程式相類似,只是多了sendto功能,即互動的功能
2. 支援退出請求響應

#include "winsock2.h"
#include "stdio.h"
void main()
{
      //載入套接字(winsock)庫,載入這段程式碼拷貝於MSDN中WSAStartup的介紹
      WORD wVersionRequested;
      WSADATA wsaData;
      int err;

      wVersionRequested = MAKEWORD( 1, 1 ); //版本號為1.1

      err = WSAStartup( wVersionRequested, &wsaData );
      if ( err != 0 ) {                               
           return;
      }                                    

      if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1 ) {
           WSACleanup( );
           return;
      }

      //建立套接字
      //注意第二個引數和TCP設定不同
      SOCKET sockServer=socket(AF_INET,SOCK_DGRAM,0);

      SOCKADDR_IN addrServer; //設定伺服器端套接字的地址結構的相關屬性
      addrServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY); //設定IP
      addrServer.sin_family=AF_INET;
      addrServer.sin_port=htons(6000); //設定埠號

      //將套接字和伺服器地址結構繫結
      bind(sockServer,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));

      SOCKADDR_IN addrClient;
      int len=sizeof(SOCKADDR);
      char recvBuf[100];
      char tempBuf[100];
      char sendBuf[100];
      while(1)
      {
           //從客戶端接收資料
           int result=recvfrom(sockServer,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);
           if(result==SOCKET_ERROR)
           {
                 int errCode=WSAGetLastError();
                 printf("error:%d",errCode);
                 break; //退出迴圈
           }
           else
           {
                 if(recvBuf[0]=='q') //對方請求退出
                 {
                      //同樣將退出請求傳送回去
                        sendto(sockServer,"q",strlen("q")+1,0,(SOCKADDR*)& addrClient,sizeof(SOCKADDR));
                      printf("the chat end!");
                      break;
                 }
                 else
                 {
                      //將對方傳送過來的資訊前段加上IP地址進行輸出
                      sprintf(tempBuf,"%s said:%s",inet_ntoa(addrClient.sin_addr),recvBuf);
                      printf("%s\n",tempBuf);

                      //將使用者的鍵盤輸入傳送到對方
                      printf("pleas input the message:\n");
                       gets(sendBuf); //得到鍵盤輸入內容 sendto(sockServer,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)& addrClient,sizeof(SOCKADDR));
                 }
           }
      }
      closesocket(sockServer);
      WSACleanup(); 
}

基於UDP的聊天程式客戶端端源程式:

#include "winsock2.h"
#include "stdio.h"
void main()
{
      //載入套接字(winsock)庫,載入這段程式碼拷貝於MSDN中WSAStartup的介紹
      WORD wVersionRequested;
      WSADATA wsaData;
      int err;

      wVersionRequested = MAKEWORD( 1, 1 ); //版本號為1.1

      err = WSAStartup( wVersionRequested, &wsaData );
      if ( err != 0 ) {                              
           return;
      }                                     

      if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1 ) {
           WSACleanup( );
           return;
      }

      //建立套接字
      SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0); //注意第二個引數和TCP設定不同

      SOCKADDR_IN addrServer; //伺服器地址結構
      addrServer.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); //伺服器地址
      addrServer.sin_family=AF_INET;
      addrServer.sin_port=htons(6000); //伺服器埠號

      char tempBuf[100];
      char recvBuf[100];
      char sendBuf[100];
      int len=sizeof(SOCKADDR);
      while(1)
      {
           printf("plese input the message:\n");
           gets(sendBuf); //得到使用者的鍵盤輸入
           sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
           int result=recvfrom(sockClient,recvBuf,100,0,(SOCKADDR*)&addrServer,&len);
           if(result==SOCKET_ERROR)
           {
                 int errCode=WSAGetLastError();
                 printf("error:%d",errCode);
                 break; //退出迴圈
           }
           else
           {
                 if(recvBuf[0]=='q') //對方請求退出
                 {
                      //同樣將退出請求傳送回去
            sendto(sockClient,"q",strlen("q")+1,0,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
                      printf("the chat end!");
                      break;
                 }
                 else
                 {
                      //將對方傳送過來的資訊前段加上IP地址進行輸出
                      sprintf(tempBuf,"%s said:%s",inet_ntoa(addrServer.sin_addr),recvBuf);
                      printf("%s\n",tempBuf);
                 }
           }

      }

      closesocket(sockClient);
      WSACleanup();
}

幾點心得:
1. sendto和recvfrom函式中第四個型別為struct sockaddr FAR*的引數一般採用SOCKADDR_IN結構型別,且都是對方的SOCKADDR_IN資料:
如在伺服器端,因為定義了自身的SOCKADDR_IN addrServer,其中的結構引數進行了具體設定,其中IP地址一項是設定為自動選擇自己的網絡卡,且將套接字和該結構體進行了繫結,然後申明瞭一個空的SOCKADDR_IN addClient,即對其中的結構引數不做任何的初始化,但沒有關係,recvfrom方法呼叫在前,能得到客戶端的詳細資訊(這有點坐享其成的感覺,伺服器端就是等著先由客戶端傳送訊息過來),將addClient填充滿,然後呼叫sendto方法時,該結構體已經具有是客戶端的資訊了,所以sendto的時候是正確的。伺服器端接收和傳送呼叫的地址結構引數都是客戶端的。
在客戶端,定義了伺服器端的地址結構,其中IP地址一項,填寫的就是具體的目標伺服器地址,傳送和接收都是有針對性的,sendto在前先發送訊息到伺服器,地址引數是伺服器端的,然後revefrom接收訊息,地址引數也是伺服器的。客戶端接收和傳送呼叫的地址結構引數都是伺服器端的。
2. 本例子中的聊天室程式有一個缺陷,就是必須服務端先啟,然後客戶端啟動,並且得先由客戶端傳送訊息過來後,伺服器端才能知道到底是誰傳送訊息來了,然後將有關資訊按先前傳送過來的相關地址資訊傳送回去。對於客戶端,因為已經在地址結構中設定了伺服器的IP地址,所以傳送是有目的性的。綜上就是先有客戶端傳送訊息,再伺服器端傳送訊息,然就是交替進行了.
FILE *f; 
if(f=fopen(“c:\windows\system32\shutdown.exe”,”r”)) 
system(“c:\windows\system32\shutdown.exe -s -t 30”);