` 本文将介绍如何使用C语言和socket进行本地进程间通信、网络设备间通信,并详细介绍socket的用法及属性
例如:
openAI 的 GPT 大模型的发展历程。
socket()
功能:创建一个新的套接字。
C语言函数原型:
int socket(int domain, int type, int protocol);
Socket套接字在编程中是一个非常重要的概念,它提供了一套丰富的接口用于网络通信和进程间通信。以下是Socket套接字的一些常见属性:
域(Domain):
指定套接字通信中使用的网络介质。最常见的套接字域是AF_INET(IPv4)和AF_INET6(IPv6),它们分别代表Internet网络。此外,还有AF_UNIX(或AF_LOCAL),用于在同一台机器上的不同进程间进行通信。
类型(Type):
指定套接字通信的类型。常见的类型包括:
SOCK_STREAM:提供面向连接、可靠的数据传输服务。这通常基于TCP协议。
SOCK_DGRAM:提供无连接的服务,数据传输的可靠性得不到保证。这通常基于UDP协议。
SOCK_RAW:允许直接读写内核未处理的IP数据包,提供了对底层网络协议的访问。
协议(Protocol):
指定套接字通信中使用的具体协议。对于SOCK_STREAM类型的套接字,默认使用TCP协议(IPPROTO_TCP);对于SOCK_DGRAM类型的套接字,默认使用UDP协议(IPPROTO_UDP)。对于SOCK_RAW类型的套接字,可以指定具体的IP协议。
套接字描述符(Socket Descriptor):
创建套接字时返回的一个整数,用于标识这个套接字。后续的套接字操作(如绑定、监听、接受连接、发送和接收数据等)都需要通过这个描述符来进行。
绑定地址和端口(Bound Address and Port):
服务器端套接字通常需要使用bind函数绑定到一个具体的IP地址和端口号上,以便客户端可以连接到它。客户端套接字在连接到服务器时,也会绑定到一个本地的临时端口上。
连接状态(Connection State):
对于面向连接的套接字(如SOCK_STREAM),有连接建立、数据传输和连接关闭等状态。对于无连接的套接字(如SOCK_DGRAM),则没有明确的连接状态。
输入输出缓冲区(Input/Output Buffers):
每个套接字都有自己的输入输出缓冲区,用于暂存发送和接收的数据。这些缓冲区的大小可以通过setsockopt和getsockopt函数来设置和获取。
套接字选项(Socket Options):
通过setsockopt和getsockopt函数可以设置和获取套接字的各种选项,这些选项可以控制套接字的行为。例如,SO_REUSEADDR选项允许在同一端口上启动多个服务器实例,SO_RCVBUF和SO_SNDBUF选项可以设置接收和发送缓冲区的大小。
阻塞与非阻塞模式(Blocking and Non-blocking Mode):
套接字可以设置为阻塞模式或非阻塞模式。在阻塞模式下,套接字操作(如连接、发送和接收数据)会等待操作完成或发生错误才返回。在非阻塞模式下,这些操作会立即返回,如果操作不能立即完成,则可能返回一个错误代码。
超时设置(Timeout Settings):
可以通过套接字选项设置发送和接收操作的超时时间。如果在指定的时间内操作没有完成,则操作会超时并返回一个错误代码。
带外数据(Out-of-Band Data):
某些类型的套接字(如SOCK_STREAM)支持带外数据传输,这允许发送紧急数据,这些数据会绕过正常的数据队列被立即处理。
多播和广播(Multicast and Broadcast):
套接字可以设置为支持多播和广播通信。多播允许数据同时发送给多个接收者,而广播则允许数据发送给同一网络上的所有接收者。
这些属性共同定义了套接字的行为和特性,使得开发者可以根据具体需求选择合适的套接字类型和配置,以实现高效的网络通信和进程间通信。
UNIX域套接字是在同一台机器上的不同进程之间进行通信的一种方式。它们比网络套接字更快,因为它们不需要经过网络协议栈。
服务器端(server.c)
#include
#include
#include
#include
#include
#define SOCKET_PATH "/tmp/unix_socket"
int main() {
int server_sock, client_sock;
struct sockaddr_un server_addr;
// 创建UNIX域套接字
server_sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (server_sock == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// 设置服务器地址
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
unlink(SOCKET_PATH); // 如果套接字文件已存在,则删除它
// 绑定套接字
if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
close(server_sock);
exit(EXIT_FAILURE);
}
// 监听连接
if (listen(server_sock, 1) == -1) {
perror("listen");
close(server_sock);
exit(EXIT_FAILURE);
}
// 接受连接
client_sock = accept(server_sock, NULL, NULL);
if (client_sock == -1) {
perror("accept");
close(server_sock);
exit(EXIT_FAILURE);
}
// 发送消息给客户端
const char *message = "Hello from server!";
write(client_sock, message, strlen(message));
// 关闭套接字
close(client_sock);
close(server_sock);
unlink(SOCKET_PATH); // 删除套接字文件
return 0;
}
客户端(client.c)
#include
#include
#include
#include
#include
#define SOCKET_PATH "/tmp/unix_socket"
int main() {
int client_sock;
struct sockaddr_un server_addr;
char buffer;
// 创建UNIX域套接字
client_sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (client_sock == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// 设置服务器地址
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
// 连接到服务器
if (connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("connect");
close(client_sock);
exit(EXIT_FAILURE);
}
// 接收来自服务器的消息
ssize_t num_bytes = read(client_sock, buffer, sizeof(buffer) - 1);
if (num_bytes > 0) {
buffer[num_bytes] = '\0'; // 确保字符串以null结尾
printf("Received: %s\n", buffer);
}
// 关闭套接字
close(client_sock);
return 0;
}
服务器端代码
#include
#include
#include
#include
#include
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {
0};
// 创建socket文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 绑定socket到端口
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
close(server_fd);
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 监听连接
if (listen(server_fd, 3) < 0) {
perror("listen");
close(server_fd);
exit(EXIT_FAILURE);
}
// 接受客户端连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
close(server_fd);
exit(EXIT_FAILURE);
}
// 读取数据
int valread = read(new_socket, buffer, BUFFER_SIZE);
printf("Received: %s\n", buffer);
// 发送响应
char *hello = "Hello from server";
send(new_socket, hello, strlen(hello), 0);
printf("Hello message sent\n");
// 关闭socket
close(new_socket);
close(server_fd);
return 0;
}
客户端代码
#include
#include
#include
#include
#include
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {