目录
Socket原理
通信协议原理
TCP
UDP
代码实现
TCP
C++
python
UDP
C++
python
Socket(套接字)是计算机网络中用于实现进程间通信的一种机制,特别是在不同主机之间通过网络进行数据传输时。它是网络编程的核心概念之一,为应用程序提供了统一的接口,使得开发者可以通过网络发送和接收数据。可以将 Socket 类比为电话系统中的“电话机”。两台设备通过 Socket 建立连接后,就像两个人通过电话通话一样进行数据交换。
在网络通信中,最常用到的就是socket,可以实现不同协议,如TCP、UDP,的网络通信。一般采用客户端-服务器的工作模式。客户端是主动发送连接请求或者数据的一方,服务端是被动接收请求的一方,根据收到的客户端连接请求或数据,返回给客户端相应的数据,进行数据交换。通常包括以下步骤:
服务器端:
创建 Socket:服务器创建一个 Socket,并绑定到特定的 IP 地址和端口。
监听连接:服务器开始监听来自客户端的连接请求。
接受连接:当客户端发起连接请求时,服务器接受连接并创建一个新的 Socket 用于与客户端通信。
数据交换:通过 Socket 发送和接收数据。
关闭连接:通信完成后,关闭 Socket。
客户端:
创建 Socket:客户端创建一个 Socket。
发起连接:客户端向服务器的 IP 地址和端口发起连接请求。
数据交换:连接成功后,通过 Socket 发送和接收数据。
关闭连接:通信完成后,关闭 Socket。
TCP(传输控制协议) 和 UDP(用户数据报协议) 是两种主要的传输层协议,用于在网络中传输数据。它们在原理、特性和适用场景上有显著差异。选择 TCP 还是 UDP 取决于具体的应用需求。
TCP 是一种面向连接的协议,提供可靠的、基于字节流的通信。核心特点是可靠性,确保数据能够完整、有序地传输。在传输数据前需要建立连接(三次握手),数据传输完成之后还要断开连接(四次挥手),其大致原理图如下:(图片来自于网络)
主要特点包括:
可靠性:通过确认机制、重传机制和校验和确保数据不丢失、不重复、无错误。
有序性:通过序列号保证数据按发送顺序到达。
流量控制:通过滑动窗口机制防止发送方过快导致接收方缓冲区溢出。
拥塞控制:通过动态调整发送速率避免网络拥塞。
当然,也存在着一些缺点,比如建立和断开连接需要额外开销,延迟较高;协议复杂,占用较多系统资源等。根据这些特性,TCP更适合对数据完整性要求高的场景,即希望数据能够准确无误得到达目的地,即使花费较多的时间,占用更多的资源也没有关系。一般文件传输、网页浏览、电子邮件、数据库访问都是使用的TCP协议。
UDP 是一种无连接的协议,提供简单的、基于数据报的通信。核心特点是高效性,但不保证可靠性。传输数据前不需要建立连接,直接发送数据即可,结束也不需要断开连接。
主要特点包括:
无连接:无需建立连接,直接发送数据。
不可靠性:不保证数据是否到达、是否有序、是否重复。
高效性:协议简单,开销小,传输速度快。
支持广播和多播:可以将数据发送给多个接收方。
其缺点也很明显:不能保证数据可靠性,可能丢失、重复或乱序,也不支持流量控制和拥塞控制。因为没有拥塞控制,所以在网络拥堵的情况下,UDP也能将数据送达,但是数据的完整性就没办法保证了。根据这些特性,UDP更适合对实时性要求高的场景,即希望数据能够更快速得到达目的地,即使数据内容可能会有一些错误也没有关系。一般视频通话、在线游戏、DNS 查询都是使用的UDP协议。
代码都有详细注释,且提供了目前经常使用的c++和python两种实现方式。为了方便阅读和理解,代码仅实现了一些基本功能,可以根据实际需求,增删代码。
C++语言实现中,Windows系统和Linux系统的代码是有差别的,所以下面我将给出两个操作系统的代码版本。
Linux服务端代码:
#include
#include
#include
#include
#include
using namespace std;
int main(){
//1. 创建socket
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket < 0){ //判断是否创建成功
cout << "socket error" << endl;
return -1;
}
//2. 监听连接
// 设置需要监听的ip地址和端口
struct sockaddr_in Addr;
memset(&Addr, 0, sizeof(Addr));
Addr.sin_family = AF_INET; //IPv4
Addr.sin_port = htons(8000); //设置端口
Addr.sin_addr.s_addr = inet_addr("10.12.54.121"); //设置ip
// 绑定指定端口
if (bind(serverSocket , (struct sockaddr *)&Addr, sizeof(Addr)) < 0) {
cout << "bind error" << endl;
return -1;
}
// 监听端口
if (listen(serverSocket, 10) < 0) {
cout << "listen error" << endl;
return -1;
}else cout << "waiting" << endl;
//3. 接受连接
int clientSocket = accept(serverSocket, NULL,NULL);
//4. 数据交换
while(true){ //一直循环等待接收数据,直到客户端断开连接
// 接收客户端发来的数据
char buffer[1024] = { 0 }; //存放数据的缓存
int ret = recv(clientSocket, buffer, 1024, 0);
if (ret <= 0)break;
cout << "Message from client: " << buffer << endl;
// 返回响应给客户端
strcpy(buffer, "ok");
send(clientSocket, buffer, (int)strlen(buffer), 0);
cout << "ok message sent" << endl;
}
//5. 关闭连接
close(clientSocket);
close(serverSocket);
return 0;
}
Windows服务端代码
#include
#include
#pragma comment(lib, "ws2_32.lib") //链接Winsock库
using namespace std;
int main(){
// 初始化Winsock,开始网络权限
WSADATA wasData;
WSAStartup(MAKEWORD(2, 2), &wasData);
//1. 创建socket
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket < 0){ //判断是否创建成功
cout << "socket error" << endl;
return -1;
}
//2. 监听连接
// 设置需要监听的ip地址和端口
struct sockaddr_in Addr;
memset(&Addr, 0, sizeof(Addr));
Addr.sin_family = AF_INET; //IPv4
Addr.sin_port = htons(8080); //设置端口
Addr.sin_addr.s_addr = inet_addr("10.12.43.126"); //设置ip
// 绑定指定端口
if (bind(serverSocket , (struct sockaddr *)&Addr, sizeof(Addr)) < 0) {
cout << "bind error" << endl;
return -1;
}
// 监听端口
if (listen(serverSocket, 10) < 0) {
cout << "listen error" << endl;
return -1;
}else cout << "waiting" << endl;
//3. 接受连接
SOCKET clientSocket = accept(serverSocket, NULL, NULL);
//4. 数据交换
while(true){ //一直循环等待接收数据,直到客户端断开连接
// 接收客户端发来的数据
char buffer[1024] = { 0 }; //存放数据的缓存
int ret = recv(clientSocket, buffer, 1024, 0);
if (ret <= 0)break;
cout << "Message from client: " << buffer << endl;
// 返回响应给客户端
strcpy(buffer, "ok");
send(clientSocket, buffer, (int)strlen(buffer), 0);
cout << "ok message sent" << endl;
}
//5. 关闭连接
closesocket(clientSocket);
closesocket(serverSocket);
return 0;
}
Linux客户端代码:
#include
#include
#include
#include
#include
using namespace std;
int main(){
//1. 创建socket
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket < 0){ //判断是否创建成功
cout << "socket error" << endl;
return -1;
}
//2. 发起连接
// 设置服务器的ip地址和端口
struct sockaddr_in Addr;
memset(&Addr, 0, sizeof(Addr));
Addr.sin_family = AF_INET; //IPv4
Addr.sin_port = htons(8000); //设置端口
Addr.sin_addr.s_addr = inet_addr("10.12.54.121"); //设置ip
// 给服务器发送连接请求
if (connect(clientSocket, (struct sockaddr*)&Addr, sizeof(Addr)) < 0) {
cout << "connect error" << endl;
close(clientSocket);
return -1;
}
//3. 数据交换
int i=0;
while(i<10){ //发送10次数据后就退出
// 给服务端发送数据
char buffer[1024] = { 0 }; //存放数据的缓存
strcpy(buffer, "hello");
send(clientSocket, buffer, (int)strlen(buffer), 0);
// 接收服务端的响应数据
int ret = recv(clientSocket, buffer, 1024, 0);
if (ret <= 0)break;
cout << "Message from client: " << buffer << endl;
i++;
}
//4. 关闭连接
close(clientSocket);
return 0;
}
Windows客户端代码:
#include
#include
#pragma comment(lib, "ws2_32.lib") //链接Winsock库
using namespace std;
int main(){
// 初始化Winsock,开始网络权限
WSADATA wasData;
WSAStartup(MAKEWORD(2, 2), &wasData);
//1. 创建socket
SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket < 0){ //判断是否创建成功
cout << "socket error" << endl;
return -1;
}
//2. 发起连接
// 设置服务器的ip地址和端口
struct sockaddr_in Addr;
memset(&Addr, 0, sizeof(Addr));
Addr.sin_family = AF_INET; //IPv4
Addr.sin_port = htons(8080); //设置端口
Addr.sin_addr.s_addr = inet_addr("10.12.43.126"); //设置ip
// 给服务器发送连接请求
if (connect(clientSocket, (struct sockaddr*)&Addr, sizeof(Addr)) < 0) {
cout << "connect error" << endl;
closesocket(clientSocket);
return -1;
}
//3. 数据交换
int i=0;
while(i<10){ //发送10次数据后就退出
// 给服务端发送数据
char buffer[1024] = { 0 }; //存放数据的缓存
strcpy(buffer, "hello");
send(clientSocket, buffer, (int)strlen(buffer), 0);
// 接收服务端的响应数据
int ret = recv(clientSocket, buffer, 1024, 0);
if (ret <= 0)break;
cout << "Message from client: " << buffer << endl;
i++;
}
//4. 关闭连接
closesocket(clientSocket);
return 0;
}
服务端代码:
import socket
#1. 创建Socket
socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#2. 监听连接
#绑定端口
ip = "10.12.43.126"
port = 8080
socket_server.bind((ip, port))
#开始监听
socket_server.listen()
print("waiting at "+ip+":"+str(port))
#3. 接受连接
client_socket, client_address = socket_server.accept()
print('connect:', client_address)
#4. 数据交换
while True: #一直等待客户端发送数据,直到断开连接
# 接收客户端发来的数据
receive = client_socket.recv(1024)
if not receive:
break
print("receive:"+receive.decode())
# 响应客户端数据
re = "ok"
client_socket.send(re.encode())
#5. 关闭连接
client_socket.close()
socket_server.close()
客户端代码:
import socket
#1. 创建Socket
socket_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#2. 发起连接
ip = "10.12.43.126"
port = 8080
socket_client.connect((ip,port))
print("connect"+ip+":"+str(port))
#3. 数据交换
i=0
while i<10: #发送10次数据后就退出
# 给服务端发送数据
message = "hello"
socket_client.send(message.encode())
# 接收服务端的响应数据
response = socket_client.recv(1024)
print("receive:"+ response.decode())
i=i+1
#4. 关闭连接
socket_client.close()
Linux服务端代码:
#include
#include
#include
#include
#include
using namespace std;
int main(){
//1. 创建socket
int serverSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (serverSocket < 0){ //判断是否创建成功
cout << "socket error" << endl;
return -1;
}
//2. 监听数据接收端口
// 设置需要监听的ip地址和端口
struct sockaddr_in Addr;
memset(&Addr, 0, sizeof(Addr));
Addr.sin_family = AF_INET; //IPv4
Addr.sin_port = htons(8080); //设置端口
Addr.sin_addr.s_addr = inet_addr("10.12.54.121"); //设置ip
// 绑定指定端口
if (bind(serverSocket , (struct sockaddr *)&Addr, sizeof(Addr)) < 0) {
cout << "bind error" << endl;
return -1;
}else cout << "waiting at IP: " << inet_ntoa(Addr.sin_addr)<< " Port: " << ntohs(Addr.sin_port) << endl;
//3. 数据交换
while(true){ //一直循环等待接收数据
// 接收客户端发来的数据
char buffer[1024] = { 0 }; //存放数据的缓存
struct sockaddr_in client_addr; //记录客户端的IP地址
socklen_t client_len = sizeof(client_addr);
int ret = recvfrom(serverSocket, buffer, 1024, 0, (struct sockaddr *) &client_addr, &client_len);
if (ret <= 0)break;
cout << "Message from client: " << buffer << endl;
// 返回响应给客户端
strcpy(buffer, "ok");
sendto(serverSocket, buffer, sizeof(buffer), 0, (struct sockaddr *)&client_addr, client_len);
cout << "ok message sent" << endl;
}
//4. 关闭连接
close(serverSocket);
return 0;
}
Windows服务端代码:
#include
#include
#pragma comment(lib, "ws2_32.lib") //链接Winsock库
using namespace std;
int main(){
// 初始化Winsock,开始网络权限
WSADATA wasData;
WSAStartup(MAKEWORD(2, 2), &wasData);
//1. 创建socket
SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (serverSocket < 0){ //判断是否创建成功
cout << "socket error" << endl;
return -1;
}
//2. 监听数据接收端口
// 设置需要监听的ip地址和端口
struct sockaddr_in Addr;
memset(&Addr, 0, sizeof(Addr));
Addr.sin_family = AF_INET; //IPv4
Addr.sin_port = htons(8080); //设置端口
Addr.sin_addr.s_addr = inet_addr("10.12.43.126"); //设置ip
// 绑定指定端口
if (bind(serverSocket , (struct sockaddr *)&Addr, sizeof(Addr)) < 0) {
cout << "bind error" << endl;
return -1;
}else cout << "waiting at IP: " << inet_ntoa(Addr.sin_addr)<< " Port: " << ntohs(Addr.sin_port) << endl;
//3. 数据交换
while(true){ //一直循环等待接收数据
// 接收客户端发来的数据
char buffer[1024] = { 0 }; //存放数据的缓存
struct sockaddr_in clientAddr; //记录客户端的IP地址
int clientAddrLen = sizeof(clientAddr);
memset(&clientAddr, 0, sizeof(clientAddr));
int ret = recvfrom(serverSocket, buffer, 1024, 0, (struct sockaddr*)&clientAddr, &clientAddrLen); //直接接收数据,无需建立连接
if (ret <= 0)break;
cout << "Message from client: " << buffer << endl;
// 返回响应给客户端
strcpy(buffer, "ok");
sendto(serverSocket, buffer, 1024, 0, (struct sockaddr*)&clientAddr, clientAddrLen);
cout << "ok message sent" << endl;
}
//4. 关闭连接
closesocket(serverSocket);
return 0;
}
Linux客户端代码:
#include
#include
#include
#include
#include
using namespace std;
int main(){
//1. 创建socket
int clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (clientSocket < 0){ //判断是否创建成功
cout << "socket error" << endl;
return -1;
}
//2. 数据交换
// 设置服务器的ip地址和端口
struct sockaddr_in Addr;
memset(&Addr, 0, sizeof(Addr));
Addr.sin_family = AF_INET; //IPv4
Addr.sin_port = htons(8080); //设置端口
Addr.sin_addr.s_addr = inet_addr("10.12.54.121"); //设置ip
// 发送和接收数据
while(true){
// 给服务端发送数据
string message;
cout << "Enter message: ";
getline(cin, message); //手动输入要发送的数据
sendto(clientSocket,message.c_str(), message.size(), 0, (struct sockaddr*)&Addr, sizeof(Addr)); //直接发送数据,无需建立连接
// 接收服务端的响应数据
char buffer[1024] = { 0 };;
socklen_t AddrSize = sizeof(Addr);
int ret = recvfrom(clientSocket, buffer, sizeof(buffer), 0, (struct sockaddr*)&Addr, &AddrSize);
if (ret <= 0)break;
cout << "Message from server: " << buffer << endl;
}
//3. 关闭连接
close(clientSocket);
return 0;
}
Windows客户端代码:
#include
#include
#pragma comment(lib, "ws2_32.lib") //链接Winsock库
using namespace std;
int main(){
// 初始化Winsock,开始网络权限
WSADATA wasData;
WSAStartup(MAKEWORD(2, 2), &wasData);
//1. 创建socket
SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (clientSocket < 0){ //判断是否创建成功
cout << "socket error" << endl;
return -1;
}
//2. 数据交换
// 设置服务器的ip地址和端口
struct sockaddr_in Addr;
memset(&Addr, 0, sizeof(Addr));
Addr.sin_family = AF_INET; //IPv4
Addr.sin_port = htons(8080); //设置端口
Addr.sin_addr.s_addr = inet_addr("10.12.43.126"); //设置ip
// 发送和接收数据
while(true){
// 给服务端发送数据
string message;
cout << "Enter message: ";
getline(cin, message); //手动输入要发送的数据
sendto(clientSocket,message.c_str(), message.size(), 0, (struct sockaddr*)&Addr, sizeof(Addr)); //直接发送数据,无需建立连接
// 接收服务端的响应数据
char buffer[1024] = { 0 };;
int AddrSize = sizeof(Addr);
int ret = recvfrom(clientSocket, buffer, 1024, 0, (struct sockaddr*)&Addr, &AddrSize);
if (ret <= 0)break;
cout << "Message from server: " << buffer << endl;
}
//3. 关闭连接
closesocket(clientSocket);
return 0;
}
服务端代码:
import socket
#1. 创建Socket
socket_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#2. 监听数据接收端口
#绑定端口
ip = "10.12.43.126"
port = 8080
socket_server.bind((ip, port))
#3. 数据交换
while True: #一直等待客户端发送数据
# 接收客户端发来的数据
receive,addr = socket_server.recvfrom(1024)
if not receive:
break
print("receive:"+receive.decode())
# 响应客户端数据
re = "ok"
socket_server.sendto(re.encode(),addr)
#4. 关闭连接
socket_server.close()
客户端代码:
import socket
#1. 创建Socket
socket_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#2. 数据交换
# 设置服务器的ip地址和端口
ip = "10.12.43.126"
port = 8080
# 发送和接收数据
while True:
# 给服务端发送数据
message = input("Enter message: ")
socket_client.sendto(message.encode(),(ip,port))
# 接收服务端的响应数据
response,_ = socket_client.recvfrom(1024)
print("receive:"+ response.decode())
#3. 关闭连接
socket_client.close()