C++网络编程——socket

news2024/12/24 21:43:39

在服务器中,需要建立一个socket套接字才能对外提供一个网络通信接口,在Linux系统中套接字仅是一个文件描述符,也就是一个int类型的值

socket概念

socket 的原意是“插座”,在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。

我们把插头插到插座上就能从电网上获得电力供应,同样为了与远程计算机进行数据传输,需要连接到因特网,而socket就是用来连接到因特网的工具

UNIX/Linux下的socket

在UNIX/Linux下,一切都是文件

为了表示和区分已经打开的文件,UNIX/Linux 会给每个文件分配一个 ID,这个 ID 就是一个整数,被称为文件描述符(File Descriptor)。例如:

  • 通常用 0 来表示标准输入文件(stdin),它对应的硬件设备就是键盘;

  • 通常用 1 来表示标准输出文件(stdout),它对应的硬件设备就是显示器。

UNIX/Linux 程序在执行任何形式的 I/O 操作时,都是在读取或者写入一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数,它的背后可能是一个硬盘上的普通文件、FIFO、管道、终端、键盘、显示器,甚至是一个网络连接。

请注意,网络连接也是一个文件,它也有文件描述符

Linux下socket使用

创建

#include<sys/socket.h>
int sockfd = socket(AF_INET,SOCK_STREAM,0);
  • 创建socket的API中第一个参数是IP地址类型,AF_INET表示使用IPv4,如果使用IPv6请使用AF_INET6,另外AF_UNIX则是Unix域套接字,即本地套接字

  • 第二个是参数为数据传输方式,SOCK_STREAM表示流格式、面向连接,多用于TCP。SOCK_DGRAM表示数据报格式、无连接,多用于UDP,至于TCP为什么为流格式,UDP为什么为面向报文,在专栏计算机网络部分为有详细解释。

  • 第三个参数:协议,0表示根据前面的两个参数自动推导协议类型。设置为IPPROTO_TCP和IPPTOTO_UDP,分别表示TCP和UDP。

sockadd_in结构体

对于客户端,服务器存在的唯一标识是一个IP地址和端口,这时候我们需要将这个套接字绑定到一个IP地址和端口上。首先创建一个sockaddr_in结构体

#include <arpa/inet.h>  //这个头文件包含了<netinet/in.h>,不用再次包含了
#include<string.h> //包含了bzero,如果是c使用string.h如果是C++使用cstring.h
struct sockaddr_in serv_addr;
bzero(&serv_addr, sizeof(serv_addr));

创建完后用bzero初始化这个结构体

sockaddr_in结构体内容如下:

struct sockaddr_in {
    sa_family_t sin_family;  // 地址族,通常设置为 AF_INET
    in_port_t sin_port;      // 端口号,网络字节序
    struct in_addr sin_addr; // IPv4 地址
};

其中IPV4地址结构体如下:

struct in_addr {
    in_addr_t s_addr; // 32位的 IPv4 地址,采用网络字节序
};

另外如果是在IPV6或者UNIX下本地套接字的话,要使用另外的结构体

  • IPV6:

struct sockaddr_in6 {
    sa_family_t     sin6_family;  // 地址族,通常设置为 AF_INET6
    in_port_t       sin6_port;    // 端口号,网络字节序
    uint32_t        sin6_flowinfo;// 流信息
    struct in6_addr sin6_addr;    // IPv6 地址
    uint32_t        sin6_scope_id;// 作用域ID
};
​
struct in6_addr {
    unsigned char   s6_addr[16]; // 128位的 IPv6 地址
};
  • UNIX:

struct sockaddr_un {
    sa_family_t sun_family; // 地址族,通常设置为 AF_UNIX
    char        sun_path[108]; // Unix socket 的路径名
};

通用的sockaddr结构体

struct sockaddr {
    sa_family_t sa_family;  // 地址族
    char        sa_data[14]; // 地址信息
};
  1. sa_family

    • 表示地址族,通常取值为 AF_INETAF_INET6AF_UNIX 等。

    • 用于确定后续的地址信息如何解释。

  2. sa_data

    • 一个14字节的地址信息数组。

    • 具体的地址信息格式取决于地址族。

