一、前言
本章主要总结 C++ windows 下tcp和 udp通信的一些要点
二、原理
可参考两张简单的通信流程图
(1)tcp的通信过程
(2)UDP的通信过程
三、TCP代码
服务端
//sever_tcp.cpp
//导入模块
#include <string>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <WinSock2.h>
#include <Windows.h>
#include <WS2tcpip.h>
//加载lib
#pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll
//使用命名空间
using namespace std;
int main() {
//初始化 DLL
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//绑定套接字
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET; //使用IPv4地址
//sockAddr.sin_addr.s_addr = inet_addr("192.168.101.7"); //具体的IP地址
sockAddr.sin_port = htons(1234); //端口
inet_pton(sockAddr.sin_family, "127.0.0.1", &sockAddr.sin_addr.s_addr);
bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//进入监听状态
listen(servSock, 20);
//接收客户端请求
SOCKADDR clntAddr;
int nSize = sizeof(SOCKADDR);
SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);
while (true)
{
printf("输入要发送的数据(要退出输入exit):");
//向客户端发送数据
string str;
getline(cin, str);
if (str != "exit") send(clntSock, ("k" + str + "k").c_str(), str.size() + sizeof(char), NULL);
else
{
send(clntSock, "exit", 4 + sizeof(char), NULL);
break;
}
}
printf("成功退出!\n");
//关闭套接字
closesocket(clntSock);
closesocket(servSock);
//终止 DLL 的使用
WSACleanup();
return 0;
}
客户端
//client_tcp.cpp
//导入模块
#include <string>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <WinSock2.h>
#include <Windows.h>
#include <WS2tcpip.h>
//加载lib
#pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll
//使用命名空间
using namespace std;
int main() {
//初始化DLL
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//向服务器发起请求
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET;
//sockAddr.sin_addr.s_addr = inet_addr("192.168.101.7");
sockAddr.sin_port = htons(1234);
inet_pton(sockAddr.sin_family, "127.0.0.1", &sockAddr.sin_addr.s_addr);
connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
while (true)
{
//接收服务器传回的数据
char szBuffer[MAXBYTE] = { 0 };
int can = recv(sock, szBuffer, MAXBYTE, NULL);
string buffer = szBuffer;
if (buffer == "exit")
{
printf("对方关闭了Socket。\n");
//关闭套接字
closesocket(sock);
//终止使用 DLL
WSACleanup();
system("pause");
return 0;
}
//输出接收到的数据
if (can > 0) {
printf("从server接收到的数据: %s\n", buffer.size() > 2 ? buffer.substr(1, buffer.size() - 1).c_str() : "");
}
}
return 0;
}
运行结果
四、UDP代码
服务端,相比较于TCP ,UDP客户端无需 connect ,服务端也无需accept ,socket套接字初始化,TCP的是 SOCK_STREAM 而UDP的是 SOCK_DGRAM,UDP接收和发送用的是recvfrom 和 sendto , TCP用的是recv和send
//sever_udp.cpp
//导入模块
#include <string>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <WinSock2.h>
#include <Windows.h>
#include <WS2tcpip.h>
//加载lib
#pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll
//使用命名空间
using namespace std;
int main() {
//初始化 DLL
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET servSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); // TCP是 SOCK_STREAM udp是 SOCK_DGRAM,不一样
//绑定套接字
sockaddr clientAddr;
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET; //使用IPv4地址
//sockAddr.sin_addr.s_addr = inet_addr("192.168.101.7"); //具体的IP地址
sockAddr.sin_port = htons(1314); //端口
inet_pton(sockAddr.sin_family, "127.0.0.1", &sockAddr.sin_addr.s_addr);
if(-1 == bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)) )
{
printf("bind error!\n");
//exit(1);
}
//进入监听状态
//listen(servSock, 20); //udp不用 listen
//接收客户端请求
SOCKADDR clntAddr;
int nSize = sizeof(SOCKADDR);
//SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize); UDP的服务端不用accept ,客户端不用 connect
while (true)
{
char szBuffer[MAXBYTE] = { 0 };
int str_len = recvfrom(servSock, szBuffer, MAXBYTE, 0, &clientAddr, &nSize);
if (str_len < 0) continue;
printf("从server接收到的数据: %s\n", szBuffer);
printf("输入要发送的数据(要退出输入exit):");
//向客户端发送数据
string str;
getline(cin, str);
if (str != "exit")
//send(clntSock, ("k" + str + "k").c_str(), str.size() + sizeof(char), NULL);
sendto(servSock, ("k" + str + "k").c_str(), str.size(), 0, &clientAddr, sizeof(SOCKADDR) );
else
{
sendto(servSock, "exit", 4 + sizeof(char), 0, &clientAddr, sizeof(SOCKADDR));
break;
}
}
printf("成功退出!\n");
//关闭套接字
closesocket(servSock);
//终止 DLL 的使用
WSACleanup();
return 0;
}
//client_udp.cpp
//导入模块
#include <string>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <WinSock2.h>
#include <Windows.h>
#include <WS2tcpip.h>
//加载lib
#pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll
//使用命名空间
using namespace std;
int main() {
//初始化DLL
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);// TCP是 SOCK_STREAM udp是 SOCK_DGRAM,不一样
//向服务器发起请求
sockaddr serverAddr;
int serverAddrLen = 0;
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET;
//sockAddr.sin_addr.s_addr = inet_addr("192.168.101.7");
sockAddr.sin_port = htons(1314);
inet_pton(sockAddr.sin_family, "127.0.0.1", &sockAddr.sin_addr.s_addr);
//connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); UDP是不用先connect服务端的
serverAddrLen = sizeof(serverAddr);
while (true)
{
//接收服务器传回的数据
char szBuffer[MAXBYTE] = { 0 };
//int can = recv(sock, szBuffer, MAXBYTE, NULL);
printf("请输入一个字符串,发送给服务端:");
string str;
getline(cin, str);
sendto(sock, ("k" + str + "k").c_str(), str.size(), 0, (struct sockaddr*)&sockAddr, sizeof(sockAddr));
int can = recvfrom(sock, szBuffer, MAXBYTE, 0, &serverAddr, &serverAddrLen );
string buffer = szBuffer;
if (buffer == "exit")
{
printf("对方关闭了Socket。\n");
//关闭套接字
closesocket(sock);
//终止使用 DLL
WSACleanup();
system("pause");
return 0;
}
//输出接收到的数据
if (can > 0) {
printf("从server接收到的数据: %s\n", buffer.size() > 2 ? buffer.substr(1, buffer.size() - 1).c_str() : "");
memset(szBuffer, 0, MAXBYTE ); // 重置缓冲区
}
}
return 0;
}
效果图