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 1024int 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; // IPv4servaddr.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 1024int 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; // IPv4servaddr.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.rhkb.cn/news/190363.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智慧工地源码:助力数字建造、智慧建造、安全建造、绿色建造

智慧工地围绕建设过程管理&#xff0c;建设项目与智能生产、科学管理建设项目信息生态系统集成在一起&#xff0c;该数据在虚拟现实环境中&#xff0c;将物联网收集的工程信息用于数据挖掘和分析&#xff0c;提供过程趋势预测和专家计划&#xff0c;实现工程建设的智能化管理&a…

Golang源码分析 | 程序引导过程

环境说明 CentOS Linux release 7.2 (Final&#xff09; go version go1.16.3 linux/amd64 GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7使用gdb查看程序入口 编写一个简单的go程序 // main.go package mainfunc main() {print("Hello world") } 编译go …

假冒 Skype 应用程序网络钓鱼分析

参考链接: https://slowmist.medium.com/fake-skype-app-phishing-analysis-35c1dc8bc515 背景 在Web3世界中&#xff0c;涉及假冒应用程序的网络钓鱼事件相当频繁。慢雾安全团队此前曾发表过分析此类网络钓鱼案例的文章。由于Google Play在中国无法访问&#xff0c;许多用户…

K8S知识点(十)

&#xff08;1&#xff09;Pod详解-启动命令 创建Pod&#xff0c;里面的两个容器都正常运行 &#xff08;2&#xff09;Pod详解-环境变量 &#xff08;3&#xff09;Pod详解-端口设置 &#xff08;4&#xff09;Pod详解-资源配额 修改&#xff1a;memory 不满足条件是不能正常…

Django之三板斧的使用,全局配置文件介绍,request对象方法,pycharm链接数据库,Django链接数据库,ORM的增删改查

【1】三板斧(3个方法)的使用 Httpresponse() 括号内写什么字符串&#xff0c;返回的就是什么字符串返回的是字符串 render(request&#xff0c; 静态文件 ) request是固定的静态文件是写在templates文件夹里面的&#xff0c;如&#xff0c;HTML文件 redirect( 重定向的地址 ) 重…

Hadoop原理,HDFS架构,MapReduce原理

Hadoop原理&#xff0c;HDFS架构&#xff0c;MapReduce原理 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c…

LeetCode刷题总结(一)

文章目录 前言题型排序问题动态规划 前言 本文把刷题过程中的总结记下来&#xff0c;方便未来回顾的时候继续拓展。 题型 排序问题 排序问题的解决方法有很多。对于简单算法来说&#xff0c;最重要的是记住思路&#xff1b;对于高级算法来说&#xff0c;最重要的是记住细节…

利用LangChain实现RAG

检索增强生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;结合了搜寻检索生成能力和自然语言处理架构&#xff0c;透过这个架构&#xff0c;模型可以从外部知识库搜寻相关信息&#xff0c;然后使用这些信息来生成response。要完成检索增强生成主要包含四个步骤…

2023亚太杯数学建模A题思路

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料5 最后 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 2023年第十三…

【中间件篇-Redis缓存数据库08】Redis设计、实现、redisobject对象设计、多线程、缓存淘汰算法

Redis的设计、实现 数据结构和内部编码 type命令实际返回的就是当前键的数据结构类型&#xff0c;它们分别是&#xff1a;string(字符串)hash(哈希)、list(列表)、set(集合)、zset (有序集合)&#xff0c;但这些只是Redis对外的数据结构。 实际上每种数据结构都有自己底层的…

【DP】背包问题全解

一.简介 DP&#xff08;动态规划&#xff09;背包问题是一个经典的组合优化问题&#xff0c;通常用来解决资源分配的问题&#xff0c;如货物装载、投资组合优化等。问题的核心思想是在有限的资源约束下&#xff0c;选择一组物品以最大化某种价值指标&#xff0c;通常是总价值或…

【Java 进阶篇】Java与JQuery选择器:解锁前端开发的魔法大门

在前端开发的世界中&#xff0c;选择器是我们与HTML文档进行互动的钥匙&#xff0c;而Java和JQuery则为我们提供了强大的工具&#xff0c;使得前端开发不再是一个艰深的谜题。本篇博客将围绕Java与JQuery选择器展开&#xff0c;深入解析选择器的奥秘&#xff0c;为你打开前端开…

Qt文档阅读笔记-Fetch More Example解析

Fetch More Example这个例子说明了如何在视图模型上添加记录。 这个例子由一个对话框组成&#xff0c;在Directory的输入框中&#xff0c;可输入路径信息。应用程序会载入路径信息的文件信息等。不需要按回车键就能搜索。 当有大量数据时&#xff0c;需要对视图模型进行批量增…

宝塔开心版hostcli的广告去除

首先感谢hostcli把宝塔7.6剥离了&#xff0c;直接安装我这里是缺少pyenv的包。 直接进入正题吧。 定位到页面左下方的广告位于 /www/server/panel/BTPanel/templates/default/layout.html “退出”按钮下方有条线开始去掉 去掉之前的忘了截图了&#xff0c;就这样吧&#xff…

《QT从基础到进阶·十七》QCursor鼠标的不同位置坐标获取

一些常用鼠标图形&#xff1a; 鼠标光标相对于整个电脑屏幕的位置&#xff1a;QCursor::pos() 当前光标相对于当前窗口的位置&#xff1a;this->mapFromGlobal(QCursor::pos()) void MainWindow::mouseReleaseEvent(QMouseEvent* event) {QPoint pos event->pos(); …

06-解决Spirng中的循环依赖问题

Bean的循环依赖问题 循环依赖: A对象中有B属性 , B对象中有A属性(丈夫类Husband中有Wife的引用, 妻子类Wife中有Husband的引用) toString()方法重写时直接输出wife/husband会出现递归导致的栈内存溢出错误 直接输出wife/husband会调用它们的toString()方法, 在toString()方法…

Spring的Redis客户端

如何在Spring中操作redis 在创建springboot项目的时候引入redis的依赖. 在配置文件里指定redis主机的地址和端口,此处我们配置了ssh隧道,所以连接的就是本机的8888端口. 创建一个controller类,注入操作redis的对象. 前面使用jedis,是通过jedis对象里的各种方法来操作redis的,此…

在任何机器人上实施 ROS 导航堆栈的指南

文章目录 路径规划参考 路径规划 路径规划是导航的最终目标。这允许用户向机器人给出目标姿势&#xff0c;并让它在给定的环境中自主地从当前位置导航到目标位置。这是我们迄今为止所做的一切&#xff08;地图绘制和本地化&#xff09;的汇集点。ROS 导航堆栈已经为我们完成了…

通讯协议学习之路(实践部分):SPI开发实践

通讯协议之路主要分为两部分&#xff0c;第一部分从理论上面讲解各类协议的通讯原理以及通讯格式&#xff0c;第二部分从具体运用上讲解各类通讯协议的具体应用方法。 后续文章会同时发表在个人博客(jason1016.club)、CSDN&#xff1b;视频会发布在bilibili(UID:399951374) 本文…

【PG】PostgreSQL 预写日志(WAL)、checkpoint、LSN

目录 预写式日志&#xff08;WAL&#xff09; WAL概念 WAL的作用 WAL日志存放路径 WAL日志文件数量 WAL日志文件存储形式 WAL日志文件命名 WAL内容 检查点&#xff08;checkpoint&#xff09; 1 检查点概念 2 检查点作用 触发检查点 触发检查点之后数据库操作 设置合…