Windows網絡編程(三):建立TCP連接和收發消息

先看服務端:

// ConsoleApplication3.cpp : 定義控制檯應用程序的入口點。

//

#include "stdafx.h"

#define _WINSOCK_DEPRECATED_NO_WARNINGS    //這個聲明要在stdafx.h的後面,但要在其他頭文件的前面

#include <winsock2.h>

#include <stdio.h>

#include <windows.h>

// 需要聲明Winsock庫

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

int _tmain(int argc, _TCHAR* argv[])

{

   //----------------------

   // 此時ws2_32.dll可能還沒有加載,需要用WSAStartup確認已經完成了加載

   WSADATA wsaData;

   int iResult = WSAStartup(MAKEWORD(2, 2),&wsaData);

   if (iResult != NO_ERROR) {

       wprintf(L"WSAStartup failed with error: %ld\n", iResult);

       return 1;

   }

   //----------------------

   // 創建一個套接字用於監聽客戶端連接的請求

   SOCKET ListenSocket;

   // 參數一:使用IPv4地址

   // 參數二:Socket支持可靠、雙向的連接

   // 參數三:Winsock支持多種網絡協議,這裏使用TCP協議

   ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

   if (ListenSocket == INVALID_SOCKET) {

       wprintf(L"socket failed with error: %ld\n", WSAGetLastError());

       WSACleanup();

       return 1;

   }

   //----------------------

   // 給套接字綁定端口和ip

   // 在IPv4下sockaddr_in、SOCKADDR_IN、sockaddr、SOCKADDR可以通用

   sockaddr_in service;

   service.sin_family = AF_INET;

   // inet_addr()將點十進制轉換成無符號長整型,也就是in_addr,還有個inet_ntoa函數可以

   // 傳入一個in_addr將其轉爲點十進制

   service.sin_addr.s_addr = inet_addr("127.0.0.1");

   //htons主機字節轉爲網絡字節,傳入u_short,htonsl傳入u_long,ntohs和ntohl是這個函數的逆運算

   service.sin_port = htons(12000);

if (bind(ListenSocket,

   (SOCKADDR *)service, sizeof(service)) == SOCKET_ERROR) {

   wprintf(L"bind failed with error: %ld\n", WSAGetLastError());

   closesocket(ListenSocket);

   WSACleanup();

   return 1;

}

//----------------------

// 監聽客戶端請求

if (listen(ListenSocket, 1) == SOCKET_ERROR) {

   wprintf(L"listen failed with error: %ld\n", WSAGetLastError());

   closesocket(ListenSocket);

   WSACleanup();

   return 1;

}



SOCKET AcceptSocket;

wprintf(L"Waiting for client to connect...\n");



//----------------------

// 接收客戶端的請求

AcceptSocket = accept(ListenSocket, NULL, NULL);

if (AcceptSocket == INVALID_SOCKET) {

   wprintf(L"accept failed with error: %ld\n", WSAGetLastError());

   closesocket(ListenSocket);

   WSACleanup();

   return 1;

}

else

   wprintf(L"Client connected.\n");



closesocket(ListenSocket);



WSACleanup();



return 0;

再看客戶端:

// ConsoleApplication2.cpp : 定義控制檯應用程序的入口點。

//

#include "stdafx.h"

#define _WINSOCK_DEPRECATED_NO_WARNINGS //使用被棄用函數不警告,位置必須在stdafx.h的後面,在其他頭文件的前面

#include <winsock2.h>

#include <ws2tcpip.h>

#include <stdio.h>

// 需要聲明Winsock庫

#pragma comment(lib, "ws2_32.lib")

int wmain()

{

   //----------------------

   // 初始化Winsock,確保ws2_32.dll可以使用

   WSADATA wsaData;

   int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

   if (iResult != NO_ERROR) {

       wprintf(L"WSAStartup function failed with error: %d\n", iResult);

       return 1;

   }

   //----------------------

   // 創建一個套接字用於連接服務器

   SOCKET ConnectSocket;

   // 參數一:使用IPv4地址

   // 參數二:Socket支持可靠、雙向的連接

   // 參數三:Winsock支持多種網絡協議,這裏使用TCP協議

   ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

   if (ConnectSocket == INVALID_SOCKET) {

       wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());

       WSACleanup();

       return 1;

   }

   //----------------------

   // 給套接字綁定端口和ip

   sockaddr_in clientService;

   clientService.sin_family = AF_INET;

   clientService.sin_addr.s_addr = inet_addr("127.0.0.1");

   clientService.sin_port = htons(12000);

   //----------------------

   // 連接到服務器

   iResult = connect(ConnectSocket, (SOCKADDR *)&clientService, sizeof(clientService));

   if (iResult == SOCKET_ERROR) {

       wprintf(L"connect function failed with error: %ld\n", WSAGetLastError());

       iResult = closesocket(ConnectSocket);

       if (iResult == SOCKET_ERROR)

           wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());

       WSACleanup();

       return 1;

   }

wprintf(L"Connected to server.\n");



iResult = closesocket(ConnectSocket);

if (iResult == SOCKET_ERROR) {

   wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());

   WSACleanup();

   return 1;

}



WSACleanup();

return 0;

在上面的服務器和客戶端代碼的基礎上,我們來添加發送和接收TCP消息的代碼。服務器和客戶端連接成功以後就可以進行發送和接收TCP消息了,爲了減少文章篇幅,只貼上新增加的代碼。

    char *sendbuf = "test from server";

   int recvbuflen = 512;

   char recvbuf[512];

   // 將緩衝區置0,防止字符串出現“燙”

   memset(recvbuf, 0, 512);



   //----------------------

   // 發送數據

   iResult = send(AcceptSocket, sendbuf, (int)strlen(sendbuf), 0);

   if (iResult == SOCKET_ERROR)

   {

       printf("send failed with error %d\n", WSAGetLastError());

       closesocket(AcceptSocket);

       WSACleanup();

       return 1;

   }

   printf("Bytes Sent:%d %s \n", iResult,sendbuf);



   //----------------------

   // 當沒有要發送的數據時關閉連接

   iResult = shutdown(AcceptSocket, SD_SEND);

   if (iResult == SOCKET_ERROR)

   {

       printf("shutdown failed with error:%d\n", WSAGetLastError());

       closesocket(AcceptSocket);

       WSACleanup();

       return 1;

   }

   //----------------------

   // 接收數據

   do 

   {

       // 如果連接已經關閉,iResult爲0

       iResult = recv(AcceptSocket, recvbuf, recvbuflen, 0);

       if (iResult != 0)

           printf("Bytes received:%d %s\n", iResult, recvbuf);

       else if (iResult == 0)

           printf("Connection closed\n");

       else

           printf("recv failed with error %d\n", WSAGetLastError());



   } while (iResult != 0);

重點說一下recv()這個函數,如果協議棧中的數據小於recv()接收的緩衝區,recv()會直接接收所有的數據,如果協議棧中的數據大於接收的緩衝區,那麼會將recv()接收的緩衝區填滿,然後再接着接收,如果協議棧中沒有數據recv()會一直等待。

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