Socket编程入门

news2024/11/24 12:56:43

Socket编程

套接字的类型

套接字分为两种类型

  • Stream Sockets,流格式,传输使用的是TCP协议
  • Datagram Sockets,数据包格式,传输使用的是UDP协议

结构体

在不同类型电脑中,字节的排列顺序是不同的:大端序是高位字节存入低地址,低位字节存储高地址;小端序则相反。网络协议都是通过大端的方式传输数据的。

被套接字用到的各种数据类型

  • socket描述符:int类型

  • struct sockaddr:为许多类型的套接字存储套接字地址信息

    struct sockaddr {
        unsigned short sa_family;  // 地址家族, 设置成AF_INET
        char sa_data[14];
    };
    
  • 为了处理struct sockaddr,程序员创造了一个并列的结构:struct sockaddr_inin代表Internet

    struct sockaddr_in {
        short int sin_family;  // 通信类型, 设置成AF_INET
        unsigned short int sin_port;  // 2字节,端口
        struct in_addr sin_addr;  // Internet 地址
        unsigned char sin_zero[8];  // 为了使该结构体和sockaddr结构的长度相同
    };
    
    struct in_addr {
        unsigned long s_addr;  // 4字节
    };
    

字节序

库中提供了从本机字节序转换到网络字节序的函数

#include <arpa/inet.h>

htons();  // Host to Network Short
htonl();  // Host to Network Long
ntohs();  // Network to Host Short
ntohl();  // Network to Host Long

在数据结构struct sockaddr_in中,sin_addrsin_port需要转换为网络字节序,而sin_family不需要,因为sin_family被内核用来决定数据结构中包含什么类型的地址,只是内核在用,不发送到网络上,所以用本地字节序。
上面的这些函数也适用unsigned类型的参数

如何处理IP地址

如何使用将IP地址转换为unsigned long类型存储在sockaddr_in中呢?可以使用inet_addr()将IP地址从点分十进制转换成无符号长整型:

#include <arpa/inet.h>

// struct sockaddr_in ina;
ina.sin_addr.s_addr = inet_addr("132.241.5.10");

值得注意的是,如果给出的ip地址不合法,那么inet_addr将返回-1,而-1转换出的ip地址为广播地址255.255.255.255,所以在使用之前一定要进行检查。

也可以使用相反的方法将长整型的IP地址转换为点分十进制的字符串:

#include <arpa/inet.h>

printf("%s", inet_ntoa(ina.sin_addr));  // 注意这里传入的是结构体

inet_ntoa()每次调用返回的是一个指向一个字符的指针,这个指向的内存是由函数管理的,每次调用都会覆盖上次调用存下来的IP地址,所以多次调用需要用strcopy()函数来存储结果。

socket()函数

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

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

domain应该设置成AF_INET,和struct sockaddr_in中一样;参数type告诉内核是SOCK_STREAM类型还是SOCK_DGRAM类型;最后把protocol设置成0 ,protocol也可以通过getprotobyname()返回的结构体中得到,详细信息可以查看man手册。

socket()返回文件描述符,这个描述符可能在之后的各种系统调用中用到;错误时返回-1,错误类型存储在全局变量errno中。

bind()函数

拥有套接字之后,就需要把套接字和机器上的一定的端口关联起来。

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

int bind(int sockfd, struct sockaddr *my_addr, int addrlen);

sockfd是调用socket()返回的文件描述符;my_addr是指向struct sockaddr的指针;addrlen就是sizeof(struct sockaddr)

image-20230821161247302

在处理IP地址和端口的时候,有些东西是可以自动处理的

my_addr.sin_port = 0;  // 随机选择一个没有使用的端口
my_addr.sin_addr.s_addr = INADDR_ANY;  // 使用自己的IP地址, 内核会自动处理成自己的IP地址

connect()函数

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

int connect(int sockfd, struct sockadddr *serv_addr, int addrlen);

参数格式和bind()函数一模一样

image-20230821163358379

在这里并没有绑定本地的端口,仅仅设置了对方的端口,因为在连接对方的时候,本地端口并不重要,内核将去自动选择一个合适的端口号。

listen()函数

用来等待接入请求,并在后面的accept()中处理请求

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

int listen(int sockfd, int backlog);

sock是调用soket()返回的套接字文件描述符;backlog是允许进入队列的连接数目。

accept()函数

