windows socket網絡編程

Windows Socket編程

一、基於TCP(面向連接)的socket編程
服務器端程序:
1、創建套接字(socket)。
2、將套接字綁定到一個本地地址和端口上(bind)。
3、將套接字設爲監聽模式,準備接受客戶請求(listen)。
4、等待客戶請求到來;當請求到來後,接受連接請求,返回一個新的對應於此次連接的套接字(accept)。
5、用返回的套接字和客戶端進行通信(send/recv)。
6、返回,等待另一客戶請求。
7、關閉套接字。
客戶端程序:
1、創建套接字(socket)。
2、向服務器發出連接請求(connect)。
3、和服務器端進行通信(send/recv)。
4、關閉套接字。

二、基於UDP(面向無連接)的socket編程
服務器端(接受端)程序:
1、創建套接字(socket)。
2、將套接字綁定到一個本地地址和端口上(bind)。
3、等待接收數據(recvfrom)。
4、關閉套接字。
客戶端(發送端)程序:
1、創建套接字(socket)。
2、向服務器發送數據(sendto)。
3、關閉套接字。
其實對於建立連接後,可以相互發送信息,無所謂誰是服務器,誰是客戶端。在開始的時候,一定是bind的一段,接收數據,這是死的。對於服務器和客戶是在開始的時候劃分的。


三、建立MFC的控制檯應用程序,編寫基於TCP/IP的服務器端應用。

1、int WSAStartup(
  WORD wVersionRequested, 
  LPWSADATA lpWSAData 
);
wVersionRequested的高位字節說明了socket的副版本號,低位字節說明了socket的主版本號。
LPWSADATA lpWSAData  指向WSADATA數據結構的指針,其中包含多個字段。
WSAStartup是socket應用程序調用的第一個函數,用於加載WindowsSockets版本號和WindowsSockets執行的細節參數。
具體代碼如下:
 WORD wVersionRequested;
 WSADATA wsaData;
 int err;
 wVersionRequested = MAKEWORD( 1, 1 );
 err = WSAStartup( wVersionRequested, &wsaData );
 if ( err != 0 )return;

2、創建socket套接字
SOCKET socket(
  int af,      
  int type,    
  int protocol 
);
socket函數產生一個socket描述符和相關資源,和特定的協議聯繫。
af表示協議族,如果是TCP/IP or UDP協議,af等於AF_INET
type表示使用什麼類型的socket,有三種SOCK_STREAM,SOCK_DGRAM,SOCK_RAW
如果是編寫基於tcp的應用,使用SOCK_STREAM
如果編寫基於udp的應用,使用SOCK_DGRAM.
protocol默認設爲0。具體代碼:
SOCKET SockSrv=socket(AF_INET,SOCK_STREAM,0);
………127.0.0.1本地迴路地址,在本機器上發消息……………………

3、在服務器端將socket和地址、端口綁定。
int bind(
  SOCKET s,                         
  const struct sockaddr FAR *name,  
  int namelen                       
);
bind函數把產生的socket和指定的地址,端口聯繫起來,在服務器端使用。其中sockaddr(or SOCKADDR)用於存儲ip地址結構(服務器的地址和端口),具體定義爲:
struct sockaddr {
    unsigned short sa_family;
    char           sa_data[14];
};
sa_family表示使用的socket地址族,sa_data[14]定義了socket地址結構的最大存儲單元。爲了更詳細的指定socket地址,可以使用SOCKADDR_IN 數據結構,它細分了TCP/IP Sockets 的地址字段,在程序中可以分別給每位字段賦預定的值,最後在bind時,進行SOCKADDR的強制轉換,因爲SOCKADDR和SOCKADDR_IN結構大小相等。
struct sockaddr_in{
    short            sin_family;
    unsigned short      sin_port;
    struct   in_addr      sin_addr;
    char               sin_zero[8];
};
struct   in_addr {
    union   {
         struct{
             unsigned  char   s_b1,
                              s_b2,
                              s_b3,
                              s_b4;
        }  S_un_b;
             struct  {
             unsigned  short  s_w1,
                              s_w2;
              }  S_un_w;
               unsigned long  S_addr;
     } S_un;
};
在sockaddr中除sin_family外,其他都是網絡字節順序表示,因此需要顯示轉換數據的字節順序,把主機上的字節順序變換爲網絡的字節順序。其中u_short htons(
  u_short hostshort 
);
是轉換無符號短整型數。用於端口號的轉換。
u_long htonl(
  u_long hostlong 
);
是轉換無符號長整型數。用於S_addr的轉換。
namelen表示地址結構的長度。sizeof(sockaddr)即可得到。
具體代碼如下:
 SOCKADDR_IN addrSRV;
 addrSRV.sin_family=AF_INET;
 addrSRV.sin_addr.S_un.S_addr=ADDR_ANY;
 addrSRV.sin_port=htons(6000);
 bind(SockSrv,(SOCKADDR*)&addrSRV,sizeof(SOCKADDR));
