C/C++ Socket - TCP 与 UDP 网络编程
Posted cpp_learners
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++ Socket - TCP 与 UDP 网络编程相关的知识,希望对你有一定的参考价值。
前言
socket编程分为TCP和UDP两个模块,其中TCP是可靠的、安全的,常用于发送文件等,而UDP是不可靠的、不安全的,常用作视频通话等。
如下图:
头文件与库:
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
准备工作:
创建工程后,首先右键工程,选择属性
然后选择 C/C++ - 预处理器 - 预处理器定义
将字符串 _WINSOCK_DEPRECATED_NO_WARNINGS 添加到里面去,点击应用即可!
TCP
连接过程图:
创建tcp服务器和客户端都是按照上图的步骤来操作的!
1). 服务器
-
初始化套接字库
对应图中socket()WORD wVersion; WSADATA wsaData; int err; // 设置版本,可以理解为1.1 wVersion = MAKEWORD(1, 1); // 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来 // 启动 err = WSAStartup(wVersion, &wsaData);
-
创建tcp套接字
对应图中socket()// AF_INET:ipv4 AF_INET6:ipv6 SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
-
绑定到本机
对应图中bind()// 准备绑定信息 SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 设置绑定网卡 addrSrv.sin_family = AF_INET; // 设置绑定网络模式 addrSrv.sin_port = htons(6000); // 设置绑定端口 // hton: host to network x86:小端 网络传输:htons大端 // 绑定到本机 int retVal = bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
-
监听
对应图中listen()// 同时能接收10个链接,主要看参数二的设置个数 listen(sockSrv, 10);
-
接收连接请求,返回针对客户端的套接字
对应图中accept()SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrCli, &len);
-
发送数据
对应图中write()sprintf_s(sendBuf, "hello client!\\n"); int iSend = send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);
-
接收数据
对应图中read()recv(sockConn, recvBuf, 100, 0);
-
关闭套接字
对应图中close()closesocket(sockConn);
-
清理套接字库
WSACleanup();
具体实现代码:
#include <iostream>
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
int main(void)
// 1.初始化套接字库
WORD wVersion;
WSADATA wsaData;
int err;
// 设置版本,可以理解为1.1
wVersion = MAKEWORD(1, 1); // 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来
// 启动
err = WSAStartup(wVersion, &wsaData);
if (err != 0)
return err;
// 检查:网络低位不等于1 || 网络高位不等于1
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
// 清理套接字库
WSACleanup();
return -1;
// 2.创建tcp套接字 // AF_INET:ipv4 AF_INET6:ipv6
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
// 准备绑定信息
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 设置绑定网卡
addrSrv.sin_family = AF_INET; // 设置绑定网络模式
addrSrv.sin_port = htons(6000); // 设置绑定端口
// hton: host to network x86:小端 网络传输:htons大端
// 3.绑定到本机
int retVal = bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
if (retVal == SOCKET_ERROR)
printf("Failed bind:%d\\n", WSAGetLastError());
return -1;
// 4.监听,同时能接收10个链接
if (listen(sockSrv, 10) == SOCKET_ERROR)
printf("Listen failed:%d", WSAGetLastError());
return -1;
std::cout << "Server start at port: 6000" << std::endl;
SOCKADDR_IN addrCli;
int len = sizeof(SOCKADDR);
char recvBuf[100];
char sendBuf[100];
while (1)
// 5.接收连接请求,返回针对客户端的套接字
SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrCli, &len);
if (sockConn == SOCKET_ERROR)
//printf("Accept failed:%d", WSAGetLastError());
std::cout << "Accept failed: " << WSAGetLastError() << std::endl;
break;
//printf("Accept client IP:[%s]\\n", inet_ntoa(addrCli.sin_addr));
std::cout << "Accept client IP: " << inet_ntoa(addrCli.sin_addr) << std::endl;
// 6.发送数据
sprintf_s(sendBuf, "hello client!\\n");
int iSend = send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);
if (iSend == SOCKET_ERROR)
std::cout << "send failed!\\n";
break;
// 7.接收数据
recv(sockConn, recvBuf, 100, 0);
std::cout << recvBuf << std::endl;
// 关闭套接字
closesocket(sockConn);
// 8.关闭套接字
closesocket(sockSrv);
// 9.清理套接字库
WSACleanup();
return 0;
2). 客户端
-
初始化套接字库
对应图中socket()WORD wVersion; WSADATA wsaData; int err; // 可以理解为1.1 wVersion = MAKEWORD(1, 1); // 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来 // 启动 err = WSAStartup(wVersion, &wsaData); // 创建TCP套接字 SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
-
连接服务器
对应图中connect()// 连接服务器 int err_log = connect(sockCli, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
-
发送数据到服务器
对应图中write()char sendBuf[] = "你好,服务器,我是客户端!"; send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);
-
接收服务器的数据
对应图中read()char recvBuf[100]; recv(sockCli, recvBuf, sizeof(recvBuf), 0);
-
关闭套接字并清除套接字库
对应图中close()closesocket(sockCli); WSACleanup();
具体实现代码:
#include <iostream>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
int main(void)
// 1.初始化套接字库
WORD wVersion;
WSADATA wsaData;
int err;
// 可以理解为1.1
wVersion = MAKEWORD(1, 1); // 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来
// 启动
err = WSAStartup(wVersion, &wsaData);
if (err != 0)
return err;
// 检查:网络地位不等于1 || 网络高位不等于1
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
// 清理套接字库
WSACleanup();
return -1;
// 创建TCP套接字
SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 服务器地址
addrSrv.sin_port = htons(6000); // 端口号
addrSrv.sin_family = AF_INET; // 地址类型(ipv4)
// 2.连接服务器
int err_log = connect(sockCli, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
if (err_log == 0)
printf("连接服务器成功!\\n");
else
printf("连接服务器失败!\\n");
return -1;
char recvBuf[100];
char sendBuf[] = "你好,服务器,我是客户端!";
// 3.发送数据到服务器
send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);
// 4.接收服务器的数据
recv(sockCli, recvBuf, sizeof(recvBuf), 0);
std::cout << recvBuf << std::endl;
// 5.关闭套接字并清除套接字库
closesocket(sockCli);
WSACleanup();
system("pause");
return 0;
运行效果:
3). TCP聊天小项目
下面是根据上面的代码修改的一个聊天小项目(使用到了多线程)
只有一个服务器,服务器一直开启等待客户端连接;
客户都安可以开启多个,且可以一直连续的与服务器进行发送接收消息;
服务器给客户端发送数据,得通过1 - 9来区分到底给那个客户端发送消息,例如给第二个客户端发送消息:2你好,客户端
客户端那边接收到的数据是:你好,客户端
服务器代码:
#include <iostream>
#include <WinSock2.h>
#include <stdio.h>
#include <Windows.h>
#include <process.h>
#include <vector>
#include <conio.h>
#include <string.h>
#include <string>
#pragma comment(lib, "ws2_32.lib")
SOCKET sockSrv;
std::vector<SOCKET> vec_sockConn;
std::vector<SOCKADDR_IN> vec_sockaddr_in;
std::vector<int> vec_sockIndex;
// 这个结构体用作线程参数
typedef struct SERVER_CLIENT
SOCKET server;
SOCKADDR_IN client;
int clientIndex;
SC;
// 判断有没有断开连接
bool IsSocketClosed(SOCKET clientSocket)
bool ret = false;
HANDLE closeEvent = WSACreateEvent();
WSAEventSelect(clientSocket, closeEvent, FD_CLOSE);
DWORD dwRet = WaitForSingleObject(closeEvent, 0);
if (dwRet == WSA_WAIT_EVENT_0)
ret = true;
else if (dwRet == WSA_WAIT_TIMEOUT)
ret = false;
WSACloseEvent(closeEvent);
return ret;
// 接收请求
unsigned int WINAPI ThreadAccept(LPVOID p)
static int i = 0;
while (1)
SOCKADDR_IN addrCli;
int len = sizeof(SOCKADDR);
// 5.接收连接请求,返回针对客户端的套接字
SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrCli, &len);
if (sockConn == SOCKET_ERROR)
printf("Accept failed:%d", WSAGetLastError());
// 存储当前服务器与客户端 连接绑定的socket
vec_sockIndex.emplace_back(i++);
vec_sockaddr_in.emplace_back(addrCli);
vec_sockConn.emplace_back(sockConn);
printf("\\033[0;%d;40m客户端[%d]上线\\033[0m\\n", 31, i);
return 0;
unsigned int WINAPI _ThreadRecv(LPVOID p)
char recvBuf[100];
memset(recvBuf, 0, 100);
SC _sc = *(SC *)p;
while (1)
Sleep(20);
if (IsSocketClosed(_sc.server) == true)
printf("客户端 [%d] 断开连接!\\n", _sc.clientIndex + 1);
break;
// 接收数据
recv(_sc.server, recvBuf, 100, 0);
if (strlen(recvBuf) == 0)
continue;
printf("接收到客户端 [%d] 的消息:%s\\n", _sc.clientIndex + 1, recvBuf);
memset(recvBuf, 0, 100);
return 0;
unsigned int WINAPI ThreadRecv(LPVOID p)
static int index = 0;
while (1)
// 还没有客户端与服务器进行连接
if (vec_sockConn.size() == 0)
continue;
// 接收线程已经开启和客户端个数相等
if (vec_sockConn.size() == index)
continue;
SC sc;
sc.server = vec_sockConn.at(index);
sc.client = vec_sockaddr_in.at(index);
sc.clientIndex = vec_sockIndex.at(index);
HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, _ThreadRecv, (void *)&sc, 0, NULL);
index++;
Sleep(20);
return 0;
int main(void)
// 1.初始化套接字库
WORD wVersion;
WSADATA wsaData;
int err;
// 设置版本,可以理解为1.1
wVersion = MAKEWORD(1, 1); // 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来
// 启动
err = WSAStartup(wVersion, &wsaData);
if (err != 0)
return err;
// 检查:网络低位不等于1 || 网络高位不等于1
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
// 清理套接字库
WSACleanup();
return -1;
// 2.创建tcp套接字 // AF_INET:ipv4 AF_INET6:ipv6
sockSrv = socket(AF_INET, SOCK_STREAM, 0);
// 准备绑定信息
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 设置绑定网卡
addrSrv.sin_family = AF_INET; // 设置绑定网络模式
addrSrv.sin_port = htons(6000); // 设置绑定端口
// hton: host to network x86:小端 网络传输:htons大端
// 3.绑定到本机
int retVal = bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
if (retVal == SOCKET_ERROR)
printf("Failed bind:%d\\n", WSAGetLastError());
return -1;
// 4.监听,同时接收10个链接
if (listen(sockSrv, 10) == SOCKET_ERROR)
printf("Listen failed:%d", WSAGetLastError以上是关于C/C++ Socket - TCP 与 UDP 网络编程的主要内容,如果未能解决你的问题,请参考以下文章
高性能 TCP/UDP/HTTP 通信框架 HP-Socket v4.1.2
高性能 TCP/UDP/HTTP 通信框架 HP-Socket v4.0.1 发布