(Linux) 套接字socket基础

news2024/7/5 0:04:11

文章目录

  • 前言
    • 基本原理
  • Code
    • server
    • client
  • 核心函数
    • socket
    • bind
    • listen
    • accept
    • recv
    • send
    • connect
    • close
  • 多线程改进
  • END

前言

本文将以纯C语言描述,编译器gcc。

C/C++没有标准的网络库,因为都需要用到各个平台的接口才行。

本文讲解Linux下最基础的socket编程,实现一个简单的回声服务器。

基本原理

具体流程如下:

此图描述的非常清晰,就是一步一步往下操作即可。

tcp.jpg (408×560) (subingwen.cn)

Code

废话不多说,直接上code

cv代码后直接可以编译运行

注意:

  • 请先启动server再启动client
  • 如出现异常,请确认端口是否可以用
  • 如果是云端操作,请注意ip

server

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

int main() {
    printf("***** server main start ******\n");

    // 1. 创建监听的套接字
    // ipv4 流式传输
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        printf("socket = -1");
        return -1;
    }

    // 配置基本信息
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    // 主机字节序转为网络字节序
    // 设定一个没有冲入的端口
    saddr.sin_port = htons(12345);
    // any就是0->0.0.0.0 此时不区分大小端
    // 自动定读网卡的实际ip地址
    saddr.sin_addr.s_addr = INADDR_ANY;

    // 2. 绑定socket
    int ret = bind(fd, (struct sockaddr*)&saddr, sizeof(saddr));
    if (ret == -1) {
        printf("bind = -1");
        return -1;
    }

    // 3. 设置监听
    // 最大128
    ret = listen(fd, 128);
    if (ret == -1) {
        printf("listen = -1");
        return -1;
    }

    printf("***** ready to accept ******\n");

    struct sockaddr_in caddr;
    int addrlen = sizeof(struct sockaddr_in);
    // 4. 阻塞并等待客户端的连接
    // 成功则获得客户端的fd
    int cfd = accept(fd, (struct sockaddr*)&caddr, &addrlen);
    if (cfd == -1) {
        printf("accept = -1");
        return -1;
    }

    // 连接成功,打印客户端ip和端口
    char ip[32] = {};
    printf("client ip = %s, port = %d\n",
           inet_ntop(AF_INET, &caddr.sin_addr.s_addr, ip, sizeof(ip)),
           ntohs(caddr.sin_port));

    // 5. 通信
    while (1) {
        char buff[1024] = {};
        int len = recv(cfd, buff, sizeof(buff), 0);
        if (len > 0) {
            printf("client say : %s\n", buff);
            // 原版不动的回传
            send(cfd, buff, len, 0);
        } else if (len == 0) {
            printf("client close ... \n");
            break;
        } else {
            printf("recv = -1\n");
            break;
        }
    }

    // 6. 关闭文件描述符
    close(fd);
    close(cfd);

    printf("***** server main end ******\n");
    return 0;
}

client

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

int main() {
    printf("***** client main start ******\n");
    // 1. 创建通信的套接字
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        printf("socket = -1");
        return -1;
    }

    // 连接服务器
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(12345);
    inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr.s_addr);

    // 2. 绑定socket
    int ret = connect(fd, (struct sockaddr*)&saddr, sizeof(saddr));
    if (ret == -1) {
        printf("connect = -1");
        return -1;
    }

    int number = 0;
    // 3. 通信
    while (1) {
        // 发送
        char buff[1024] = {};
        sprintf(buff, "Hello i am client [%d]...\n", number++);
        send(fd, buff, strlen(buff) + 1, 0);

        // 接受
        memset(buff, 0, sizeof(buff));
        int len = recv(fd, buff, sizeof(buff), 0);
        if (len > 0) {
            printf("server say : %s\n", buff);

        } else if (len == 0) {
            printf("server close ... \n");
            break;
        } else {
            printf("read = -1\n");
            break;
        }

        // 延时2秒
        sleep(2);
    }

    // 4. 关闭文件描述符
    close(fd);

    printf("***** client main end ******\n");
    return 0;
}

核心函数

头文件

#include <arpa/inet.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <unistd.h>

socket

用于创建socket对象

前两个参数决定了使用什么通信协议。

#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
// ipv4 的流式 就是tcp
socket(PF_INET, SOCK_STREAM, 0);

// ipv4 的报文 就是udp
socket(PF_INET, SOCK_DGRAM, 0);

bind

服务端客户端通用

#include <sys/types.h>
#include <sys/socket.h>

// const 是入参
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

listen

服务端开启监听

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd, int backlog);

accept

服务端等待客户端的接入

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
// 这里是回参
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <sys/socket.h>

int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);

recv

接收数据

#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

send

发送数据

#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

connect

客户端去连接服务端

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
// const 是入参
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

close

关闭fd的通用函数

#include <unistd.h>

int close(int fd);

多线程改进

注意编译的时候要加入-lpthread

线程函数的接口是void* () (void*)

经过改进后可以并发接入多个客户端的连接。

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

/**
 * 将 struct sockaddr_in 和 fd 绑定到一起
*/
struct SockInfo {
    struct sockaddr_in addr;
    int fd;
};

struct SockInfo infos[512];

/**
 * pthread 固定函数类型
 * void* () (void*)
*/
void* working(void* arg) {
    // 指针的强转
    struct SockInfo* pinfo = (struct SockInfo*)arg;

    // 连接成功,打印客户端ip和端口
    char ip[32] = {};
    printf("client ip = %s, port = %d\n",
           inet_ntop(AF_INET, &pinfo->addr.sin_addr.s_addr, ip, sizeof(ip)),
           ntohs(pinfo->addr.sin_port));

    // 5. 通信
    while (1) {
        char buff[1024] = {};
        int len = recv(pinfo->fd, buff, sizeof(buff), 0);
        if (len > 0) {
            printf("client say : %s\n", buff);
            send(pinfo->fd, buff, len, 0);
        } else if (len == 0) {
            printf("client close ... \n");
            break;
        } else {
            printf("recv = -1\n");
            break;
        }
    }

    // 6. 关闭文件描述符
    close(pinfo->fd);
    pinfo->fd = -1;

    printf("thread end success\n");
    return NULL;
}

int main() {
    printf("***** server main start ******\n");
    // socket
    int fd = socket(AF_INET, SOCK_STREAM, 0);

    if (fd == -1) {
        printf("socket = -1");
        return -1;
    }

    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(12345);
    saddr.sin_addr.s_addr = INADDR_ANY;

    // bind
    int ret = bind(fd, (struct sockaddr*)&saddr, sizeof(saddr));
    if (ret == -1) {
        printf("bind = -1");
        return -1;
    }

    // listen
    ret = listen(fd, 128);
    if (ret == -1) {
        printf("listen = -1");
        return -1;
    }

    // 初始化所有封装的 struct
    printf("********ready to accept**********\n");
    int max = sizeof(infos) / sizeof(infos[0]);
    for (int i = 0; i < max; i += 1) {
        // memset 0
        bzero(&infos[i], sizeof(infos[i]));
        infos[i].fd = -1;
    }

    // accept
    // thread
    int addrlen = sizeof(struct sockaddr_in);
    while (1) {
        // 找出可用空间
        struct SockInfo* pinfo = NULL;
        for (int i = 0; i < max; i += 1) {
            if (infos[i].fd == -1) {
                pinfo = &infos[i];
                break;
            }
        }

        int cfd = accept(fd, (struct sockaddr*)&pinfo->addr, &addrlen);
        pinfo->fd = cfd;
        
        // 根据自己的需要处理失败的 accept
        if (cfd == -1) {
            printf("accept = -1");
            break;
        }

        // 创建子线程,并与主线程分离
        pthread_t tid;
        pthread_create(&tid, NULL, working, pinfo);
        pthread_detach(tid);
    }

    // 关闭fd
    close(fd);

    printf("***** server main end ******\n");
    return 0;
}


END

参考:套接字 - Socket | 爱编程的大丙 (subingwen.cn)

