Socket网络编程

本文主要讲解Socket网络编程。
首先介绍socket,包括TCP和UDP通信过程;然后介绍常用的函数;最后编写client-server例子,并进行测试。

文章目录

  • Socket介绍
    • TCP通信过程
      • 服务器端通信过程:
      • 客户端通信过程:
    • UDP通信过程
      • 服务器端通信过程:
      • 客户端通信过程:
  • 常用函数介绍
    • socket
      • 函数原型
      • 参数介绍
    • bind
      • 函数原型
      • 参数介绍
    • listen
      • 函数原型
      • 参数介绍
    • connect
      • 函数原型
      • 参数介绍
    • accept
      • 函数原型
      • 参数介绍
    • close
      • 函数原型
      • 参数介绍
    • recv
      • 函数原型
      • 参数介绍
    • send
      • 函数原型
      • 参数介绍
    • recvmsg
      • 函数原型
      • 参数介绍
    • sendmsg
      • 函数原型
      • 参数介绍
      • msghdr
      • flag
    • setsockopt
      • 函数原型
      • 参数介绍
  • demo
    • 服务端代码
    • 客户端代码
      • 测试结果

Socket介绍

C 语言中的 socket 编程是一种用于在网络上进行通信的编程接口。通过 socket,程序可以在不同的计算机之间进行数据交换,实现网络通信的功能。

TCP通信过程

在这里插入图片描述

服务器端通信过程:

  1. 创建 Socket:使用 socket() 函数创建一个套接字,指定地址族为 AF_INET,类型为 SOCK_STREAM(表示流式套接字),协议为 IPPROTO_TCP。这一步通常会得到一个套接字描述符。

  2. 绑定 Socket:调用 bind() 函数将套接字与服务器所在主机的 IP 地址和端口号绑定。这样服务器就可以监听来自该端口的连接请求。

  3. 监听连接请求:使用 listen() 函数开始监听客户端的连接请求,并设置最大连接数。

  4. 接受连接:当有客户端发起连接请求时,使用 accept() 函数接受连接,返回一个新的套接字描述符用于与该客户端进行通信。

  5. 数据通信:通过新的套接字描述符,使用 send() 和 recv() 函数发送和接收数据。通常在一个循环中进行数据交换,直到通信结束。

  6. 关闭连接:通信结束后,关闭新的套接字描述符,释放资源。

客户端通信过程:

  1. 创建 Socket:同样使用 socket() 函数创建套接字,地址族为 AF_INET,类型为 SOCK_STREAM,协议为 IPPROTO_TCP。

  2. 连接服务器:使用 connect() 函数向服务器发起连接请求,指定服务器的 IP 地址和端口号。

  3. 数据通信:连接建立后,使用 send() 和 recv() 函数发送和接收数据,通常也是在一个循环中进行数据交换,直到通信结束。

  4. 关闭连接:通信结束后,关闭套接字描述符,释放资源。

UDP通信过程

在这里插入图片描述

服务器端通信过程:

  1. 创建 Socket:调用socket()函数创建一个套接字,指定地址族为AF_INET,类型为SOCK_DGRAM(数据报套接字),协议为IPPROTO_UDP。这一步通常会得到一个套接字描述符。

  2. 绑定 Socket:使用bind()函数将套接字与服务器所在主机的IP地址和端口号绑定。这样服务器就可以监听来自该端口的数据报。

  3. 数据通信:通过绑定的套接字描述符,使用sendto()和recvfrom()函数发送和接收数据报。UDP是无连接的协议,因此每次发送数据时都需要指定目标的IP地址和端口号。

  4. 关闭连接:通信结束后,关闭套接字描述符,释放资源。

客户端通信过程:

  1. 创建 Socket:同样使用socket()函数创建套接字,地址族为AF_INET,类型为SOCK_DGRAM,协议为IPPROTO_UDP。

  2. 数据通信:通过创建的套接字描述符,使用sendto()和recvfrom()函数发送和接收数据报。UDP是无连接的协议,因此每次发送数据时都需要指定目标的IP地址和端口号。

  3. 关闭连接:通信结束后,关闭套接字描述符,释放资源。

常用函数介绍

socket

函数原型

创建一个新的套接字,返回一个 int 类型的套接字文件描述符,用于后续的网络连接操作。

#include <sys/socket.h>

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