在初始化IPV4的结构体后,就要对其设置地址族,IP地址和端口

serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(8888);

绑定

然后将socket地址与文件描述符绑定:

bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr));

为什么定义的时候使用专用socket地址,而绑定的时候转换为通用socket地址(sockaddr),这些内容在游双《Linux高性能服务器编程》第五章第一节:socket地址API中有详细讨论,我这里也进行一部分的引用加以说明

“通用socket地址结构体显然很不好用,比如设置与获取 IP地址和端口号就需要执行烦琐的位操作。所以Linux为各个协议族提 供了专门的socket地址结构体,同时所有专用socket地址类型的变量在实际使 用时都需要转化为通用socket地址类型sockaddr(强制转换即可),因 为所有socket编程接口使用的地址参数的类型都是sockaddr。”

inet_addr是将char类型字符串转变为网络字节序

#include <sys/socket.h>
​
#include <netinet/in.h>
​
#include <arpa/inet.h>
​
in_addr_t inet_addr(const char *cp); //将char类型字符串转变为网络字节序
​
char *inet_ntoa(struct in_addr in);  //将网络字节序转换为char类型

不过现在用这些比较少,

#include <arpa/inet.h>
    int inet_pton(int af, const char *src, void *dst);
    const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
      //af参数指明IP地址族,可以是AF_INET(IPv4)或AF_INET6(IPv6)。
      //src参数是指向二进制IP地址的指针。
      //dst是存储转换后的文本IP地址的缓冲区。
      //size参数指明dst缓冲区的大小。
      //该函数返回一个指向dst的指针,如果失败则返回NULL并设置errno。
    int inet_pton(int af, const char *src, void *dst);
      //af参数指明IP地址族,可以是AF_INET(IPv4)或AF_INET6(IPv6)。
      //src参数是指向文本IP地址的指针。
      //dst是存储转换后的二进制IP地址的缓冲区。
      //该函数返回值:
      //如果转换成功,返回1。
      //如果src参数不是有效的IP地址字符串,返回0。
      //如果出错,返回-1并设置errno。

例如:

#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<arpa/inet.h>
​
int main(){
  char buf[] = "192.168.1.2"; //低字节为2,高字节为192
  unsigned int name = 0;
  inet_pton(AF_INET,buf,&name);  //将点分十进制转换为网络字节序,大端存储,发送时用的多
  printf("name = %d\n",name); //0x0201a8c0
  unsigned char* p = (unsigned char*) &name;
  printf("%d,%d,%d,%d\n",*p,*(p+1),*(p+2),*(p+3)); //大端存储
  char ip[16] = "";
  inet_ntop(AF_INET,&name,ip,16); //将网络字节序转换为点分十进制  ,接收时用的多
  printf("%s",ip);
}

打印结果为:

name = 33663168 
192,168,1,2
192.168.1.2

监听

使用listen函数监听这个socket端口,这个函数的第二个参数是listen函数的最大监听队列长度,系统建议的最大值SOMAXCONN被定义为128。

listen(sockfd, SOMAXCONN);

要接受一个客户端连接,需要使用accept函数。对于每一个客户端,我们在接受连接时也需要保存客户端的socket地址信息,于是有以下代码:

struct sockaddr_in clnt_addr;
socklen_t clnt_addr_len = sizeof(clnt_addr);
bzero(&clnt_addr, sizeof(clnt_addr));
int clnt_sockfd = accept(sockfd, (sockaddr*)&clnt_addr, &clnt_addr_len);
printf("new client fd %d! IP: %s Port: %d\n", clnt_sockfd, inet_ntoa(clnt_addr.sin_addr), ntohs(clnt_addr.sin_port));

要注意和acceptbind的第三个参数有一点区别,对于bind只需要传入serv_addr的大小即可,而accept需要写入客户端socket长度,所以需要定义一个类型为socklen_t的变量,并传入这个变量的地址。另外,accept函数会阻塞当前程序,直到有一个客户端socket被接受后程序才会往下运行。

现在,客户端已经可以通过IP地址和端口号连接到这个socket端口了,让我们写一个测试客户端连接试试:

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr;
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(8888);
connect(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr));  

代码和服务器代码几乎一样:创建一个socket文件描述符,与一个IP地址和端口绑定,最后并不是监听这个端口,而是使用connect函数尝试连接这个服务器。

运行编译出来的./server和./client可以看到服务器接收到了客户端的连接请求,并成功连接

new client fd 3! IP: 127.0.0.1 Port: 53505

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

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

相关文章

HCIP-Datacom-ARST自选题库__BGP/MPLS IP VPN简答【3道题】

1.在BGP/MPLSIPVPN场景中&#xff0c;如果PE设备收到到达同一目的网络的多条路由时&#xff0c;将按照定的顺序选择最优路由。请将以下内容按照比较顺序进行排序。 2.在如图所示的BGP/MPLSIP VPN网络中&#xff0c;管理员准备通过Hub-Spoke组网实现H站点对VPM流量的集中管控&am…

1.2数学基础

向量运算 矩阵运算 比较基础就不记录了 MVP矩阵推导 1.讲为什么要有矩阵变换和不同的坐标空间 将3D物体转化到2D平面为各个空间的运用做准备 2.介绍各个空间的概念和含义 MVP矩阵代表什么&#xff1f; MVP矩阵分别是模型(Model)、观察(View)、投影(Projection)三个矩阵。…

Pycharm中如何引入gitee?

1、在Pycharm中打开settings&#xff0c;找到plugins&#xff0c;搜索gitee&#xff0c;点击install&#xff0c;点击ok即可。 2、下载安装好后&#xff0c;选择VCS&#xff0c;找到Get from version Control。 3、通过ssh公钥方式登录&#xff0c;ssh公钥可以gitee上获取 4、…

跨境电商拓海新策略:智能小家电遇全球机遇与挑战

全球化与互联网普及背景下&#xff0c;跨境电商成为企业拓展海外市场的重要途径。智能小家电以其创新性和实用性&#xff0c;逐渐成为跨境电商热门品类。面对全球市场的机遇与挑战&#xff0c;跨境电商企业需要深思如何制定有效的拓海新策略。 首先&#xff0c;智能小家电在全球…

IC开发——VCS基本用法

1. 简介 VCS是编译型verilog仿真器&#xff0c;处理verilog的源码过程如下&#xff1a; VCS先将verilog/systemverilog文件转化为C文件&#xff0c;在linux下编译链接生成可执行文件&#xff0c;在linux下运行simv即可得到仿真结果。 VCS使用步骤&#xff0c;先编译verilog源…

HT46R002 贴片 SOP8 经济型AD型OTP MCU单片机芯片

HT46R002在智能家居中的具体应用案例可以包括以下几个方面&#xff1a; 1. 智能照明控制&#xff1a;可以用于控制LED灯的亮度和色温&#xff0c;甚至可以通过手机APP远程控制开关和调节灯光效果。 2. 环境监测&#xff1a;用于监测室内温度、湿度、空气质量等&#xff0c;当检…

【busybox记录】【shell指令】ln

目录 内容来源&#xff1a; 【GUN】【ln】指令介绍 【busybox】【ln】指令介绍 【linux】【ln】指令介绍 使用示例&#xff1a; 创建链接文件 - 链接文件&#xff08;默认 - 硬链接&#xff09; 创建链接文件 - 链接文件&#xff08;软链接&#xff09; 创建链接文件 -…

李廉洋:5.29黄金震荡,原油持续走高,今日美盘行情走势分析及策略。

黄金消息面分析&#xff1a;当前美国存在一个令人担忧且未被充分关注的问题&#xff1a;房地产行业低迷、高利率和抵押贷款利率、租金高涨以及美联储的紧缩政策构成了一个恶性循环。由于高房价和高抵押贷款利率&#xff0c;美国住房经济活动远低于两年前的水平。为了让该行业好…

Android ANR Trace日志阅读分析技巧

什么是Trace日志 Trace日志是指ANR目录下的一份txt文件 adb pull /data/anr/traces.txt Trace日志有什么用 分析应用ANR无响应的问题&#xff0c; Trace怎么用 Cmd line: com.xx ABI: arm Build type: optimized Zygote loaded classes3682 post zygote classes3750 Intern…

SpringSecurity6从入门到实战之引言和基本概念

