WinSock網絡編程基礎(3)server

上一篇講的是簡單的發送數據的客戶端的實現。接下來說的是如何實現收發數據服務器。
這裏說的服務器其實就是一個進程,它須要等待任意數量的客戶端與之創建起鏈接,以便響應它們的請求。windows

服務器必須在已知的名稱上監聽鏈接(在TCP/IP中是ip地址和端口號,不一樣協議的尋址方案和命名方法也不一樣)api

 

建立服務器程序的第一步和客戶端同樣,要先初始化而且建立SOCKET(LISTENSOCKET用於監聽客戶端鏈接)

#define DEFAULT_PORT "27015"

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

ZeroMemory(&hints, sizeof (hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;

// Resolve the local address and port to be used by the server
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
    printf("getaddrinfo failed: %d\n", iResult);
    WSACleanup();
    return 1;
}
SOCKET ListenSocket = INVALID_SOCKET;
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
    printf("Error at socket(): %ld\n", WSAGetLastError());
    freeaddrinfo(result);
    WSACleanup();
    return 1;
}

  

 

這裏的套接字設置的監聽地址爲AF_INET,也就是IPv4的地址。若是想監聽IPv6的地址就能夠設置爲AF_INET6,若是想同時監聽的話就須要建立兩個監聽套接字,(vista系統以後提供了一個特殊的套接字可以同時監聽IPv4和IPv6。詳情: Dual-Stack Sockets服務器

 

接下來須要將套接字綁定到它已知的地址(IP和端口)

iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
}
freeaddrinfo(result);
 

  

bind函數:socket

int bind(
  _In_ SOCKET s, //等待客戶端鏈接的套接字
  _In_ const struct sockaddr *name, // 指向進行綁定的地址結構 
  _In_ int namelen //表示要傳遞的,由協議決定的地址結構的長度
);

一旦綁定出錯,bind回返回SOCKET_ERROR。對bind而言,最多見的錯誤是WSAEADDRINUSE(表示另外一進程已經同本地ip及端口號綁定,或者該ip和端口號處於TIME——WAIT狀態)和WSAEFAULT(調用的套接字已經被綁定)tcp

bind函數被調用以後,getaddrinfo函數返回的地址信息就基本沒有什麼做用了,可使用freeaddrinfo釋放地址信息。函數

 

接下來要作的是將套接字設置爲監聽模式:

if ( listen( ListenSocket, SOMAXCONN ) == SOCKET_ERROR ) {
    printf( "Listen failed with error: %ld\n", WSAGetLastError() );
    closesocket(ListenSocket);
    WSACleanup();
    return 1;
}

  

listen函數:指針

int listen(
  _In_ SOCKET s, // 被綁定的套接字
  _In_ int backlog // 監聽隊列中容許保持的還沒有處理的最大鏈接數量
);

backlog這個參數很重要,它指定了被擱置的鏈接的最大隊列長度(好比backlog參數爲3,可是有4臺客戶端同時發出請求,那麼前3個會被放在掛起隊列之中,而第四個會鏈接請求失敗返回WSAECONNREFUSED錯誤)server

如今能夠接受客服端的鏈接了:

SOCKET ClientSocket;

ClientSocket = INVALID_SOCKET;

ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
    printf("accept failed: %d\n", WSAGetLastError());
    closesocket(ListenSocket);
    WSACleanup();
    return 1;
}

  

accept函數會返回一個新的套接字描述符,它對應已經接受了客戶端的鏈接,該客戶端的後續操做都用這個新的套接字,而原來LinstenSocket仍然處於監聽模式:blog

SOCKET accept(
  _In_ SOCKET s, //一個出於監聽模式的套接字
  _Out_ struct sockaddr *addr, //一個指向sockaddr_in結構的指針,用於取得對方的地址信息
  _Inout_ int *addrlen //addr結構的長度
);

 

接受客戶端的鏈接以後,就使用RECV和SEND函數接收發送數據了

