天天看點

[Win32]用戶端程式

用戶端程式過程

一個Socket用戶端程式的典型過程如下。

  1. 用戶端程式在運作後,首先需要使調用WSAStartup函數,確定程序加載socket應用程式所必須的環境和庫檔案,如Ws2_32.dll。
  2. 調用函數Socket建立SOCKET,在建立時需指定使用的網絡協定、連接配接類型等。
  3. 填充SOCKADDR結構,指定服務端的位址、端口等。
  4. 調用connect函數連接配接到服務端。
  5. 如果連接配接成功,就可以使用send和recv函數發送和接收資料。
  6. 在資料傳輸完成後,可調用closesocket函數關閉Socket。
  7. 調用WSACleanup函數釋放資源。

用戶端程式

建立win32項目控制台程式 Win32Client項目:

// Win32Client.cpp : 定義控制台應用程式的入口點。 
#include "stdafx.h" 
#include <stdio.h>
#include <WinSock2.h>
#pragma  comment(lib,"wsock32.lib")
#define  DEFIP  "127.0.0.1"  //本地位址
#define  DEFPORT  10000  //端口要大于1024 
void main()
{ 
  WSADATA wsaData;
  LPVOID recvbuf;
  if (WSAStartup(MAKEWORD(2,2),&wsaData) != NO_ERROR)
  {
    printf("Error at WSAStartup()\n");
  }
  SOCKET sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  if (sockfd == INVALID_SOCKET)
  {
    printf("error at socket():%ld\n",WSAGetLastError());
    closesocket(sockfd);
    WSACleanup();
    return;
  }
  SOCKADDR_IN serverAddr; 
  serverAddr.sin_family=AF_INET;
  serverAddr.sin_addr.s_addr= inet_addr(DEFIP);
  serverAddr.sin_port=htons(DEFPORT); 
  if(connect(sockfd,(SOCKADDR*)&serverAddr,sizeof(SOCKADDR)) == SOCKET_ERROR) 
  {
    printf("error at connect()%ld\n",WSAGetLastError());
    closesocket(sockfd);
    WSACleanup();
    return;
  }
  int bytesSend=0;
  char sendBuf[32]="get information";
  bytesSend=send(sockfd,sendBuf,strlen(sendBuf)+1,0);
  if (bytesSend == SOCKET_ERROR)
  {
    printf("error at send()%ld\n",WSAGetLastError());
    closesocket(sockfd);
    WSACleanup();
    return;
  }
  printf("Bytes send: %ld\n",bytesSend);

  int bytesRecv=0;  
  char recvBub[2096]={0};
  while( bytesRecv != SOCKET_ERROR)
  {
    bytesRecv=recv(sockfd,recvBub,strlen(recvBub)+1,0);
    if (bytesRecv == 0)
    {
      printf("error at recv()%d",WSAGetLastError());
      break;
    }
    printf("bytes recv:[%d] %s\n",bytesRecv,recvBub);
  }
  closesocket(sockfd);
  WSACleanup();
}      

函數解析

/*
SOCKET socket( int af, int type, int protocol );
參數:int af:通信協定族(AF_UNIX,AF_INET等),AF_UNIX隻能夠用于單一的Unix系統程序間的通信,而AF_INET是針對Internet的,是以可以允許遠端——本機之間的通信。AF:Address families(位址協定族)。協定族決定了socket的位址類型,在通信中必須采用對應的位址,如AF_INET決定了要用ipv4位址(32位的)與端口号(16位的)的組合、AF_UNIX決定了要用一個絕對路徑名作為位址。
int type:socket類型:SOCK_STREAM(TCP/IP),SOCK_DGRAM(UDP)
int protocol:指定協定(有IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它們分别對應TCP傳輸協定、UDP傳輸協定、STCP傳輸協定、TIPC傳輸協定),由于我們指定了type,是以這個地方我們一般隻要用0來代替就可以了
傳回值:若成功傳回SOCKET對象辨別,SOCKET就是unsigned int;如失敗,則傳回INVALID_SOCKET,調用WSAGetLastError()可得知原因,所有WinSocket的函數都可以使用這個函數來擷取失敗的原因。
注意:并不是上面的type和protocol可以随意組合的,如SOCK_STREAM不可以跟IPPROTO_UDP組合。當protocol為0時,會自動選擇type類型對應的預設協定。
當我們調用socket建立一個socket時,傳回的socket描述字它存在于協定族(address family,AF_XXX)空間中,但沒有一個具體的位址。如果想要給它指派一個位址,就必須調用bind()函數,否則就當調用connect()、listen()時系統會自動随機配置設定一個端口。

位址結構說明:
typedef struct sockaddr_in
{
short sin_family;//由于我們主要使用Internet,是以一般為AF_INET
u_short sin_port;//16位端口号,網絡位元組順序
struct in_addr sin_addr;//32位IP位址,網絡位元組順序
char sin_zero[8];//保留
}SOCKADDR_IN;
struct in_addr  // Internet address. 
{  
  uint32_t       s_addr;     // address in network byte order
};
//sin_:socket address internet
struct sockaddr是通用的套接字位址,而struct sockaddr_in則是internet環境下套接字的位址形式,二者長度一樣,都是16個位元組。二者是并列結構,指向sockaddr_in結構的指針也可以指向sockaddr。一般情況下,需要把sockaddr_in結構強制轉換成sockaddr結構再傳入系統調用函數中。 

位元組轉換函數 
在網絡上面有着許多類型的機器,這些機器在表示資料的位元組順序是不同的,比如i386晶片是低位元組在記憶體位址的低端,高位元組在高端,而alpha晶片卻相反.為了統一起來,則有專門的位元組轉換函數. 
unsigned long int htonl(unsigned long int hostlong) 
unsigned short int htons(unisgned short int hostshort) 
unsigned long int ntohl(unsigned long int netlong) 
unsigned short int ntohs(unsigned short int netshort)
在這四個轉換函數中,h代表host, n代表 network.s代表short l代表long
第一個函數的意義是将本機器上的long資料轉化為網絡上的long.其他幾個函數的意義也差不多

int connect(SOCKET s,const struct sockaddr FAR * name,int namelen);
參數:s:socket的辨別碼
name:存儲伺服器的連接配接資訊(協定族,伺服器的ip,端口)
namelen:name的長度
傳回值:成功傳回0,失敗傳回SOCKET_ERROR

int send( SOCKET s, const char FAR *buf,int len, int flags );
參數:s:Socket 的識别碼
buf:存放要傳送的資料的暫存區
len buf:的長度
flags:此函數被調用的方式,可以設為 0或MSG_DONTROUTE及MSG_OOB組合
傳回值:若成功則傳回發送的資料的長度,否則傳回SOCKET_ERROR

int recv( SOCKET s, char FAR *buf, int len, int flags );
參數:s:Socket 的識别碼
buf:存放接收到的資料的暫存區
len buf:的長度
flags:此函數被調用的方式,可以設為 0或MSG_DONTROUTE及MSG_OOB組合
傳回值:若成功則傳回接收資料的長度,否則傳回SOCKET_ERROR

int closesocket(SOCKET s);
*/      

其結果:

[Win32]用戶端程式