参数介绍

  • domain:指定 Socket AF(Address Family,地址族)可选:AF_INET(IPv4)或AF_INET6(IPv6)
  • type:指定数据传输方式,可选:
    • SOCK_STREAM(面向连接的 TCP)
    • SOCK_DGRAM(无连接的 UDP)
    • SOCK_RAW(原始 IP 数据包)
  • protocol:指定具体的传输层协议,可选:
    • IPPROTO_TCP
    • IPPTOTO_UDP
  • 函数返回值:
    • 成功:返回 Socket fd。
    • 失败:返回 -1。

bind

函数原型

将套接字与一个本地 IP:Port 绑定。通常用于服务端,以便在本地监听网络连接

#include <sys/socket.h>

int bind(int socket, struct sockaddr *addr, socklen_t addrlen); 

参数介绍

  • socket:指定 Server socket 文件描述符。
  • addr:指定 Server sockaddr 结构体变量,指针类型。
  • addrlen:指定 addr 变量的大小,使用 sizeof() 计算得出。
  • 函数返回值:
    • 成功:返回 0。
    • 失败:返回 -1。

listen

函数原型

开始监听来自远程主机的连接请求。通常用于服务器端,在套接字上等待来自客户端的连接请求。

#include <sys/socket.h>

int listen(int socket, int backlog);

参数介绍

  • socket:指定需要进入监听状态的 Server socket 文件描述符。
  • backlog:指定请求队列的最大长度,当队列满了之后,就不再接收请求。
  • 函数返回值:
    • 成功:返回 0。
    • 失败:返回 -1。

connect

函数原型

建立与远程主机的连接。通常用于客户端,以便连接到远程服务器

#include <sys/socket.h>

int connect(int socket, struct sockaddr *serv_addr, socklen_t addrlen);  

参数介绍

  • socket:指定 Client socket 文件描述符。
  • serv_addr:指定 Server sockaddr 结构体变量,指针类型。
  • addrlen:指定 addr 变量的大小,使用 sizeof() 计算得出。
  • 函数返回值:
    • 成功:返回 0。
    • 失败:返回 -1。

accept

函数原型

接受一个连接请求,返回一个新的、表示客户端的 Socket 文件描述符,作为服务端和客户端之间发送与接收操作的句柄。通常用于服务器端,用于接收客户端的连接请求。

#include <sys/socket.h>

int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);

参数介绍

  • socket:指定 Server socket 文件描述符。
  • addr:指定 Client sockaddr 结构体变量,指针类型。
  • addrlen:指定 addr 变量的大小,使用 sizeof() 计算得出。
  • 函数返回值:
    • 成功:返回 Client socket fd。
    • 失败:返回 -1。

close

函数原型

关闭套接字连接。

#include <unistd.h>

int close(int fildes);

参数介绍

  • fd:指定要关闭的 Socket 的文件描述符。
  • 函数返回值:
    • 成功:返回0。
    • 失败:返回 -1。

recv

函数原型

从套接字接收数据

#include <sys/socket.h>

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

参数介绍

  • sockfd:指定要接收 TCP 数据的 Socket 文件描述符。
  • buf:指定接收数据缓冲区的入口地址。
  • len:指定要接收的数据的 Byte 数目。
  • flags:指定接收数据时的选项,常设为 0。
  • 函数返回值:
    • 成功:返回接收的字节数。
    • 失败:返回 -1。

send

函数原型

向套接字发送数据。

 #include <sys/socket.h>
 
 ssize_t send(int sockfd, const void *buf, size_t len, int flags);

参数介绍

  • sockfd:指定要发送 TCP 数据的 Socket 文件描述符。
  • buf:指定发送数据缓冲区入的口地址。
  • len:指定要发送数据的 Byte 数目。
  • flags:指定发送数据时的选项,常设为 0。
  • 函数返回值:
    • 成功:返回发送的字节数。
    • 失败:返回 -1。

recvmsg

函数原型

#include <sys/socket.h>

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

参数介绍

  • sock:指定要接收 TCP 或 UDP 数据的 Socket 文件描述符。
  • msg:指示将接收的数据存储到 msghdr 结构体中。
  • flags:支持函数的行为,可选 0 或者 MSG_DONTWAIT 等标志位。
  • 函数返回值:
    • 成功:返回接收的字节数。
    • 失败:返回 -1。

sendmsg

函数原型

#include <sys/socket.h>

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

参数介绍

  • sock:指定要发送 TCP 或 UDP 数据的 Socket 文件描述符。
  • msg:指示 msghdr 结构体中包含了要发送的数据、数据长度等信息。
  • flags:支持函数的行为,可选 0 或者 MSG_DONTWAIT 等标志位。
  • 函数返回值:
    • 成功:返回发送的字节数。
    • 失败:返回 -1。

msghdr

