WinSock網絡編程基礎(2)客戶端

接下來講一下如何用WinSock建立基於TCP/IP模型的客戶端和服務器。編程

TCP能夠提供兩個計算機間可靠無誤的數據傳輸,應用程序使用TCP通訊時,會在兩臺計算機之間創建一個虛擬鏈接,鏈接以後計算機之間變能夠以雙向字節流進行數據交換。windows

下面說下簡單的發送數據的客戶端實現.api

建立客戶機的鏈接比較簡單:服務器

 

1.建立一個套接字,定義addrinfo對象並初始化這些值(該對象包含一個sockaddr結構)

struct addrinfo *result = NULL,
                *ptr = NULL,
                hints;

ZeroMemory( &hints, sizeof(hints) );
hints.ai_family = AF_UNSPEC;  //能夠是IPv4或IPv6地址
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

 

調用getaddrinfo函數肯定服務器ip地址(由命令行參數傳遞)和端口網絡

#define DEFAULT_PORT "27015"

// Resolve the server address and port
iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
    printf("getaddrinfo failed: %d\n", iResult);
    WSACleanup();
    return 1;
}

 

 socket原型:異步

socket( socket

       IN int af,       //協議的地址族,使用IPv4來描述Winsock,設置爲AF_INET tcp

       IN int type,     //套接字類型,TCP/IP設置爲SOCK_STREAM,UDP/IP設置爲SOCK_DGRAM 函數

       IN int protocol      //用於給定地址族和類型具備多重入口的傳送限定,TCP設置爲IPPROTO_TCP,UDP設置爲IPPROTO_UDP this

       ); 

調用socket建立嵌套字,錯誤檢測

SOCKET ConnectSocket = INVALID_SOCKET;
ptr=result;
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, 
    ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
    printf("Error at socket(): %ld\n", WSAGetLastError());
    freeaddrinfo(result);
    WSACleanup();
    return 1;
}

 

2.鏈接到的服務器名。(在TCP/IP中就是監聽服務器的IP地址和端口號)

iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
    closesocket(ConnectSocket);
    ConnectSocket = INVALID_SOCKET;
}
//釋放資源
freeaddrinfo(result);

if (ConnectSocket == INVALID_SOCKET) {
    printf("Unable to connect to server!\n");
    WSACleanup();
    return 1;
}

 

客戶端程序在建立套節字以後,要使用 connect函數請求與服務器鏈接,connect原型:

int WSAAPI connect( 

        IN SOCKET s,            //要創建鏈接的socket 

        IN const struct sockaddr FAR * name,    //指向保存要創建鏈接信息的地址結構 

        IN int namelen          //參數2指向地址結構的大小 

        ); 

 

3.發送和接收數據。

 

收發數據纔是網絡編程的主題,在已經創建鏈接的套接字上發生數據可使用send或WSASend(WinSock2中),接受可用recv和WSARecv。收發數據都是用char類型(面向字節的數據)。

#define DEFAULT_BUFLEN 512

int recvbuflen = DEFAULT_BUFLEN;

char *sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];

int iResult;

iResult = send(ConnectSocket, sendbuf, (int) strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
    printf("send failed: %d\n", WSAGetLastError());
    closesocket(ConnectSocket);
    WSACleanup();
    return 1;
}

printf("Bytes Sent: %ld\n", iResult);

iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
    printf("shutdown failed: %d\n", WSAGetLastError());
    closesocket(ConnectSocket);
    WSACleanup();
    return 1;
}

do {
    iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
    if (iResult > 0)
        printf("Bytes received: %d\n", iResult);
    else if (iResult == 0)
        printf("Connection closed\n");
    else
        printf("recv failed: %d\n", WSAGetLastError());
} while (iResult > 0);

 

全部收發數據返回的錯誤代碼都是SOCKET_ERROR,一旦有錯誤返回,系統就會調用WSAGetLastError()獲取詳細錯誤信息。

常見錯誤: 


WSAECONNABORTED和WSAECONNRESET:鏈接正在關閉(超時或者因爲通訊放正在關閉鏈接)
WSAEWOULDBLOCK:套接字處於非阻塞模式或異步狀態。


使用send和recv函數原型。

int send(

  SOCKET s, // 套節字句柄

  const char FAR* buf,// 要發送的數據的緩衝區的地址

  int len, // 緩衝區的長度

  int flags  // 指定了的調用方式,一般設爲0

);

