TCP/UDP 基本概念
IP:每一天计算机在某一网络中的唯一身份标识
PORT:一个端口号对应一个应用程序,当做应用程序的标识
网络编程:Socket
Socket建立计算机与计算机之间的链接,建立链接之后能够发送数据包
客户端:谁发请求谁就是客户端
服务端:谁接收请求谁就是服务端
网络模型:四层
第一层:应用层:HTTP客户端/服务端
第二层:传输层:协议(TCP/UDP)
第三层:网络层:IP
第四层:链路层:以太网协议
TCP:面向链接,客户端于服务器建立链接时要经历三次握手,采用字节进行数据的传输,较为安全,但是效率比UDP慢
SYN:建立链接的标志,由客户端发送,一般都是SYN=j
ACK;确认报文,ACK首次 = 1将SYN+1发送给客户端,客户端用来确认请求被正确响应
三次握手:
第一次:客户端发起链接请求,服务端接收客户端的链接请求(验证客户端发送以及服务端接收功能是否正常)
第二次:服务端发送响应,客户端接收(验证服务端的发送和客户端的接收是否正常)
第三次:客户端发送数据给服务端,服务端接收数据(验证客户端的数据发送和服务端的数据接收是否正常)
四次挥手:
第一次:客户端发送FIN标志给服务端
第二次:服务端响应客户端的FIN请求,答复ACK,ACK是在客户端的SYN基础上+1
第三次:服务端断开链接
第四次:客户端响应服务器发送的ACK
UDP:面向无连接、存在丢包、每一个数据包的大小限制在64K以内,不安全、不靠谱的链接,
HTTP:协议的底层就是采用TCP协议
UDP服务器四部曲:
初始化socket环境:WSAStartup
构造socket对象:socket()
绑定需要占用的IP以及port:bing()
接收和发送信息:recvfrom()、sendto()
代码:
UDPServer.hpp
#pragma once
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
using namespace std;
// Adding a dynamic library
#pragma comment(lib, "ws2_32.lib")
class UDPServer {
public:
UDPServer();
~UDPServer();
// 初始化socket环境
bool InitSocketEnv();
// 构造socket对象
bool ConstrunctSocketObj();
// 绑定需要占用的IP以及port
bool BindIpAndPort();
// 接收和发送信息
void RecvAndSendMsg();
private:
SOCKET m_Socket;
SOCKADDR_IN m_BindAddress; // Bind Address
SOCKADDR_IN m_RemoteAddress; // Bind remote Address
int m_RemoteAddressLen;
};
UDPServer.cpp:
#include "UDPServer.h"
const int BUF_LEN = 1024;
UDPServer::UDPServer() : m_Socket(-1), m_RemoteAddressLen(0)
{
memset(static_cast<void*>(&m_BindAddress), 0x00, sizeof(SOCKADDR_IN));
memset(static_cast<void*>(&m_RemoteAddress), 0x00, sizeof(SOCKADDR_IN));
}
UDPServer::~UDPServer()
{
closesocket(m_Socket);
WSACleanup();
}
// 初始化socket环境
bool UDPServer::InitSocketEnv()
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "UDPServer::InitSocketEnv(): WSAStartup error: " << GetLastError() << endl;
return false;
}
return true;
}
// 构造socket对象
bool UDPServer::ConstrunctSocketObj()
{
m_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (m_Socket == INVALID_SOCKET) {
cout << "UDPServer::ConstrunctSocketObj() new socket failed: " << GetLastError() << endl;
closesocket(m_Socket);
m_Socket = INVALID_SOCKET;
return false;
}
cout << "UDPServer::ConstrunctSocketObj() new socket successful: socket id [ " << m_Socket << " ]" << endl;
return true;
}
// 绑定需要占用的IP以及port
bool UDPServer::BindIpAndPort()
{
const char* pIpStr = "127.0.0.1";
int nPort = 8080;
m_BindAddress.sin_family = AF_INET;
m_BindAddress.sin_addr.S_un.S_addr = inet_addr(pIpStr);
m_BindAddress.sin_port = htons(nPort);
auto binRet = bind(m_Socket, (sockaddr*)&m_BindAddress, sizeof(SOCKADDR));
if (binRet == SOCKET_ERROR) {
pIpStr = nullptr;
cout << "UDPServer::BindIpAndPort() when bind Address failed: " << GetLastError() << endl;
return false;
}
pIpStr = nullptr;
return true;
}
// 接收和发送信息
void UDPServer::RecvAndSendMsg()
{
char recvBuffer[BUF_LEN] = { 0 };
char sendBuffer[BUF_LEN] = "Nice to meet you, too!";
m_RemoteAddressLen = sizeof(m_RemoteAddress);
std::printf("已设置绑定占用的链接,其中IP:%s, port: %d\n", inet_ntoa(m_BindAddress.sin_addr), ntohs(m_BindAddress.sin_port));
while (true) {
int recvLen = recvfrom(m_Socket, recvBuffer, BUF_LEN, 0, (sockaddr*)&m_RemoteAddress, &m_RemoteAddressLen);
if (recvLen > 0) {
std::printf("接收到一个链接, 其中IP: %s, port: %d\n", inet_ntoa(m_RemoteAddress.sin_addr), m_RemoteAddress.sin_port);
cout << "消息内容: " << recvBuffer << endl;
}
int SendLen = sendto(m_Socket, sendBuffer, BUF_LEN, 0, (sockaddr*)&m_RemoteAddress, m_RemoteAddressLen);
if (SendLen > 0) {
cout << "发送到客户端的信息:" << sendBuffer << endl;
}
}
}
UDP客户端四部曲:
初始化socket环境:WSAStartup
构造socket对象:socket()
绑定需要占用的IP以及port:bing()
接收和发送信息:sendto(),recvfrom()、
UDPClinet.hpp
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
// Adding a dynamic library
#pragma comment(lib, "ws2_32.lib")
class UDPClient {
public:
UDPClient();
~UDPClient();
// 初始化socket环境
bool InitSocketEnv();
// 构造socket对象
bool ConstrunctSocketObj();
// 绑定需要占用的IP以及port
void BindIpAndPort();
// 发送和接收信息
void SendAndRecvMsg();
private:
SOCKET m_Socket;
SOCKADDR_IN m_RemoteAddress;
int m_RemoteAddressLen;
};
UDPClient.cpp
#include "UDPClient.h"
using namespace std;
const int BUF_LEN = 1024;
UDPClient::UDPClient() : m_Socket(-1), m_RemoteAddressLen(0)
{
memset(static_cast<void*>(&m_RemoteAddress), 0x00, sizeof(SOCKADDR_IN));
}
UDPClient::~UDPClient()
{
closesocket(m_Socket);
WSACleanup();
}
bool UDPClient::InitSocketEnv()
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "UDPClient::InitSocketEnv(): WSAStartup error: " << GetLastError() << endl;
return false;
}
return true;
}
bool UDPClient::ConstrunctSocketObj()
{
m_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (m_Socket == INVALID_SOCKET) {
cout << "UDPClient::ConstrunctSocketObj(): new socket id failed: " << GetLastError() << endl;
closesocket(m_Socket);
m_Socket = INVALID_SOCKET;
return false;
}
return true;
}
void UDPClient::BindIpAndPort()
{
const char* pIpStr = "127.0.0.1";
int nPort = 8080;
m_RemoteAddress.sin_family = AF_INET;
m_RemoteAddress.sin_port = htons(nPort);
m_RemoteAddressLen = sizeof(m_RemoteAddress);
inet_pton(AF_INET, pIpStr, &m_RemoteAddress.sin_addr);
}
void UDPClient::SendAndRecvMsg()
{
char recvBuffer[BUF_LEN] = { 0 };
char sendBuffer[BUF_LEN] = "Nice to meet you!";
while (true) {
int sendLen = sendto(m_Socket, sendBuffer, BUF_LEN, 0, (sockaddr*)&m_RemoteAddress, m_RemoteAddressLen);
if (sendLen > 0) {
std::printf("发送到服务器的链接,其中ip: %s, port: %d\n", inet_ntoa(m_RemoteAddress.sin_addr), ntohs(m_RemoteAddress.sin_port));
cout << "发送到服务器的信息:" << sendBuffer << endl;
}
int recvLen = recvfrom(m_Socket, recvBuffer, BUF_LEN, 0, (sockaddr*)&m_RemoteAddress, &m_RemoteAddressLen);
if (recvLen > 0) {
std::printf("接收到来自服务器的消息,其中ip: %s, port: %d\n", inet_ntoa(m_RemoteAddress.sin_addr), ntohs(m_RemoteAddress.sin_port));
cout << "接收到服务器的消息: " << recvBuffer << endl;
}
}
}
main执行函数:
// TCP_Program.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <thread>
#include "UDPServer.h"
#include "UDPClient.h"
void RunUDPServer()
{
// 先启动UDP的服务器
UDPServer udpServer = UDPServer();
udpServer.InitSocketEnv();
udpServer.ConstrunctSocketObj();
udpServer.BindIpAndPort();
udpServer.RecvAndSendMsg();
}
void RunUDPClinet()
{
// 启动UDP客户端
UDPClient udpClient = UDPClient();
udpClient.InitSocketEnv();
udpClient.ConstrunctSocketObj();
udpClient.BindIpAndPort();
udpClient.SendAndRecvMsg();
}
int main()
{
// 多线程执行
std::thread thrd_1(RunUDPServer);
std::thread thrd_2(RunUDPClinet);
thrd_1.join();
thrd_2.join();
return 0;
}
——————————————————————————————————————
简易TCP服务器五部曲
:
初始化变量并加载win sock相关版本库
构造socket
绑定server端的ip以及端口信息
监听server端的socket
等待一个远程客户端连接并且返回一个socket
向这个成功建立连接的socket发送信息
code:
TCPServer.h
#pragma once
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
/* 提供了网络相关的API支持
如使用WSAStartup、WSACleanup方法
引入这个动态库的两种方法:
第一种:像下面直接在代码中引入
第二种:属性设置->连接器->输入->附加依赖项->编辑->输入"ws2_32.lib"
*/
#pragma comment(lib, "ws2_32.lib")
class TCPServer {
public:
TCPServer();
~TCPServer();
bool BuildSocket();
int BindServerAddress();
int ListenClient();
SOCKET WaitAccept();
int SendMsgToClient(SOCKET& in_SocketClinet);
private:
SOCKET m_Socket;
int m_RemoteAddressLen;
sockaddr_in m_SockAddress;
sockaddr_in m_RemoteAddress;
};
TCPServer.cpp
#include "TCPServer.h"
using namespace std;
TCPServer::TCPServer()
{
m_Socket = INVALID_SOCKET;
m_RemoteAddressLen = sizeof(sockaddr_in);
memset(static_cast<void*>(&m_SockAddress), 0x00, sizeof(sockaddr_in));
memset(static_cast<void*>(&m_RemoteAddress), 0x00, sizeof(sockaddr_in));
WSADATA wsData;
WSAStartup(MAKEWORD(2, 2), &wsData);
}
TCPServer::~TCPServer()
{
closesocket(m_Socket);
WSACleanup();
}
bool TCPServer::BuildSocket()
{
m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_Socket == INVALID_SOCKET) {
cout << "TCPServer::BuildSocket() build socket failed: " << GetLastError() << endl;
m_Socket = INVALID_SOCKET;
return false;
}
cout << "TCPServer::BuildSocket() build socket success!" << endl;
return true;
}
int TCPServer::BindServerAddress()
{
m_SockAddress.sin_family = AF_INET;
m_SockAddress.sin_addr.S_un.S_addr = INADDR_ANY; // 代表本地的地址都可以访问
//m_SockAddress.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
m_SockAddress.sin_port = htons(8080);
int nRet = bind(m_Socket, (sockaddr*)&m_SockAddress, sizeof(sockaddr_in));
if (nRet == SOCKET_ERROR) {
cout << "TCPServer::BindServerAddress() bind clinet ip&port failed: " << GetLastError() << endl;
return nRet;
}
cout << "TCPServer::BindServerAddress() bind client ip&port success!" << endl;
return nRet;
}
int TCPServer::ListenClient()
{
// 第二个参数说明: 待处理连接队列的最大长度
int nRet = listen(m_Socket, 15);
if (nRet == SOCKET_ERROR) {
cout << "TCPServer::ListenClient() listen client failed: " << GetLastError() << endl;
return nRet;
}
cout << "TCPServer::ListenClient() listen client success!" << endl;
return nRet;
}
SOCKET TCPServer::WaitAccept()
{
SOCKET nRet = INVALID_SOCKET;
while (true) {
nRet = accept(m_Socket, (sockaddr*)&m_RemoteAddress, &m_RemoteAddressLen);
if (nRet == INVALID_SOCKET) {
cout << "TCPServer::WaitAccept() accpet a error socket: " << GetLastError() << endl;
return nRet;
}
cout << "TCPServer::WaitAccept() a new client joined serve, IP: " << inet_ntoa(m_RemoteAddress.sin_addr) << endl;
SendMsgToClient(nRet);
}
return nRet;
}
int TCPServer::SendMsgToClient(SOCKET& in_SocketClinet)
{
char RecvBuffer[1024] = { "Hello! I'm TCP Server. i had recv you msg!" };
int nRet = send(in_SocketClinet, RecvBuffer, 1024, 0);
if (nRet == -1) {
cout << "TCPServer::SendMsgToClient() send msg to clinet failed: " << GetLastError() << endl;
return nRet;
}
cout << "TCPServer::SendMsgToClient() send msg to client success!" << endl;
return nRet;
}
TCP客户端四部曲:
构建socket
连接服务器
连接成功后给服务器发送消息并接收来自服务器的响应
code:
TCPClient.hpp
#pragma once
#include <WS2tcpip.h>
#include <WinSock2.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")
class TCPClinet {
public:
TCPClinet();
~TCPClinet();
int BuildSocket();
int ConnetServer();
int RecvFromServer();
private:
SOCKET m_Socket;
int m_AddressLen;
sockaddr_in m_Address;
};
TCPClinet.cpp
#include "TCPClient.h"
using namespace std;
TCPClinet::TCPClinet()
{
m_Socket = INVALID_SOCKET;
m_AddressLen = sizeof(sockaddr_in);
memset(static_cast<void*>(&m_Address), 0x00, sizeof(sockaddr_in));
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
}
TCPClinet::~TCPClinet()
{
closesocket(m_Socket);
WSACleanup();
}
int TCPClinet::BuildSocket()
{
m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_Socket == INVALID_SOCKET) {
cout << "TCPClinet::BuildSocket() build sokcet failed: " << GetLastError() << endl;
return INVALID_SOCKET;
}
cout << "TCPClinet::BuildSocket() build socket success!" << endl;
return 0;
}
int TCPClinet::ConnetServer()
{
m_Address.sin_family = AF_INET;
m_Address.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
m_Address.sin_port = htons(8080);
int nRet = connect(m_Socket, (sockaddr*)&m_Address, m_AddressLen);
if (nRet == SOCKET_ERROR) {
cout << "TCPClinet::ConnetServer() connect server failed: " << GetLastError() << endl;
return nRet;
}
cout << "TCPClinet::ConnetServer() ocnnect Server success!" << endl;
return nRet;
}
int TCPClinet::RecvFromServer()
{
char RecvBuffer[1024] = { 0 };
int nRet = recv(m_Socket, RecvBuffer, 1024, 0);
if (nRet == -1) {
cout << "TCPClinet::RecvFromServer() recv msg from server failed: " << GetLastError() << endl;
return nRet;
}
cout << "TCPClinet::RecvFromServer() recv a msg from server: " << RecvBuffer << endl;
return nRet;
}
main.cpp
// TCP_Program.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <thread>
#include "UDPServer.h"
#include "UDPClient.h"
#include "TCPServer.h"
#include "TCPClient.h"
using namespace std;
void RunTCPServer()
{
TCPServer tcpServer = TCPServer();
tcpServer.BuildSocket();
tcpServer.BindServerAddress();
tcpServer.ListenClient();
tcpServer.WaitAccept();
}
void RunTCPClinet()
{
TCPClinet tcpClient = TCPClinet();
tcpClient.BuildSocket();
tcpClient.ConnetServer();
tcpClient.RecvFromServer();
}
int main()
{
std::thread thrd_1(RunTCPServer);
Sleep(3000);
cout << "--------------------------------------------" << endl;
std::thread thrd_2(RunTCPClinet);
cout << "--------------------------------------------" << endl;
thrd_1.join();
Sleep(1000);
thrd_2.join();
return 0;
}