struct msghdr {
    /* 指定接收或发送数据的对端地址,可以为 NULL 或 0,表示不需要使用对端地址。*/
    void         *msg_name;       /* optional address */
    socklen_t     msg_namelen;    /* size of address */

    /* 指定接收或发送数据的缓冲区和缓冲区大小,可以使用多个缓冲区同时接收或发送数据。*/
    struct iovec *msg_iov;        /* scatter/gather array */
    size_t        msg_iovlen;     /* # elements in msg_iov */

 /* 指定一些附加的控制信息,可以为 NULL 或 0。*/
    void         *msg_control;    /* ancillary data, see below */
    size_t        msg_controllen; /* ancillary data buffer len */

 /* 指定函数的行为,例如是否需要接收带外数据等。*/
    int           msg_flags;      /* flags on received message */
};

flag

  • MSG_PEEK:允许从接收队列中查看数据而不将其删除。这意味着,如果接收队列中有数据,recv() 函数将返回数据的一个副本,但是该数据仍将留在接收队列中。这对于查看接收队列中的数据而不实际处理它们非常有用。此外,使用 MSG_PEEK 选项,我们可以检查套接字缓冲区中是否有足够的数据可供读取,以便稍后调用 recv() 函数。
  • MSG_WAITALL:如果套接字缓冲区中没有足够的数据,则 recv() 函数将一直等待,直到收到请求的数据量。
  • MSG_DONTWAIT:指定此标志后,recv() 函数将立即返回,即使没有收到数据也不会阻塞。如果没有数据可用,则 recv() 将返回 -1,并将 errno 设置为 EAGAIN 或 EWOULDBLOCK。
  • MSG_OOB:用于处理带外数据,即紧急数据。带外数据不遵循正常的传输控制协议(如 TCP),可以使用此标志将其标记为紧急数据并将其与其他数据分开处理。
  • MSG_TRUNC:如果接收缓冲区中的数据比接收缓冲区长度长,则截断数据并返回。
  • MSG_CTRUNC:如果接收缓冲区中的控制消息(例如带外数据或错误消息)比接收缓冲区长度长,则截断消息并返回。

setsockopt

函数原型

设置socket选项值

#include <sys/socket.h>

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

参数介绍

  • sockfd:指定 socket fd。
  • level:指定选项的协议层,可选 SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP 等。
  • optname:指定要设置的选项名。
    • SO_REUSEADDR:int 类型,表示重用 IP 地址。
    • SO_KEEPALIVE:int 类型,用于启用/禁用 Keepalive(保持连接)功能。
    • SO_LINGER:struct linger 类型,用于指定关闭套接字时的行为。
    • TCP_NODELAY:int 类型,用于禁用 Nagle 算法,从而实现数据的实时传输。
  • optval:指定存放选项值的缓冲区入口。
  • optlen:指定选项值缓冲区的长度。
  • 函数返回值:
    • 成功:0。
    • 失败:-1,并设置了 errno 错误码。

demo

服务端代码

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

#define PORT 8888
#define MAXLINE 1024

int main() {
    int sockfd, connfd;
    char buffer[MAXLINE];
    struct sockaddr_in servaddr, cli;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    // 创建TCP套接字
    if (sockfd == -1) {
        printf("socket creation failed");
        return EXIT_FAILURE;
    }

    // 设置服务器地址结构
    servaddr.sin_family = AF_INET; // IPv4
    servaddr.sin_addr.s_addr = INADDR_ANY;
    servaddr.sin_port = htons(PORT);

    // 绑定服务器套接字
    if (bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0) {
        printf("socket bind failed");
        return EXIT_FAILURE;
    }

    // 监听连接
    if (listen(sockfd, 5) != 0) {
        printf("listen failed");
        return EXIT_FAILURE;
    }

    socklen_t len = sizeof(cli);

    // 接受连接
    connfd = accept(sockfd, (struct sockaddr*)&cli, &len);
    if (connfd < 0) {
        printf("server accept failed");
        return EXIT_FAILURE;
    }

    // 读取客户端消息
    ssize_t num = recv(connfd, buffer, sizeof(buffer),0);
    if (num < 0){
        printf("read data from client fail, num=%ld\n", num);
        return EXIT_FAILURE;
    }
    printf("Client message: %s, size: %ld\n", buffer, num);

    char * data = "hello world";
    // 发送消息给客户端
    num = send(connfd, data, strlen(data),0);
    if (num < 0){
        printf("write data to client fail, num=%ld\n", num);
        return EXIT_FAILURE;
    }
    // 关闭连接
    close(sockfd);
    return 0;
}

