【linux网络编程】套接字socket
Linux 套接字(Socket)详细介绍
1. 什么是套接字(Socket)?
套接字(Socket) 是计算机网络通信的编程接口(API),它允许不同计算机或同一计算机上的进程通过 TCP/IP 网络进行数据交换。
-
本质:一个用于 进程间通信(IPC) 的端点,是应用程序访问网络的接口,用于在不同计算机或同一计算机的进程之间通信。
-
作用:在应用程序和网络协议之间建立连接,允许数据收发。
-
特点:
- 支持本机和远程通信(同一主机进程通信或不同主机进程通信)。
- 屏蔽底层网络传输细节(应用程序不需要直接操作 TCP/IP 数据包)。
- 支持 TCP/UDP 通信(可靠的 TCP 或无连接的 UDP)。
- 支持多种协议族(IPv4、IPv6、本地通信)。
📌 套接字的地位
- 不属于传输层,而是应用程序访问传输层(TCP/UDP)的接口。
- 允许应用程序调用 TCP/UDP 来实现可靠或无连接的数据传输。
2. 套接字的基本工作原理
网络通信流程:
- 客户端创建套接字,请求连接服务器。
- 服务器创建套接字,等待客户端连接。
- 服务器接受连接,建立数据通道。
- 双方进行数据收发。
- 连接关闭。
一个完整的 TCP 连接:
客户端(192.168.1.10:50000) → 服务器(192.168.1.100:80)
请求数据 ← 响应数据
UDP 连接(无连接):
客户端(192.168.1.10:50000) → 服务器(192.168.1.100:53)
DNS 查询 ← DNS 响应
3. 套接字的基本类型
1️⃣ 按通信方式分类
套接字类型 | 说明 | 典型协议 |
---|---|---|
流式套接字(SOCK_STREAM) | 面向连接,可靠,有流量控制 | TCP |
数据报套接字(SOCK_DGRAM) | 无连接,不可靠,速度快 | UDP |
原始套接字(SOCK_RAW) | 直接访问 IP 层,常用于网络分析 | ICMP、ARP |
2️⃣ 按协议族分类
地址族 | 说明 | 适用范围 |
---|---|---|
AF_INET | IPv4 套接字 | 互联网通信 |
AF_INET6 | IPv6 套接字 | 互联网通信 |
AF_UNIX | 本地进程间通信(IPC) | 本机进程通信 |
AF_PACKET | 直接访问底层数据链路层 | 抓包、ARP |
4. 套接字编程 API
📌 套接字 API 主要函数(C 语言)
函数 | 作用 |
---|---|
socket() | 创建套接字 |
bind() | 绑定 IP 和端口 |
listen() | 监听连接(TCP 服务器端) |
accept() | 接受客户端连接 |
connect() | 连接服务器(TCP 客户端) |
send()/recv() | TCP 发送/接收数据 |
sendto()/recvfrom() | UDP 发送/接收数据 |
close() | 关闭套接字 |
5. TCP 套接字通信示例
📌 服务器端(TCP)
#include <winsock2.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib") // 链接 Winsock 库
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
// 1. 初始化 Winsock
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup 失败" << std::endl;
return 1;
}
// 2. 创建 Socket
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == INVALID_SOCKET) {
std::cerr << "Socket 创建失败" << std::endl;
WSACleanup();
return 1;
}
// 3. 绑定地址
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(PORT);
if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
std::cerr << "绑定失败" << std::endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}
// 4. 监听连接
if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
std::cerr << "监听失败" << std::endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}
std::cout << "服务器正在监听端口 " << PORT << "..." << std::endl;
// 5. 接受客户端连接
sockaddr_in clientAddr;
int clientAddrSize = sizeof(clientAddr);
SOCKET clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrSize);
if (clientSocket == INVALID_SOCKET) {
std::cerr << "接受连接失败" << std::endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}
std::cout << "客户端连接成功!" << std::endl;
// 6. 接收数据
char buffer[BUFFER_SIZE] = { 0 };
int bytesReceived = recv(clientSocket, buffer, BUFFER_SIZE, 0);
if (bytesReceived > 0) {
std::cout << "收到客户端消息: " << buffer << std::endl;
}
// 7. 发送响应
const char* response = "Hello from server!";
send(clientSocket, response, strlen(response), 0);
// 8. 关闭 Socket
closesocket(clientSocket);
closesocket(serverSocket);
WSACleanup();
return 0;
}
📌 客户端(TCP)
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib") // 链接 Winsock 库
#define SERVER_IP "127.0.0.1"
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
// 1. 初始化 Winsock
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup 失败" << std::endl;
return 1;
}
// 2. 创建 Socket
SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET) {
std::cerr << "Socket 创建失败" << std::endl;
WSACleanup();
return 1;
}
// 3. 连接服务器
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(PORT);
//serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP);
if (inet_pton(AF_INET, SERVER_IP, &serverAddr.sin_addr) <= 0) {
std::cerr << "地址转换失败\n";
closesocket(clientSocket);
WSACleanup();
return 1;
}
if (connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
std::cerr << "连接服务器失败" << std::endl;
closesocket(clientSocket);
WSACleanup();
return 1;
}
std::cout << "成功连接到服务器!" << std::endl;
// 4. 发送数据
const char* message = "Hello from client!";
send(clientSocket, message, strlen(message), 0);
// 5. 接收服务器响应
char buffer[BUFFER_SIZE] = { 0 };
int bytesReceived = recv(clientSocket, buffer, BUFFER_SIZE, 0);
if (bytesReceived > 0) {
std::cout << "收到服务器消息: " << buffer << std::endl;
}
// 6. 关闭 Socket
closesocket(clientSocket);
WSACleanup();
return 0;
}
6. UDP 套接字通信
UDP 没有连接过程,数据直接发送:
sendto()
发送数据recvfrom()
接收数据
7. 套接字通信流程
📌 TCP(面向连接)
-
服务器:
socket()
创建套接字bind()
绑定端口listen()
监听端口accept()
等待连接recv()/send()
进行通信close()
关闭连接
-
客户端:
socket()
创建套接字connect()
连接服务器send()/recv()
进行通信close()
关闭连接
📌 UDP(无连接)
-
服务器:
socket()
创建套接字bind()
绑定端口recvfrom()/sendto()
接收/发送数据close()
关闭
-
客户端:
socket()
创建套接字sendto()
发送数据recvfrom()
接收数据close()
关闭
8. 总结
- 套接字是应用程序访问网络的 API,不属于传输层。
- TCP 使用 SOCK_STREAM,UDP 使用 SOCK_DGRAM。
- 客户端通常使用动态端口,服务器使用固定端口。
socket()
是网络编程的入口,应用程序通过它访问 TCP/UDP。
📌 如果你想用 C 语言或 Python 进行网络编程,理解 socket
是最关键的一步! 🚀