### 3.1 套接字接口编程概述
套接字(Socket)是网络编程的基础,用于不同主机上的应用程序之间进行数据交换。它提供了一个统一的接口,使得开发者可以基于标准的系统调用构建网络通信程序。套接字的概念最早由 Unix 系统引入,并逐渐成为现代网络编程的核心机制之一[^1]。
在编程中,套接字接口提供了多种基本操作,包括创建、绑定、连接、监听、发送和接收数据等。这些操作构成了网络通信的核心流程,适用于 TCP 和 UDP 协议。例如,TCP 通信通常涉及服务器端的监听和客户端的连接,而 UDP 通信则更偏向于无连接的数据报传输[^2]。
### 3.2 套接字基本操作与函数调用
创建套接字通常使用 `socket()` 系统调用,该函数返回一个描述符,后续操作均基于该描述符进行。创建成功后,若为服务器端程序,需调用 `bind()` 将套接字绑定到特定的 IP 地址和端口,以接收来自客户端的连接请求。对于 TCP 服务,还需调用 `listen()` 启动监听模式,并通过 `accept()` 接收客户端连接请求,建立新的通信套接字。客户端则通过 `connect()` 主动发起连接。
在数据传输阶段,TCP 使用 `send()` 和 `recv()` 进行流式数据发送与接收,而 UDP 则使用 `sendto()` 和 `recvfrom()`,因为其通信是无连接的,每次发送或接收都需要指定目标或来源地址[^3]。
### 3.3 套接字地址结构与通用接口设计
套接字函数如 `connect()`、`bind()` 和 `accept()` 都需要一个指向特定协议地址结构的指针。为了实现接口的通用性,C 语言中采用 `struct sockaddr` 作为通用结构体指针,应用程序需将具体协议的地址结构强制转换为该类型。例如,IPv4 地址使用 `struct sockaddr_in`,而 IPv6 使用 `struct sockaddr_in6`,它们都可被转换为 `struct sockaddr` 传入系统调用中[^5]。
绑定地址时,`bind()` 函数需要三个参数:套接字描述符、地址结构指针和结构体长度。若绑定失败,函数返回 -1,并可通过 `errno` 获取错误信息[^4]。
### 3.4 TCP 通信编程示例
以下是一个基于 C 语言的 TCP 服务器端与客户端通信的简单示例:
#### TCP 服务器端代码
```c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[1024] = {0};
const char *response = "Hello from server";
// 创建套接字
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("Socket creation failed");
return -1;
}
// 绑定地址和端口
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("Bind failed");
return -1;
}
// 监听连接
listen(server_fd, 3);
printf("Server listening on port 8080\n");
// 接受连接
new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
if (new_socket < 0) {
perror("Accept failed");
return -1;
}
// 接收数据
int valread = read(new_socket, buffer, 1024);
printf("Received: %s\n", buffer);
// 发送响应
send(new_socket, response, strlen(response), 0);
printf("Response sent\n");
// 关闭套接字
close(new_socket);
close(server_fd);
return 0;
}
```
#### TCP 客户端代码
```c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char buffer[1024] = {0};
const char *message = "Hello from client";
// 创建套接字
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
perror("Socket creation failed");
return -1;
}
// 设置服务器地址
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// 连接服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Connection failed");
return -1;
}
// 发送数据
send(sock, message, strlen(message), 0);
printf("Message sent\n");
// 接收响应
int valread = read(sock, buffer, 1024);
printf("Received: %s\n", buffer);
// 关闭套接字
close(sock);
return 0;
}
```
### 3.5 UDP 通信编程示例
UDP 是无连接协议,因此其编程流程相对简单,无需建立连接即可发送和接收数据。
#### UDP 服务器端代码
```c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main() {
int sockfd;
struct sockaddr_in servaddr, cliaddr;
socklen_t len = sizeof(cliaddr);
char buffer[1024];
// 创建套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("Socket creation failed");
return -1;
}
// 绑定地址
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(8080);
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("Bind failed");
return -1;
}
// 接收数据
int n = recvfrom(sockfd, (char *)buffer, sizeof(buffer), 0, (struct sockaddr *)&cliaddr, &len);
buffer[n] = '\0';
printf("Received: %s\n", buffer);
// 发送响应
const char *response = "Hello from UDP server";
sendto(sockfd, response, strlen(response), 0, (const struct sockaddr *)&cliaddr, len);
printf("Response sent\n");
close(sockfd);
return 0;
}
```
#### UDP 客户端代码
```c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main() {
int sockfd;
struct sockaddr_in servaddr;
char buffer[1024];
// 创建套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("Socket creation failed");
return -1;
}
// 设置服务器地址
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8080);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
// 发送数据
const char *message = "Hello from UDP client";
sendto(sockfd, message, strlen(message), 0, (const struct sockaddr *)&servaddr, sizeof(servaddr));
printf("Message sent\n");
// 接收响应
socklen_t len = sizeof(servaddr);
int n = recvfrom(sockfd, (char *)buffer, sizeof(buffer), 0, (struct sockaddr *)&servaddr, &len);
buffer[n] = '\0';
printf("Received: %s\n", buffer);
close(sockfd);
return 0;
}
```
###