Qt多线程网络通信-套接字通信 socket_哔哩哔哩_bilibili

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

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

相关文章

uniapp 常用提示弹框整理

一. 加载提示弹框 在执行数据查询、页面数据渲染等过程中弹出提示。以页面渲染为例&#xff1a; //前端数据请求时&#xff0c;显示加载提示弹框 uni.showLoading({title: 加载中... }); // 数据从后端接口返回后&#xff0c;提示弹框关闭 uni.hideLoading();效果如下&#x…

Sui Builder House京都站|创意大赛获奖名单公布

Sui Builder House京都站于6月30日&#xff08;周五&#xff09;圆满结束&#xff0c;这是一次Sui生态系统项目演示和展示各自产品的良好机会。构建者们向大家展示了游戏、NFT、DeFi和基础设施赛道的项目&#xff0c;同时现场演讲还介绍了Sui的最新进展以及有关AI和用户体验设计…

React环境安装配置

React环境安装配置 一、前提二、React安装 一、前提 安装本地React环境需要Node.js&#xff0c;如果具有Node环境跳过即可。如果没有安装则可参考该篇文章安装Node环境&#xff0c;点击查看 二、React安装 全局安装React 首先打开命令行&#xff0c;建议以管理员身份输入命…

20230704测试STC32G实验箱9.6(STC32G12K128)开发板的虚拟串口(C语言深入了解)

20230704测试STC32G实验箱9.6&#xff08;STC32G12K128&#xff09;开发板的虚拟串口&#xff08;C语言深入了解&#xff09; 06第五集&#xff1a;C语言运算符和进制数入门上.mp4 07第五集&#xff1a;C语言运算符和进制数入门下.mp4 2023/7/4 19:00 下次 在【冲哥】录视频的时…

024、数据库管理之数据同步工具TiCDC

TiCDC TiCDCTiCDC介绍架构与原理适用场景对已有TiDB进行扩容部署TiCDC管理工具TiCDC同步任务查询所有TiCDC同步任务查询TiCDC指定的同步任务管理TiCDC同步任务动态更新任务监控TiCDC 实验数据同步完整实操缩容当前TiCDC节点 TiCDC TiCDC介绍 TiCDC 是一款通过拉取 TiKV 变更日…

leetcode 42-接雨水

解法一&#xff1a;双指针&#xff08;暴力按列计算&#xff09; 首先&#xff0c;如果按照列来计算的话&#xff0c;宽度一定是1了&#xff0c;我们再把每一列的雨水的高度求出来就可以了。 可以看出每一列雨水的高度&#xff0c;取决于该列左侧最高的柱子和右侧最高的柱子中…

VMware虚拟机的基本操作:快照和克隆

VMware虚拟机的基本操作 一、虚拟机快照二、克隆 一、虚拟机快照 “快照”是虚拟机磁盘文件&#xff08;VMDK&#xff09;在某个点及时的副本。系统崩溃或系统异常&#xff0c;你可以通过使用恢复到快照来保持磁盘文件系统和系统存储。当升级应用和服务器及给它们打补丁的时候…

一桩关于Json序列化引发的惨案(Go系统)

文章目录 前言突然崩了排查问题关于go的json库什么是反射 解决大结构体序列化的性能问题干掉大结构体减少反射使用一些好用的第三方序列化包 自定义序列化 写在最后 前言 一个风和日丽的下午&#xff0c;线上系统突然开始报警&#xff08;系统温馨提示&#xff0c;您的服务接口…

部署Git服务器

哈喽&#xff0c;大家好&#xff0c;本次为大家演示如何部署git服务器. 首先要准备gitea和phpstudy_pro phpstudy一路nest即可&#xff0c;可以帮你安装mysql和阿帕奇。 登录127.0.0.1:3000注册gitea即可。 如何在上传公钥的时候出现500的错误&#xff0c;加入这句便可解决…

SpringBoot+Vue+Element-ui实现文件下载

