程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> WinSock網絡編程基礎(3)server,winsock網絡編程

WinSock網絡編程基礎(3)server,winsock網絡編程

編輯:C++入門知識

WinSock網絡編程基礎(3)server,winsock網絡編程


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

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

 

創建服務器程序的第一步和客戶端一樣,要先初始化並且創建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函數:

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

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

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錯誤)

現在可以接受客服端的連接了:

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仍然處於監聽模式:

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved