Windows網路程式設計(三):建立TCP連線和收發訊息
阿新 • • 發佈:2018-11-27
先看服務端:
// 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()會一直等待。