Socket编程:C语言使用socket进行本地进程间通信、网络通信

文章目录

    • 概要
    • Socket套接字的属性和函数用法
      • Socket套接字的属性
      • Socket套接字的C语言函数用法
        • Socket本地进程间通信(UNIX域套接字)
        • Socket网络间设备通信(基于TCP)
        • Socket广播通信(基于UDP)
        • Socket组播通信(基于UDP)
    • 使用Socket和IO多路复用满足并发需求
      • 多线程(pthreads)服务器同时处理多个客户端连接
      • select函数实现并发网络通信
      • poll函数实现并发网络通信
      • epoll函数实现并发网络通信
      • 小结(网络通信中select函数、poll函数、epoll函数的区别和适用的使用场景)
        • select 函数
        • poll 函数
        • epoll 函数
        • 总结
    • 技术细节
      • C语言使用Socket进行本地进程间通信的流程
      • C语言使用Socket进行网络通信的流程
    • 总结

概要

` 本文将介绍如何使用C语言和socket进行本地进程间通信、网络设备间通信,并详细介绍socket的用法及属性

例如:

openAI 的 GPT 大模型的发展历程。

Socket套接字的属性和函数用法

socket()
‌功能‌:创建一个新的套接字。
‌C语言函数原型‌:

int socket(int domain, int type, int protocol);

Socket套接字的属性

Socket套接字在编程中是一个非常重要的概念,它提供了一套丰富的接口用于网络通信和进程间通信。以下是Socket套接字的一些常见属性:

  1. 域(Domain)‌:
    指定套接字通信中使用的网络介质。最常见的套接字域是AF_INET(IPv4)和AF_INET6(IPv6),它们分别代表Internet网络。此外,还有AF_UNIX(或AF_LOCAL),用于在同一台机器上的不同进程间进行通信。

  2. 类型(Type)‌:
    指定套接字通信的类型。常见的类型包括:
    SOCK_STREAM:提供面向连接、可靠的数据传输服务。这通常基于TCP协议。
    SOCK_DGRAM:提供无连接的服务,数据传输的可靠性得不到保证。这通常基于UDP协议。
    SOCK_RAW:允许直接读写内核未处理的IP数据包,提供了对底层网络协议的访问。

  3. 协议(Protocol)‌:
    指定套接字通信中使用的具体协议。对于SOCK_STREAM类型的套接字,默认使用TCP协议(IPPROTO_TCP);对于SOCK_DGRAM类型的套接字,默认使用UDP协议(IPPROTO_UDP)。对于SOCK_RAW类型的套接字,可以指定具体的IP协议。

  4. 套接字描述符(Socket Descriptor)‌:
    创建套接字时返回的一个整数,用于标识这个套接字。后续的套接字操作(如绑定、监听、接受连接、发送和接收数据等)都需要通过这个描述符来进行。

  5. 绑定地址和端口(Bound Address and Port)‌:
    服务器端套接字通常需要使用bind函数绑定到一个具体的IP地址和端口号上,以便客户端可以连接到它。客户端套接字在连接到服务器时,也会绑定到一个本地的临时端口上。

  6. 连接状态(Connection State)‌:
    对于面向连接的套接字(如SOCK_STREAM),有连接建立、数据传输和连接关闭等状态。对于无连接的套接字(如SOCK_DGRAM),则没有明确的连接状态。

  7. 输入输出缓冲区(Input/Output Buffers)‌:
    每个套接字都有自己的输入输出缓冲区,用于暂存发送和接收的数据。这些缓冲区的大小可以通过setsockopt和getsockopt函数来设置和获取。

  8. 套接字选项(Socket Options)‌:
    通过setsockopt和getsockopt函数可以设置和获取套接字的各种选项,这些选项可以控制套接字的行为。例如,SO_REUSEADDR选项允许在同一端口上启动多个服务器实例,SO_RCVBUF和SO_SNDBUF选项可以设置接收和发送缓冲区的大小。

  9. 阻塞与非阻塞模式(Blocking and Non-blocking Mode)‌:
    套接字可以设置为阻塞模式或非阻塞模式。在阻塞模式下,套接字操作(如连接、发送和接收数据)会等待操作完成或发生错误才返回。在非阻塞模式下,这些操作会立即返回,如果操作不能立即完成,则可能返回一个错误代码。

  10. 超时设置(Timeout Settings)‌:
    可以通过套接字选项设置发送和接收操作的超时时间。如果在指定的时间内操作没有完成,则操作会超时并返回一个错误代码。

  11. 带外数据(Out-of-Band Data)‌:
    某些类型的套接字(如SOCK_STREAM)支持带外数据传输,这允许发送紧急数据,这些数据会绕过正常的数据队列被立即处理。

  12. 多播和广播(Multicast and Broadcast)‌:
    套接字可以设置为支持多播和广播通信。多播允许数据同时发送给多个接收者,而广播则允许数据发送给同一网络上的所有接收者。
    这些属性共同定义了套接字的行为和特性,使得开发者可以根据具体需求选择合适的套接字类型和配置,以实现高效的网络通信和进程间通信。

Socket套接字的C语言函数用法

Socket本地进程间通信(UNIX域套接字)

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;
}
Socket网络间设备通信(基于TCP)

服务器端代码

#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() {
   

你可能感兴趣的:(c语言,开发语言,网络)