目录 1.后端代码部分 2.前端代码部分 3.效果展示 1.后端代码部分 GetMapping("downloadFile")public void downloadFile(RequestParam("filename") String filename,HttpServletResponse response) throws Exception {// 告知浏览器这是一个字节流&…

WebSocket connection to “wss://xxx/xxx“ failed

用了https后&#xff0c;需要用nginx作websocket地址的转发才能使用wss&#xff0c;别直接用端口访问 location /ws/ {proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "Upgrade"; proxy_pass http://localhost:10001/…

JVM源码剖析之SymbolTable和StringTable

很多读者在观看JVM相关的书籍时会看到SymbolTable和StringTable&#xff0c;书中的三言二语介绍的不是很清楚&#xff0c;并且读者的水平有限&#xff0c;导致无法理解SymbolTable和StringTable。所以特意写此篇图文并茂的文章来彻底理解SymbolTable和StringTable这两张表。 版…

Java面试Day16

1.Dubbo 是什么&#xff1f;是否了解过它的架构设计&#xff1f; Dubbo官网&#xff1a;Apache Dubbo Dubbo是一个高性能、轻量级的开源Java RPC框架&#xff0c;它提供了完整的RPC协议栈&#xff0c;包括服务发布、服务引用、负载均衡、容错、服务治理和服务监控等功能&#…

构造函数使用初始化列表+模板

构造函数使用初始化列表模板 注意对应关系&#xff1a; Stack(int size) ;template<class DataType> inline Stack<DataType>::Stack(int size) : stacksize(size), top(0) {item new DataType[stacksize];if (item ! nullptr) cout << "成功初始化栈&…

再见!Fastjson!

你为何仍用Fastjson&#xff1f; 原因可以说出5678种&#xff0c;总而言之言而总之&#xff0c;你不&#xff08;敢&#xff09;切换的原因或许只有一个&#xff1a;Fastjson的静态方法调用用着省心&#xff1b;最重要的是&#xff0c;对其它JSON库&#xff08;如Jackson/Gson…

【生活分享】《哈姆雷特》· 苏州站 - 一次有趣的音乐剧体验

平时博客里大部分都是写技术相关的文章&#xff0c;感觉内容还是比较单一的&#xff0c;也想分享一下最近的一些有趣的生活~ 6月30号的时候&#xff0c;便订好了徐俊导演编排的7月2号场《哈姆雷特》音乐剧。而实际上&#xff0c;苏州场7月1号就上映了&#xff0c;感觉那天太晚……

C++图形开发(6):落下后能弹起的小球

文章目录 1.重复下落的小球2.落下后能弹起的小球3.能上下反弹的小球4.符合重力的能上下反弹的小球 今天我们来尝试实现一个落地后可以弹起的小球 1.重复下落的小球 首先&#xff0c;我们要来实现一个小球的重复下落 我们知道&#xff0c;在前面的代码中&#xff08;详见C图形…

云his源码:医疗卫生信息系统

伴随着以5G、云计算、大数据、人工智能和物联网等新兴技术的发展&#xff0c;医疗行业也呈现了数字化的发展趋势。如何让医疗领域中的服务提供方、需求方、支付方以及供应链等参与方实现业务协同及数据协同&#xff0c;是目前医疗行业所需的能力。 HIS系统&#xff0c;即“医疗…

点云数据分类及滤波方法

如何获取点云数据 传统的点云获取技术包括非接触式测量和接触式测量两种&#xff0c;它们的主要区别在于&#xff0c;在测量过程中测头是否与工件的表面相接触。 非接触式测量是利用光学原理的方法采集数据&#xff0c;例如结构光法、测距法以及干涉法等。该方法的优点在于测…

vue项目中的vue.config.js配置文件中的proxy代理post一直在预检

记录一个比较有意思的bug,帮别人调试遇到的&#xff0c;一个哥们一直在群里问了好几次同一个问题了&#xff0c;他配置了代理&#xff0c;请求一直在发送&#xff0c;postman测试没问题&#xff0c;可以成功接收到&#xff0c;但是在项目前端请求&#xff0c;确实一点响应没有&…