int recv(

  SOCKET s,

  char FAR* buf,

  int len,

  int flags

);

 

send函數在一個鏈接的套節字上發送緩衝區內的數據,返回發送數據的實際字節數,recv函數從對方接收數據,並存儲它到指定的緩衝區。flag參數在這兩個函數中一般設爲0。

 

在阻塞模式下,send將會阻塞線程的執行直到全部的數據發送完畢(或者一個錯誤的發生),而recv函數將返回儘量多的當前信息,一直到緩衝區指定的大小。

 

函數執行失敗返回INVALID_SOCKET(-1),應該調用closesocket函數將它關閉。若是沒有錯誤發生,函數返回0,不然返回SOCKET_ERROR。函數用法以下:

int closesocket(

  __in  SOCKET s // 函數惟一的參數就是要關閉的套節字的句柄

);

 

4.斷開鏈接,關閉套接字(當客戶機已經發出數據,可使用shutdown函數和SD_SEND宏關閉發送套接字,客戶機仍然容許接受來自服務器套接字上的數據)

iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
    printf("shutdown failed: %d\n", WSAGetLastError());
    closesocket(ConnectSocket);
    WSACleanup();
    return 1;
}
closesocket(ConnectSocket);
WSACleanup();

 

附完整的客戶端代碼:

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <IPHlpApi.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Mswsock.lib")
#pragma comment(lib, "Advapi32.lib")

#define DEFAULT_PORT "27015"
#define DeFAULT_BUFLEN 512

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	int iResult;
	struct addrinfo *result = NULL, 
					*ptr = NULL,
					hints;

	char *sendbuf = "this is a test";
	char recvbuf[DeFAULT_BUFLEN];
	int recvbuflen = DeFAULT_BUFLEN;
	SOCKET ConnectSocket = INVALID_SOCKET;

	if (argc != 2)
	{
		printf("usage: %s server-name\n", argv[0]);
	}

	// 初始化winsock
	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0)
	{
		printf("WSAStartup failed: %d\n", iResult);
		return 1;
	}

	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	// 肯定服務器地址和端口
	iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
	if (iResult != 0)
	{
		printf("getaddrinfo failed: %d\n", iResult);
		WSACleanup();
		return 1;
	}

	// 嘗試鏈接到服務器地址,直到成功
	for (ptr = result; ptr != NULL; ptr = ptr->ai_next)
	{
		// 建立套接字鏈接到服務器
		ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
		if (ConnectSocket == INVALID_SOCKET)
		{
			printf("Error at socket(): %ld\n", WSAGetLastError());
			freeaddrinfo(result);
			WSACleanup();
			return 1;
		}
		// 鏈接服務器
		iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
		if (iResult == SOCKET_ERROR)
		{
			closesocket(ConnectSocket);
			ConnectSocket = INVALID_SOCKET;
			continue;
		}
		break;
	}

	// 釋放資源
	freeaddrinfo(result);
	if (ConnectSocket == INVALID_SOCKET)
	{
		printf("Unable to connect to server!\n");
		WSACleanup();
		return 1;
	}
	

	// 發送sendbuf內容
	iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf) + 1, 0);
	if (iResult == SOCKET_ERROR)
	{
		printf("send failed:%d\n", WSAGetLastError());
		closesocket(ConnectSocket);
		WSACleanup();
		return 1;
	}
	printf("Byte Send:%ld\n", iResult);

	// 數據發送完成以後斷開鏈接
	iResult = shutdown(ConnectSocket, SD_SEND);
	if (iResult == SOCKET_ERROR)
	{
		printf("shutdown faild: %d\n", WSAGetLastError());
		closesocket(ConnectSocket);
		WSACleanup();
		return 1;
	}

	// 收到迴應以後關閉鏈接
	do 
	{
		iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
		if (iResult > 0)
			printf("Byte received: %d\n", iResult);
		else if (iResult == 0)
			printf("connection closed\n");
		else
			printf("recv failed: %d\n", WSACleanup());
		
	} while (iResult > 0);

	// 清除套接字
	closesocket(ConnectSocket);
	WSACleanup();

	return 0;
}
  
 

  本文連接: http://www.bugcoding.com/entry/10

相關文章
相關標籤/搜索