linux C语言 socket的server、client 实现

news2024/10/7 8:19:27

讲解:

在Linux中,使用socket与另一端建立连接通常涉及到以下步骤:

1. 创建Socket:首先,你需要创建一个套接字(socket)。你可以使用socket()系统调用来创建套接字。通常,你需要指定套接字的类型(例如,SOCK_STREAM表示TCP套接字)和地址族(例如,AF_INET表示IPv4地址族)。

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
    perror("socket");
    exit(EXIT_FAILURE);
}

2. 设置目标地址信息:你需要指定要连接的远程服务器的地址信息,包括IP地址和端口号。通常,你会使用struct sockaddr_in来表示这些信息。

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080); // 服务器的端口号
inet_pton(AF_INET, "服务器IP地址", &(server_addr.sin_addr));

3. 使用connect()函数建立连接:接下来,你可以使用connect()系统调用来与远程服务器建立连接。

int connection_status = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (connection_status == -1) {
    perror("connect");
    close(sockfd);
    exit(EXIT_FAILURE);
}

4. 发送和接收数据:一旦连接建立成功,你可以使用send()recv()等函数来发送和接收数据。

5. 关闭套接字:当通信完成后,不要忘记关闭套接字,释放资源。

close(sockfd);

以上是使用C语言的套接字编程的基本步骤。请注意,错误处理非常重要,以确保你的程序在出现问题时能够 graceful 地处理它们。此外,还可以考虑使用非阻塞套接字或多线程/多进程来处理连接,以提高性能和可伸缩性。具体实现方式可能会因编程语言和库的不同而有所不同。

实例一

如何编写两个进程,一个进程作为socket的服务端,一个进程作为socket的客户端,服务端监听客户端:

要编写一个简单的Socket通信程序,其中一个进程作为服务器端监听客户端,另一个进程作为客户端连接到服务器,可以使用C语言编写,以下是一个基本的示例:

1. 服务端程序(server.c):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

int main() {
    // 创建套接字
    int server_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (server_sock == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置服务器地址信息
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    // 绑定套接字到服务器地址
    if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    // 监听连接请求
    if (listen(server_sock, 5) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    printf("Server listening on port 8080...\n");

    // 接受客户端连接请求
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_len);
    if (client_sock == -1) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    printf("Client connected.\n");

    // 在这里可以进行与客户端的数据交互

    // 关闭套接字
    close(client_sock);
    close(server_sock);

    return 0;
}
2. 客户端程序(client.c):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

int main() {
    // 创建套接字
    int client_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (client_sock == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置服务器地址信息
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    // 连接到服务器
    if (connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
        perror("connect");
        exit(EXIT_FAILURE);
    }

    printf("Connected to server.\n");

    // 在这里可以进行与服务器的数据交互

    // 关闭套接字
    close(client_sock);

    return 0;
}

你可以将上述代码分别保存为 server.c 和 client.c,并分别编译成可执行文件,然后在两个不同的终端窗口中运行它们。服务器将在端口8080上监听连接,而客户端将连接到服务器。在服务器和客户端之间进行数据交互的部分可以根据你的需求进行扩展。

编译

要编译生成可执行文件,你可以使用 C 编译器(如 GCC)来编译你的 C 代码。以下是编译和生成可执行文件的步骤:

  1. 打开终端窗口,并导航到包含你的 C 源代码文件的目录。
  2. 使用编译器编译你的源代码文件。对于上面提供的示例代码,你可以使用以下命令编译:
    对于服务端程序 server.c:
gcc -o server server.c


对于客户端程序 client.c:
这些命令中的 -o 选项用于指定生成的可执行文件的名称。在上面的示例中,我们将生成的可执行文件分别命名为 serverclient

  1. 如果编译没有错误,你会在同一目录下生成可执行文件 serverclient
  2. 要运行服务器程序,你可以在终端中执行以下命令:
./server


要运行客户端程序,你可以执行以下命令:
这将启动服务器和客户端,并开始它们之间的通信。

请确保在编译之前安装了合适的编译器,例如 GCC。如果你使用的是不同的编程语言或开发环境,编译步骤可能会有所不同。但通常情况下,使用编译器将源代码编译成可执行文件是一个常见的编译过程。

实例二(实际应用):

在实际应用中,不可能像实例一中,server只接收一次,client只向server发送一次。如何做到,server始终存在监听端口,当有client连接时,接收包并处理

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>  // 包含fcntl头文件

int main() {
    // 创建套接字
    int server_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (server_sock == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置服务器地址信息
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    // 绑定套接字到服务器地址
    if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    // 监听连接请求
    if (listen(server_sock, 5) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    printf("Server listening on port 8080...\n");

    // 接受客户端连接请求
    while (1) {
        struct sockaddr_in client_addr;
        socklen_t client_addr_len = sizeof(client_addr);
        int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_len);
        if (client_sock == -1) {
            perror("accept");
            exit(EXIT_FAILURE);
        }

        printf("Client connected.\n");

        // 发送数据给客户端
        char message[] = "Hello, client!";
        if (send(client_sock, message, sizeof(message), 0) == -1) {
            perror("send");
        }

        while (1) {
            // 接收客户端的数据,recv函数会阻塞等待数据到达
            char buffer[1024];
            int bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);
            if (bytes_received == -1) {
                perror("recv");
                break; // 出错时退出循环
            } else if (bytes_received == 0) {
                // 客户端关闭连接,退出循环
                printf("Client disconnected.\n");
                break;
            } else {
                buffer[bytes_received] = '\0';
                printf("Received from client: %s\n", buffer);
            }
        }
        // 关闭套接字
        close(client_sock);
    }
    close(server_sock);

    return 0;
}

需要注意: server中的recv是阻塞式的情况基于client_sock未关闭且资源未释放才可以,否则recv始终返回0.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

int main() {
    // 创建套接字
    int client_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (client_sock == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置服务器地址信息
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    // 连接到服务器
    if (connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
        perror("connect");
        exit(EXIT_FAILURE);
    }

    printf("Connected to server.\n");

    // 接收服务器的数据
    char buffer[1024];
    int bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);
    if (bytes_received == -1) {
        perror("recv");
    } else {
        buffer[bytes_received] = '\0';
        printf("Received from server: %s\n", buffer);
    }
    sleep(10);
    // 发送数据给服务器
    while (1) {
        char message[] = "Hello, server!";
        if (send(client_sock, message, sizeof(message), 0) == -1) {
            perror("send");
        }
        sleep(10);
    }
    
    // 关闭套接字, 只要关闭client,server端的recv就不会阻塞了,返回0!!
    close(client_sock);

    return 0;
}

实验效果:

如果对你有帮助,请点赞收藏,谢谢~这是我更新的动力~
如果有任何问题欢迎在评论区交流~我们一同成长~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1017663.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ssh服务登录原理与配置

文章目录 前言一、基于口令的认证&#xff08;用户名密码&#xff09;二、基于公钥的认证&#xff08;免密登录&#xff09;三、禁止用户登录和修改端口四、免密登录具体操作 前言 非对称加密是在认证用户连接的时候使用的&#xff0c;对称加密是在用户连接之后开始传输数据的…

【C++】深拷贝和浅拷贝 ④ ( 深拷贝示例 )

文章目录 一、深拷贝示例1、浅拷贝问题2、自己实现深拷贝 二、深拷贝完整代码示例 一、深拷贝示例 1、浅拷贝问题 在上一篇博客 【C】深拷贝和浅拷贝 ③ ( 浅拷贝内存分析 ) 中 , 使用了浅拷贝 , 将 原始对象 Students 赋值给了 拷贝对象 Student s2 ; 使用 C 编译器 生成的 默…

Mysql的基本查询练习

目录 一、Create 1.1单行数据全列插入 1.2 多行数据指定列插入 1.3插入否则更新 1.4 替换 二、Retrieve 2.1全列查询 2.2指定列查询 2.3查询字段为表达式 2.4为查询结果指定别名 2.5 结果去重 2.6 where 条件 2.6 NULL的查询 2.7 结果排序 三、 Update 四、Dele…

电力系统直流潮流分析【N-1】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

VHOST-SCSI代码分析(0)VHOST概述

与VIRTIO框架相比&#xff0c;VHOST将设备放到HOST上&#xff0c;让Guest和Host Kernal Space之间共享virtqueue&#xff0c;减少Exception Level切换。 对于数据的传递&#xff0c;依次经历&#xff1a;Guest UserSpace&#xff08;EL0&#xff09;-> Guest KernelSpace&am…

MySQL索引,事务及存储引擎

目录 MySQL索引 创建索引的依据&#xff1a; 索引的类型 普通索引 唯一索引 主键索引 组合索引 全文索引 查看索引 删除索引 事务 事务的 ACID 特性 原子性 一致性 隔离性 持久性 隔离级别 设置隔离级别 事务管理操作 自动提交事务 存储引擎 M…

GODIVA论文阅读

论文链接&#xff1a;GODIVA: Generating Open-DomaIn Videos from nAtural Descriptions 文章目录 摘要引言相关工作Video-to-video generationText-to-image generationText-to-video generation GODIVA方法逐帧视频自动编码器GODIVA视频生成器 实验数据集评价指标自动评估指…

QT基础教程(对话框1)

文章目录 前言一、对话框概念二、模态对话框三、非模态对话框总结 前言 本篇文章我们来讲解QT中的对话框。 资料合集地微信公众号&#xff1a;优质程序猿一、对话框概念 在Qt中&#xff0c;对话框&#xff08;Dialog&#xff09;是一种用于与用户进行交互、收集输入或展示信…

适用于 Android 的 Windows 子系统™️发行说明

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 内部版本 2304.40000.3.0 内部版本 2303.40000.3.0 内部版本 2302.4000 内部版本 2301.40000.4.0 内部版本 221…

【wxWidgets 如何实现wxAccordion 手风琴组件】

1. 简要说明 wxWidgets 并没有提供wxAccordion 手风琴组件面板, 网上也基本没有找到资料,CSDN有个标题:wxAccordion:wxWidgets的手风琴控件下载链接,再无资料可查, 但是其他语言是提供了手风琴面板组件的, 那么怎么办呢,开干喽… 2. 效果展示 3. 交流探讨 代码就暂时不发了,…

【LeetCode-简单题】225. 用队列实现栈

文章目录 题目方法一&#xff1a;单个队列实现 题目 方法一&#xff1a;单个队列实现 入栈 和入队正常进行出栈的元素其实就是队列的尾部元素&#xff0c;所以直接将尾部元素弹出即可&#xff0c;其实就可以将除了最后一个元素的其他元素出队再加入队&#xff0c;然后弹出队首元…

第二章 进程与线程 八、处理机调度(时机切换、过程调度方式)

一、进程调度的时机 二、进程调度的方式 1、非剥夺调度方式&#xff1a; 非剥夺调度方式&#xff0c;又称非抢占方式。即只允许进程主动放弃处理机。在运行过程中即便有更紧迫的任务到达&#xff0c;当前进程依然会继续使用处理机&#xff0c;直到该进程终止或主动要求进入阻…

virtualbox配置ubuntu1804虚拟机相关流程

virtualbox配置ubuntu1804虚拟机相关流程 相关版本能解决的问题安装流程1&#xff1a;新建虚拟机安装流程2&#xff1a;配置虚拟机安装流程3&#xff1a;安装虚拟机系统安装流程4&#xff1a;设置ubuntu 相关版本 virtualbox使用VirtualBox官网下载的6.1.34 r150636 版。ubunt…

CSRF攻击原理详解

CSRF概念&#xff1a; CSRF定义&#xff1a; 跨站请求伪造&#xff08;英语&#xff1a;Cross-site request forgery&#xff09;是一种对网站的恶意利用&#xff0c;也被称为 one-click attack 或者 session riding&#xff0c;通常缩写为 CSRF 或者 XSRF&#xff0c; 是一种…

通用商城项目(下)之——Nginx的安装及使用

&#xff08;作为通用商城项目的一个部分&#xff0c;单独抽离了出来。查看完整见父页面&#xff1a; &#xff09; 加入Nginx-完成反向代理、负载均衡和动静分离 1.配置SSH-使用账号密码&#xff0c;远程登录Linux 1.1配置实现 1、配置sshd 1)sudo vi /etc/ssh/sshd_confi…

Rasa:使用大语言模型进行意图分类

Rasa:使用大语言模型进行意图分类 在Rasa的最新版本(3.x)中,引入了一种新的意图分类方法,即使用大型语言模型(LLM)和一种称为检索增强生成(RAG)的方法进行意图分类。 LLM意图分类器是一种全新的意图分类器,利用大型语言模型(LLM)来对意图进行分类。LLM意图分类器…

汽车租赁系统设计与实现

汽车租赁系统 1&#xff0e;需求分析 1.1任务概述 1.2开发环境和使用技术 1.3数据库设计 2&#xff0e;登陆模块 2.1登陆页面的代码是&#xff1a; 2.2登录类login.java的代码如下&#xff1a; 2.3LoginServlet.java的代码如下&#xff1a; 3&#xff0e;公共…

ESP-IDF学习——1.环境安装与hello-world

ESP-IDF学习——1.环境安装与hello-world 0.前言一、环境搭建1.官方IDE工具2.vscode图形化配置 二、示例工程三、自定义工程四、点灯五、总结 0.前言 最近在学习freertos&#xff0c;但由于买的书还没到&#xff0c;所以先捣鼓捣鼓ESP-IDF&#xff0c;因为这个比Arduino更接近底…

神秘字符(acm模式)

#include<iostream> #include<cctype> #include<string> using namespace std; int main() {int n;cin >> n;getchar();while (n--){string str, str1, str2"";//cin >> str >> str1;getline(cin, str);//遇到换行符就忽略&…

GB28181学习(五)——实时视音频点播(信令传输部分)

要求 实时视音频点播的SIP消息应通过本域或其他域的SIP服务器进行路由、转发&#xff0c;目标设备的实时视音频流宜通过本域的媒体服务器进行转发&#xff1b;采用INVITE方法实现会话连接&#xff0c;采用RTP/RTCP协议实现媒体传输&#xff1b;信令流程分为客户端主动发起和第…