SpringSecurity6从入门到实战之引言和基本概念 前言 在当今数字化时代&#xff0c;随着网络应用的日益普及&#xff0c;保护用户数据和系统安全变得至关重要。作为Java开发社区的中坚力量&#xff0c;Spring框架提供了一整套解决方案来构建企业级应用程序。然而&#xff0c;随…

Android 13 VSYNC重学习

Android 13 VSYNC重学习 引言 学无止境&#xff0c;一个字干就完事&#xff01; 源码参考基于Android 13 aosp&#xff01; 一. Android VSync模块开胃菜 在开始正式的分析之前&#xff0c;我们先简单对Android的Vsync模块简单介绍下,如下图所示&#xff0c;其中: HW_VSync是…

Dolphinscheduler不重启加载Oracle驱动

转载自刘茫茫看山 问题背景 某天我们的租户反馈数据库连接缺少必要的驱动&#xff0c;我们通过日志查看确实是缺少部分数据库的驱动&#xff0c;因为DolphinScheduler默认只带了Oracle和MySQL的驱动&#xff0c;并且需要将pom文件中的test模式去掉才可以在打包的时候引入。我…

C# 中 async 与 await 关键字详解

async 和 await 关键字的作用是使方法能够异步执行并等待异步操作的完成。&#xff08;最重要的一点是记住 “异步执行”与“等待异步操作完成”&#xff0c;不是等待主线程操作完成&#xff09; async 修饰符可将 方法、lambda 表达式或匿名方法指定为异步。 async 关键字用于…

一张图一个表——CSS选择器总结

CSS选择器总结&#xff1a; (这些表是一张图片^_^) 看底部 完整思维导图图片和表格的下载地址&#xff1a;https://download.csdn.net/download/denlnyyr/10597820

LeetCode 377.组合总和Ⅳ

这题是我蠢了&#xff0c;它说是组合数我就信了&#xff0c;言尽于此 class Solution { public:int combinationSum4(vector<int>& nums, int target) {vector<int> dp(2000,0);dp[0]1;for(int i0;i<target;i){for(int j0;j<nums.size();j){if(i>nums…

22.Volatile原理

文章目录 Volatile原理1.Volatile语义中的内存屏障1.1.volatile写操作的内存屏障1.1.1.StoreStore 屏障1.1.2.StoreLoad 屏障 1.2.volatile读操作的内存屏障1.2.1.LoadStore屏障1.2.2.LoadLoad屏障 2.volatile不具备原子性2.1.原理 Volatile原理 1.Volatile语义中的内存屏障 在…

定位器与PWM的LED控制

文章目录 一、STM32定时器二、脉宽调制pwm三、定时器控制led&#xff08;1&#xff09;实验内容&#xff08;2&#xff09;创建工程&#xff08;3&#xff09;Keli程序&#xff08;4&#xff09;观察波形图&#xff08;5&#xff09;实物连接图&#xff08;6&#xff09;实践效…

Python机器学习 Tensorflow + keras 实现CNN

一、实验目的 1. 了解SkLearn Tensorlow使用方法 2. 了解SkLearn keras使用方法 二、实验工具&#xff1a; 1. SkLearn 三、实验内容 &#xff08;贴上源码及结果&#xff09; 使用Tensorflow对半环形数据集分 #encoding:utf-8import numpy as npfrom sklearn.datasets i…

AI图书推荐:用ChatGPT和Python搭建AI应用来变现

《用ChatGPT和Python搭建AI应用来变现》&#xff08;Building AI Applications with ChatGPT API&#xff09;将ChatGPT API与Python结合使用&#xff0c;可以开启构建非凡AI应用的大门。通过利用这些API&#xff0c;你可以专注于应用逻辑和用户体验&#xff0c;而ChatGPT强大的…

clocking wizard IP核通过AXI4-Lite接口实现动态重新配置应用实例

在最近的FPGA应用中&#xff0c;应用到了基于Zynq 7000的Uart串口设计&#xff0c;为了让串口的时钟更精确&#xff0c;采用了外部时钟模式&#xff0c;如下图所示。外部时钟连接到了Clocking Wizard IP核的输出端。 在串口通信时&#xff0c;发现串口有错码出现。例如&#xf…