#define DEFAULT_BUFLEN 512

char recvbuf[DEFAULT_BUFLEN];
int iResult, iSendResult;
int recvbuflen = DEFAULT_BUFLEN;

do {
    iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
    if (iResult > 0) {
        printf("Bytes received: %d\n", iResult);
        iSendResult = send(ClientSocket, recvbuf, iResult, 0);
        if (iSendResult == SOCKET_ERROR) {
            printf("send failed: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
            return 1;
        }
        printf("Bytes sent: %d\n", iSendResult);
    } else if (iResult == 0)
        printf("Connection closing...\n");
    else {
        printf("recv failed: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return 1;
    }

} while (iResult > 0);

  

 

通訊完畢,斷開鏈接:

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

  

 

完整的server源代碼:隊列

#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, "Mssock.lib")
//#pragma comment(lib, "Advapi32.lib")

#define DEFAULT_PORT "27015"
#define DeFAULT_BUFLEN 512
int main()
{
	WSADATA wsaData;
	int iResult;
	int iSendResult;
	struct addrinfo *result = NULL;
	struct addrinfo	hints;
	char recvbuf[DeFAULT_BUFLEN];
	int recvbuflen = DeFAULT_BUFLEN;
	SOCKET ListenSocket = INVALID_SOCKET;
	SOCKET ClientSocket = INVALID_SOCKET;


	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_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	hints.ai_flags = AI_PASSIVE;

	// 爲服務器肯定本地地址和端口
	iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
	if (iResult != 0)
	{
		printf("getaddrinfo failed: %d\n", iResult);
		WSACleanup();
		return 1;
	}

	ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
	if (ListenSocket == INVALID_SOCKET)
	{
		printf("socket failed with error: %ld\n", WSAGetLastError());
		freeaddrinfo(result);
		WSACleanup();
		return 1;
	}

	iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
	if (iResult == SOCKET_ERROR)
	{
		printf("bind failed with error: %d\n", WSAGetLastError());
		freeaddrinfo(result);
		closesocket(ListenSocket);
		WSACleanup();
		return 1;
	}
	freeaddrinfo(result);

	iResult = listen(ListenSocket, SOMAXCONN);
	if (iResult == SOCKET_ERROR)
	{
		printf("Listen failed with error: %d\n", WSAGetLastError());
		closesocket(ListenSocket);
		WSACleanup();
		return 1;
	}

	ClientSocket = accept(ListenSocket, NULL, NULL);
	if (ClientSocket == INVALID_SOCKET)
	{
		printf("accept failed: %d\n", WSAGetLastError());
		closesocket(ClientSocket);
		WSACleanup();
		return 1;
	}
	closesocket(ListenSocket);

	do 
	{
		iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
		if (iResult > 0)
		{
			printf("Bytes received: %d\n", iResult);
			iSendResult = send(ClientSocket, recvbuf, iResult, 0);
			if (iSendResult == SOCKET_ERROR)
			{
				printf("send failed: %d\n", WSAGetLastError());
				closesocket(ClientSocket);
				WSACleanup();
				return 1;
			}
			printf("Bytes sent: %d\n", iSendResult);
		}
		else if (iResult == 0)
		{
			printf("Connection closing...\n");
		}
		else
		{
			printf("recv failed: %d\n", WSAGetLastError());
			closesocket(ClientSocket);
			WSACleanup();
			return 1;
		}
	} while (iResult > 0);

	// 斷開鏈接
	iResult = shutdown(ClientSocket, SD_SEND);
	if (iResult == SOCKET_ERROR)
	{
		printf("shutdown failed: %d\n", WSAGetLastError());
		closesocket(ClientSocket);
		WSACleanup();
		return 1;
	}
	// 清理鏈接
	printf("recv message: %s", recvbuf);
	closesocket(ClientSocket);
	WSACleanup();

	return 0;
}

  

原文連接: http://www.bugcoding.com/entry/11

相關文章
相關標籤/搜索