现在的情景是,有人从很远的地方通过一个你正在监听(listen())的端口连接(connect())到你的电脑。他的连接将加入到等待接受(accept())的队列中。你调用accept()告诉他有空闲的连接,它将返回一个新的套接字文件描述符。现在你就有了两个套接字,一个还在监听端口,新的准备发送(send())和接收(recv())数据。

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

int accept(int sockfd, struct sockadddr *addr, int *addrlen);
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>

#define MYPORT 3490
#define BACKLOG 10

int main(void)
{
    int sockfd, newfd;
    struct sockaddr_in my_addr;
    struct sockaddr_in their_addr;
    int sin_size;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    assert(sockfd != -1);

    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(MYPORT);
    my_addr.sin_addr.s_addr = INADDR_ANY;

    bzero(my_addr.sin_zero, sizeof(my_addr.sin_zero));

    bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));

    listen(sockfd, BACKLOG);

    sin_size = sizeof(struct sockaddr_in);

    newfd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);

    return 0;
}

执行后可以发现程序被阻塞在accept一行,当有另外一个程序执行connect请求连接本地IP的3490端口时,程序就会继续执行。

send()recv() 函数

这两个函数适用于流式套接字;对于无连接的数据包套接字应该使用sendto()recvfrom()

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

int send(int sockfd, const void *msg, int len, int flags);

sockfd是套接字文件描述符;msg指向要发送数据的指针;len是数据的长度;flags设置成0就可以了,其他的设置选项可以在man中查看。函数返回成功发送的字节数。

image-20230822100347962

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

int recv(int sockfd, void *buf, int len, int flags);  // len需要传递buf指向内存的大小

如果recv()接收到字符串大小超过len,那么就会丢弃多出来的字符串。

image-20230822100234385

先运行服务端再运行客户端就可以在客户端看到服务端发送的消息了。

sendtorecvfrom

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

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

前四个参数和send的一样,dest_addr是要发送信息的目的信息,addrlen可以设置成sizeof(struct sockaddr)

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

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

前四个参数和recv的一样,后面两个参数是传出参数,里面记录了消息来源的IP、端口等信息。

为了能够正常收发信息,我们需要先明白服务器和客户端各需要什么信息:服务器要知道客户端的ip和端口信息,服务器自己的IP可以自动获取,端口可以让内核自动选择;客户端要绑定自己的ip和端口,客户端不需要关心具体的某个服务器,只需要关注消息是否发给自己就好了。这样消息就可以正常收发了。

SOCK_DGRAM类型的套接字也可以使用connect()进行连接,连接后就可以简单的调用send()recv()来收发消息,此时依然使用的是UDP,系统会自动加上目标信息和源信息。

close()shutdown()函数

close可以用来关闭socket文件描述符

#include <unistd.h>
int close(int fd);

它将防止套接字上还有数据的读写。此时在套接字的另一端进行读写都将返回错误信息。如果要在关闭套接字上有更多的控制,可以使用shutdown()

#include <sys/socket.h>

int shutdown(int sockfd, int how);

这里的how有三个取值

SHUT_RD;  // 禁止读取
SHUT_WR;  // 禁止发送
SHUT_RDWR;  // 禁止发送和读取

getpeername()gethostname()

#include <sys/socket.h>

int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);

获取套接字对面的地址信息,addrlen应该初始化成addr的内存大小,在执行完之后addrlen会存放addr真正存放的数据大小。

#include <unistd.h>

int gethostname(char *name, int len);

返回程序所运行的主机名字,然后可以使用gethostbyname()来获得机器的IP地址

域名服务

DNS代表域名服务(Domain Name Service),功能是把网站地址转换成IP地址。

#include <netdb.h>
struct hostent *gethostbyname(const char *name);
struct hostent {
    char *h_name;  // 服务器的名字
    char **h_aliases;  // 服务器替代名字的数组,数组最后一个元素是NULL
    int h_addrtype;  // 地址类型,AF_INET或者是AF_INET6
    int h_lengeh;  // 地址长度,单位比特
    char **h_addr_list;  // 服务器IP地址数组(字节序是网络序,输出需要转换),数组最后一个元素是NULL
};
#define h_addr h_addr_list[0]
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    struct hostent *h;
    if (argc != 2) { /* 检查命令行 */
        fprintf(stderr,"usage: getip address\n");
        exit(1);
    }

    if ((h=gethostbyname(argv[1])) == NULL) { /* 获得地址信息 */
        herror("gethostbyname");
        exit(1);
    }

    printf("Host name : %s\n", h->h_name);
    printf("IP Address : %s\n", inet_ntoa(*((struct in_addr *)h->h_addr)));  // 函数接收参数类型为结构体

    char** p = h->h_aliases;
    printf("Host aliases name :\n");
    for (int i = 0; p[i] != NULL; ++i) {
        printf("%s\n", p[i]);
    }

    printf("Host address list :\n");
    p = h->h_addr_list;
    for (int i = 0; p[i] != NULL; ++i) {
        printf("%s\n", inet_ntoa(*((struct in_addr *)p[i])));
    }
    return 0;
}

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

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

相关文章

uniapp项目添加人脸识别功能,可用作登录,付款,流程审批前的安全校验

本案例使用了hbuilder插件商城中的活体检验插件&#xff0c;可自行前往作者处下载查看&#xff0c; 效果图如下 此插件需要在manifest.json中勾选 实现流程 1&#xff1a;前往hbuilder插件市场下载插件 2&#xff1a;在页面中导入import face from "/uni_modules/mcc-…

成集云 | 维格表会员/租约/合同到期同步企微提醒 | 解决方案

源系统成集云目标系统 方案介绍 维格表是一种新一代的团队数据协作和项目管理工具&#xff0c;由深圳维格智数科技有限公司研发。它结合了可视化数据库、电子表格、实时网络协同、低代码开发技术四项功能&#xff0c;且支持API与可视化看板&#xff0c;操作简单&#xff0c;能…

【数据结构入门指南】二叉树

【数据结构入门指南】二叉树 一、二叉树的概念二、现实中的二叉树三、特殊的二叉树四、二叉树的性质五、二叉树的存储结构5.1 顺序结构5.2 链式结构 一、二叉树的概念 二叉树是一棵特殊的树。一棵二叉树是结点的一个有限集合&#xff0c;该节点&#xff1a; ①&#xff1a;或者…

Vue 2 插槽

可以先阅读组件基础-简单了解通过插槽分发内容。 一、插槽定义 插槽将子组件标签间的内容分发到子组件模板的<slot>标签位置。 如果没有<slot>标签&#xff0c;那么该内容将被丢弃。 二、编译作用域 内容在哪个作用域编译&#xff0c;就可以访问哪个作用域的数据…

SMS 与 WhatsApp 营销,哪个方式最适合你的业务?

SMS和 WhatsApp营销越来越受欢迎&#xff0c;因为它们为企业提供了接触目标受众的有效方式。超过 91%的客户希望收到来自企业的 SMS消息&#xff0c;使用WhatsAppAPI发送的消息的打开率高达99% &#xff0c;这证明了这两种形式的消息传递对于希望及时与客户沟通的企业来说变得重…

火山引擎发布自研视频编解码芯片 压缩效率提升30%

8月22日&#xff0c;火山引擎视频云宣布其自研的视频编解码芯片已成功出片。经验证&#xff0c;该芯片的视频压缩效率相比行业主流硬件编码器可提升30%以上&#xff0c;未来将服务于抖音、西瓜视频等视频业务&#xff0c;并将通过火山引擎视频云开放给企业客户。 火山引擎总裁…

C语言好题解析(四)

目录 选择题一选择题二选择题三选择题四选择题五编程题一 选择题一 已知函数的原型是&#xff1a; int fun(char b[10], int *a); 设定义&#xff1a; char c[10];int d; &#xff0c;正确的调用语句是&#xff08; &#xff09; A: fun(c,&d); B: fun(c,d); C: fun(&…

安防视频监控平台EasyNVR平台启用国标级联的操作步骤来啦!

安防视频监控汇聚EasyNVR视频集中存储平台&#xff0c;是基于RTSP/Onvif协议的安防视频平台&#xff0c;可支持将接入的视频流进行全平台、全终端分发&#xff0c;分发的视频流包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等格式。 为提高用户体验&#xff0c;让用户更加便捷…

再JAVA中如何使用qsort对类进行排序?

目录 结论&#xff1a; 解析&#xff1a; 结论&#xff1a; import java.util.Arrays;class Person implements Comparable<Person>{public String name;public int age;public Person(String name, int age) {this.name name;this.age age;}Overridepublic Stri…