客户端代码

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

#define PORT 8888
#define MAXLINE 1024

int main() {
    int sockfd;
    char buffer[MAXLINE];
    struct sockaddr_in servaddr;

    // 创建TCP套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        printf("socket creation failed\n");
        return EXIT_FAILURE;
    }

    // 设置服务器地址结构
    servaddr.sin_family = AF_INET; // IPv4
    servaddr.sin_port = htons(PORT);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    // 连接服务器
    if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0 ) {
        printf("connection with server failed\n");
        return EXIT_FAILURE;
    }
    char *data = "client send data";
    // 发送消息给服务器
    ssize_t num = send(sockfd, data, strlen(data),0);
    if (num < 0 )
    {
        printf("write data to server fail\n");
        return EXIT_FAILURE;
    }
    // 读取服务器响应
    num = recv(sockfd, buffer, sizeof(buffer),0);
    if (num < 0 )
    {
        printf("read data from client fail\n");
        return EXIT_FAILURE;
    }
    printf("Server response: %s\n", buffer);

    // 关闭连接
    close(sockfd);
    return 0;
}

测试结果

编译和运行服务端:在这里插入图片描述
编译和运行客户端:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构线性表——栈

前言&#xff1a;哈喽小伙伴们&#xff0c;今天我们将一起进入数据结构线性表的第四篇章——栈的讲解&#xff0c;栈还是比较简单的哦&#xff0c;跟紧博主的思路&#xff0c;不要掉队哦。 目录 一.什么是栈 二.如何实现栈 三.栈的实现 栈的初始化 四.栈的操作 1.数据入栈…

基于JavaWeb+SSM+校园零售商城微信小程序系统的设计和实现

基于JavaWebSSM校园零售商城微信小程序系统的设计和实现 源码获取入口前言主要技术系统设计功能截图Lun文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 前言 摘 要 在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应…

AYIT-ACM实验室发展历程

AYIT-ACM简介 ACM协会为你的梦想插上翅膀。 本院ACM协会成立于2012年 2008年开始小规模参加河南省竞赛 2014年成功实现金牌零突破 指导老师&#xff1a;孙高飞老师 安阳工学院计算机科学与信息工程学院ACM队是一支优秀的队伍&#xff0c;一支充满活力与激情的队伍&am…

【51单片机】之入门详解(一)

&#x1f4c3;博客主页&#xff1a; 小镇敲码人 &#x1f49e;热门专栏&#xff1a;C语言进阶 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&#x1f3fd;留言 &#x1f60d;收藏 &#x1f30f; 任尔江湖满血骨&#xff0c;我自踏雪寻梅香。 万千浮云遮…

MFC 简单绘图与文本编辑

目录 一.创建单文档项目 二.消息映射机制 三.WM_PAINT消息触发 四.CVIEW类 五.设备上下文 六.资源类和资源的关系 七.画线&#xff0c;矩形 八.画布 九.画笔 十.画刷 十一.利用TRACE打印日志 十二.文本编程 十三.ID号 十四.菜单栏 十五.菜单命令路由 十六.工具…

spring boot中使用Bean Validation做优雅的参数校验

一、Bean Validation简介 Bean Validation是Java定义的一套基于注解的数据校验规范&#xff0c;目前已经从JSR 303的1.0版本升级到JSR 349的1.1版本&#xff0c;再到JSR 380的2.0版本&#xff08;2.0完成于2017.08&#xff09;&#xff0c;目前最新稳定版2.0.2&#xff08;201…

Vue3 数据响应式原理:Proxy和Reflect

我们在Vue2中使用的是Object.defineProperty方法来实现数据响应式的&#xff0c;可以通过get和set方法来监听对象的访问和修改。 但是并不能响应对象中属性的增加和删除&#xff0c;只能使用Vue.$set 和Vue.$delete 来对对象中的属性进行增加和删除。 数组也不能直接通过下标…

从单服务设计看SLA保证

文章首发公众号&#xff1a;海天二路搬砖工 0. 引言 在微服务架构中&#xff0c;谈到SLA保证&#xff0c;我们更多是从宏观的角度来需求解决方案。比如&#xff0c;通过合理服务拆分来增加系统整体的可维护性&#xff1b;通过多实例部署来保证系统的灾备。但是单个服务是可靠…

vivado产生报告阅读分析-常规报告1

“ Report Utilization ” &#xff08; 使用率报告 &#xff09; 报告有助于从层级、用户定义的 Pblock 或 SLR 层面来分析含不同资源的设计的使用率。在流程中各步骤间使用 report_utilization Tcl 命令生成“ Utilization Report ”。 以下显示的报告详细信息适用于 Ultr…