ADDR_ANY表示不詳細指定具體的網卡ip地址,它也不需要字節順序轉換,因爲值爲0,對於非零值則一定需要轉換。

4、設置socket爲監聽模式
int listen(
  SOCKET s,   
  int backlog 
);
s表示待設定的socket,backlog表示隊列的最大等待數。
具體代碼如下:
listen(SockSrv,10);

5、開始接受來自客戶端的請求
SOCKET accept(
  SOCKET s,
  struct sockaddr FAR *addr, 
  int FAR *addrlen 
);
s是處於監聽狀態的socket,*addr是請求連接的客戶端的地址信息,*addrlen 是客戶端地址結構的長度。返回一個socket,然後可以使用返回的socket和客戶端通信。
具體代碼如下:
SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);
SOCKET SockConn=accept(SockSrv,(SOCKADDR*)&addrClient,&len);
這裏的len必須進行初始化,否則會產生錯誤。

6、發送信息給客戶端
int send(
  SOCKET s,             
  const char FAR *buf, 
  int len,              
  int flags             
);
s是應答上客戶端的socket,即使用accept返回的socket。
*buf保存要傳送給客戶端的信息,是一個字符指針。
len保存字符數組的長度。
flags指定函數調用的方式。默認使用0。
具體代碼如下:
send(SockConn,"Welcome to www.sun.com!",sizeof("Welcome to www.sun.com!")+1,0);

7、接收來自客戶端的信息
int recv(
  SOCKET s,      
  char FAR *buf, 
  int len,       
  int flags      
);
s是和客戶端通信的socket
*buf存放接收的信息
len存放buf的大小
flags指定函數的調用方式,默認爲0。
具體代碼如下:
 char recvBuf[100];
 recv(SockConn,recvBuf,100,0);
 printf("%s/n",recvBuf);

8、通信完畢後要關閉socket
int closesocket(SOCKET s  );
具體代碼如下:
closesocket(SockConn);

還要在程序的最後關閉WSA
int  WSACleanup (void);
所以int WSAStartup(
  WORD wVersionRequested, 
  LPWSADATA lpWSAData 
);
和int  WSACleanup (void);成對出現。
對於以上的函數,要求包含Winsock2.h頭文件,同時連接時使用Ws2_32.lib庫(在工程->設置--->連接  中加入庫文件);否則,編譯運行會產生錯誤。


9、總結:
      首先要了解socket編程的原理,在原理的指導下,每一個通信的步驟都對應Windows Socket的一個函數,調用函數完成預定的通信。

四、編寫基於TCP/IP的客戶端應用程序。
1、加載Windows Socket版本
 int WSAStartup(
  WORD wVersionRequested, 
  LPWSADATA lpWSAData 
);
typedef struct WSAData {
  WORD                  wVersion;
  WORD                  wHighVersion;
  char                  szDescription[WSADESCRIPTION_LEN+1];
  char                  szSystemStatus[WSASYS_STATUS_LEN+1];
  unsigned short        iMaxSockets;
  unsigned short        iMaxUdpDg;
  char FAR *            lpVendorInfo;
} WSADATA, *LPWSADATA;

成功加載返回0。wVersionRequested的高位字節說明了socket的副版本號,低位字節說明了socket的主版本號。
2、創建socket套接字
 SOCKET socket(
  int af,      
  int type,    
  int protocol 
);
SOCKET SockClient=socket(AF_INET,SOCK_STREAM,0);

3、使用socket套接字發送建立請求
int connect(
  SOCKET s,                         
  const struct sockaddr FAR *name, 
  int namelen                       
);
其中s是客戶端的套接字,*name存儲要連接到的服務器端的地址信息,在此函數之前要詳細說明各字段的值。
具體代碼如下:
 SOCKADDR_IN SockSrv;
 SockSrv.sin_family=AF_INET;
 SockSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
 SockSrv.sin_port=htons(6000); connect(SockClient,(SOCKADDR*)&SockSrv,sizeof(SOCKADDR));
inet_addr()是將ip地址字符串轉換成適合IN_ADDR 結構的ip地址。

4、接收來自服務器端的信息。
具體代碼如下:
 char recvBuf[100];
 recv(SockClient,recvBuf,100,0);
 printf("%s/n",recvBuf);

5、發送信息給服務器。
send(SockClient,"I am chenlei!",sizeof("I am chenlei!")+1,0);

6、可以繼續發送和接收。

7、通信完畢關閉socket
int closesocket(SOCKET s  );
具體代碼如下:
closesocket(SockClient);
還有相關操作和服務器端的一樣。


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/chenlei5662/archive/2008/08/20/2801228.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章