关于Springboot项目打包的配置问题

一、打包方式的不同致使jar包运行性能及docker部署的效率问题 1.1方式一 <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source&…

保护函数返回的利器——Linux Shadow Call Stack

写在前面 提到内核栈溢出的漏洞缓解&#xff0c;许多朋友首先想到的是栈内金丝雀&#xff08;Stack Canary&#xff09;。今天向大家介绍一项在近年&#xff0c;于Android设备中新增&#xff0c;且默默生效的安全机制——影子调用栈&#xff1a;SCS&#xff08;Shadow Call St…

GEE/PIE遥感大数据处理与典型案例

随着航空、航天、近地空间等多个遥感平台的不断发展&#xff0c;近年来遥感技术突飞猛进。由此&#xff0c;遥感数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量也大幅增长&#xff0c;使其越来越具有大数据特征。对于相关研究而言&#xff0c;遥感大数据的出现为其提…

亚马逊前台又更新了?这个功能有点意思!

亚马逊最近动作频频&#xff0c;之前听说过&#xff0c;亚马逊的IT团队换了一批新人&#xff0c;目前界面也在进行迭代改版。只不过各项新功能的改版&#xff0c;让卖家们应接不暇。由于新功能的改变都会对卖家们的业务产生影响&#xff0c;这直接关系到卖家的切身利益&#xf…

leetcode1109. 航班预订统计(java)

差分数组 leetcode1109. 航班预订统计差分数组解题代码演示 上期经典 leetcode1109. 航班预订统计 难度 - 中等 原题链接 - 1109. 航班预订统计 这里有 n 个航班&#xff0c;它们分别从 1 到 n 进行编号。 有一份航班预订表 bookings &#xff0c;表中第 i 条预订记录 bookings…

查询速度最高提升50倍!火山引擎ByteHouse在广告投放领域实践分享

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 据QuestMobile报告显示&#xff0c;移动互联网已经进入了下半场&#xff0c;在使用人数和使用时长方面已经没有明显增长&#xff0c;互联网已经流量趋于饱和。 作为…

【升职加薪秘籍】我在服务监控方面的实践(6)-业务维度的mysql监控

大家好,我是蓝胖子&#xff0c;关于性能分析的视频和文章我也大大小小出了有一二十篇了&#xff0c;算是已经有了一个系列&#xff0c;之前的代码已经上传到github.com/HobbyBear/performance-analyze&#xff0c;接下来这段时间我将在之前内容的基础上&#xff0c;结合自己在公…

电脑出现msvcp120.dll丢失的解决方法,教你三招快速解决

msvcp120.dll丢失是一件很常见的问题&#xff0c;出现msvcp120.dll丢失会导致电脑无法在正常运行&#xff0c;那么应该怎么解决这个问题呢&#xff0c;有什么办法可以快速的解决呢&#xff0c;今天教你三招快速解决msvcp120.dll丢失的方法。 一.msvcp120.dll文件丢失可能导致的…

深度学习入门(五):经典网络Alexnet实现

介绍 AlexNet是一个深度卷积神经网络架构&#xff0c;于2010年代初在深度学习重新引起人们关注时发挥了重要作用。它因在2012年的ImageNet大规模视觉识别挑战&#xff08;ILSVRC&#xff09;中获胜而闻名。 实现 创建了一个AlexNet网络的实例&#xff0c;并将一个随机生成的…

云服务之PaaS:PaaS怎么帮助企业走向云

一、前言 企业想要平滑地向云迁移&#xff0c;需要选择一个适合自己的云基础框架。PaaS(平台即服务)作为云计算的重要组成部分&#xff0c;可以为用户提供完整的应用生命周期管理和相关资源服务。开发人员可以在PaaS框架构建和自定义应用程序&#xff0c;这为企业大大地节省了金…

金桥跨越相伴岁月 桂冠加冕爱意时光 GP芝柏表铭刻相爱传奇 演绎浪漫七夕

两个多世纪以前&#xff0c;康士坦特芝勒德 (Constant Girard) 与玛莉亚柏雷戈 (Marie Perregaux) 喜结连理&#xff0c;两颗心灵在爱意中交织&#xff0c;二人将姓氏结合&#xff0c;创立“Girard-Perregaux”芝柏表&#xff0c;成为数百年来瑞士高级制表中仅有的以夫妻双人姓…