Oracle(2-2)Oracle Net Architecture

文章目录 一、基础知识1、Oracle Net Connections Oracle网络连接2、C/S Application Connection C/S应用程序连接3、OSI Communication Layers OSI通信层4、Oracle Protocol Support Oracle协议支持5、B/S Application Connections B/S应用程序连接6、TwoTypes JDBC Drivers 两…

半导体电导率受哪些因素影响?如何正确测量半导体电导率?

半导体的电导率直接影响着半导体器件的工作状态&#xff0c;是半导体材料的重要参数。因此&#xff0c;半导体电导率的检测也是半导体设计和制造过程中的关键环节&#xff0c;确保半导体器件的性能、稳定性和可靠性。 什么是半导体电导率? 半导体电导率是指导电流在单位时间和…

保姆级Decimal.js的使用(如何解决js精度问题)

精度问题控制台图样 如果银行的业务你这样做&#xff0c;不知道要损失多少钱&#xff0c;这样是不行的&#xff0c;计算的不准确是需要背锅的&#xff0c;我们给后端去做吧&#xff0c;其实我们前端也是可以做的&#xff0c;引入Decimal.js 01.引入Decimal.js decimal.js是使用…

2023.11.14-hive之表操作练习和文件导入练习

目录 需求1.数据库基本操作 需求2. 默认分隔符案例 需求1.数据库基本操作 -- 1.创建数据库test_sql,cs1,cs2,cs3 create database test_sql; create database cs1; create database cs2; create database cs3; -- 2.1删除数据库cs2 drop database cs2; -- 2.2在cs3库中创建…

2023NewStarCTF

目录 一、阳光开朗大男孩 二、大怨种 三、2-分析 四、键盘侠 五、滴滴滴 六、Include? 七、medium_sql 八、POP Gadget 九、OtenkiGirl 一、阳光开朗大男孩 1.题目给出了secret.txt和flag.txt两个文件&#xff0c;secret.txt内容如下&#xff1a; 法治自由公正爱国…

MT8788核心板主要参数介绍_联发科MTK安卓核心板智能模块

MT8788核心板是一款功能强大的4G全网通安卓智能模块&#xff0c;具有超高性能和低功耗特点。该模块采用联发科AIOT芯片平台。 MT8788核心板搭载了12nm制程的四个Cortex-A73和四个Cortex-A53处理器&#xff0c;最高主频可达2.0GHZ。它还配备了4GB64GB(2GB16GB、3GB32GB)的内存&a…

新生儿母乳过敏:原因、科普和注意事项

引言&#xff1a; 母乳过敏是一种较为罕见但可能令家长担忧的现象。母亲通常认为母乳是新生儿最安全、最适合的食物&#xff0c;然而有时候宝宝可能对母乳中的某些成分产生过敏反应。本文将科普新生儿母乳过敏的原因&#xff0c;提供相关信息&#xff0c;并为父母和监护人提供…

JTS: 20 InteriorPoint 内部中心点

文章目录 版本代码 版本 org.locationtech.jts:jts-core:1.19.0 链接: github 代码 package pers.stu.algorithm;import org.locationtech.jts.algorithm.InteriorPoint; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; i…

OpenCV踩坑笔记使用笔记入门笔记整合SpringBoot笔记大全

springboot开启摄像头抓拍照片并上传实现&问题记录 NotAllowedErrot: 请求的媒体源不能使用&#xff0c;以下情况会返回该错误: 当前页面内容不安全&#xff0c;没有使用HTTPS没有通过用户授权NotFoundError: 没有找到指定的媒体通道NoReadableError: 访问硬件设备出错Ov…

CTFSHOW -SQL 注入

重新来做一遍 争取不看wp web171 基本联合注入 拿到题目我们已经知道了是sql注入 所以我们可以直接开始 第一题 不会难道哪里去 所以我们直接进行注入即可 1 and 12-- 1 and 11-- 实现闭合 -1unionselect1,2,3--%2b 查看字段数-1unionselect1,database(),3--%2b 查看数据…

初始MySQL(三)(合计函数,分组函数,字符串相关函数,数字相关函数,时间日期函数,加密函数,流程控制函数)

目录 合计/统计函数 count 返回行的总数 sum 合计函数 - avg group by 字符串相关函数 数学相关函数 时间日期相关函数 加密函数 流程控制函数 合计/统计函数 count 返回行的总数 Select count(*) | count (列名) from tablename [WHERE where_definition] #演…
最新文章