Socket

什么是字节序?

可参考这篇文章:​​​​​​数据存储:小端模式和大端模式——终于搞明白了!!!-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_45633061/article/details/117421452在计算机中存储数据是以字节为最小单位的,而地址有高低之分,存储的数据也有高低位之分

在一些计算机中,将高位字节存储在高位地址空间中,低位字节存储在低位地址空间中,这是小端存储

在另一些计算机,将高位字节存储在低位地址空间中,低位字节存储在高位地址空间中,这是大端存储

而一般情况下,我们的PC采用小端存储,网络通信中采用大端存储,因此我们需要对数据进行转化。

Linux字节序转换函数

主机字节序<->网络字节序:

小端转大端:inet_pton(ip版本,需要转换的那个小端字符串,转换后的大端数据存放的地址)

成功则返回1

大端转小端:inet_ntop(ip版本,需要转换的那个大端字符串,转换后的小端数据存放的地址,第三个参数的大小sizeof(dst))

成功则返回1

流式传输:连接通道类水流,发送端和接受端处理数据的速度,在连接过程中可以一直改变

socket通信过程

补充:

ip用来定位主机,端口号用来定位主机的某一个进程

因此,socket需要绑定至主机的某个端口才能使用。

服务器端有两类文件描述符socket

一类只能用来监听,一类用来通信

客户端只有一类socket文件描述符,用了通信

socket文件描述符与缓存的关系

一个文件描述符指向两个缓冲区,一个读缓存区,一个写缓存区

当我们需要发送数据时,利用write函数(其参数有,操作的socket,要写入的数据,要写入的数据的大小),将数据写入写缓存区,根据参数列表我们便能看出一些端倪,要想写入数据,就得找到写缓存区,要想找到写缓存区,就得找到对应的socket文件描述符,因为前面说到了,一个文件描述符指向两个缓冲区,一个读缓存区,一个写缓存区。

当我们需要接受数据时,也是同样的道理。

服务器端-连接准备

socket函数

socket(ip版本,传输方式:流式/报式,协议)

成功返回0,错误返回-1。socket系列函数有个规律:错误则返回-1

bind函数

bind(用于监听的文件描述符,要绑定的IP和端口的结构体的地址,第二个参数结构体的大小)

成功返回0,错误返回-1。

注意绑定时ip和端口必须是大端

这里我们常用sockaddr_in来初始化,然后再强制转换为sockaddr传给bind函数

上述的结构体如下:

sockaddr_in的源代码

listen函数

listen(用于监听的文件描述符,listen一次处理的最大连接数(不超过128) )

accept函数

accept(用于监听的文件描述符,存储发送方ip和端口的结构体的地址,存放第二个结构体大小的地址)

这里需注意,第一个参数 socket(文件描述符) 用于跟对方主机进行连接

注意第2,3个参数为地址,我们需要给出一个这两地址

连接成功后,accept会把对方主机的sockaddr数据写入我们给的地址

因此第2,3个参数其实是用来接收数据的,称他为传出参数(利用值传递,引用传递达成这个目的)

accept的返回值为一个用于通信的socket(文件描述符),若错误则会返回-1

客户端-连接准备

socket函数

socket客户端只需要初始化一个socket文本描述符,用于通信

connect函数

connect(用于通信的socket文本描述符,目标主机的IP端口的结构体,第二个参数指向的内存的大小)

这里的目标主机就需要我们初始化了。

观察发现,客户端并没有bind这个流程,这是为什么呢?

其实很好解释

首先,客户端当然也绑定了端口号,只不过是系统替我们绑定了,不需要我们手动操作

那为什么服务器端需要绑定呢?

这你就你需要理解,客户端和服务器之间的区别

服务器端是服务方,是被动连接的一方,他需要先开启,再确定好端口号,然后开始监听

当有客户端连接时,他便提供服务。客户端是主动连接的一方,因此他需要服务器的ip和端口号,若服务器没有一个固定的端口号,客户端怎么找?

做个比喻

服务器是一个邮箱,若其他人要投递,则邮箱必须先确定,邮箱在哪个小区(ip地址),门牌号是多少(端口号),然后其他人才能投递过来。

而客户端就相当于投递信件的人,投递方不需要一个固定的位置,在哪都可以投递,因此端口号是随机分配的。

当然在服务器端接收到数据之后,便知道客户端的ip和端口了

好比你收到信件之后,就知道对面的地址了。

客户端-服务器 连接完成,实现通信

通过以上的函数,客户端和服务器便能实现连接了,接下来是关于通信的函数

有两组函数,send和recv其实是write和read的封装

发送:write(), send()

参数:

第一个参数为用于通信的socket文件描述符;第二个参数为指针,指向一片内存空间(其实就是tcp滑动窗口那的缓存),同时注意细节,这里加了const修饰符,代表我只是读取,不会改变这片内存的内容;第三个参数为发送的字符串的大小。

send有4个参数,第4个参数我也不知道什么意思,老师说填0就可以了。

返回值:

>0:发送的字符串的大小,与第三个参数len相同

==-1:发送失败

接受:read(), recv()

参数:

第一个参数为用于通信的socket文件描述符;第二个参数为指针,指向一片内存空间(其实就是tcp滑动窗口那的缓存),同时注意细节,这里没有加const修饰符,因为我要向这篇内存中写入数据,需要改变这片内存的内容;第三个参数为参数2指向的内存的大小。

recv有4个参数,第4个参数,依旧填0就可以了。

返回值:

>0:实际接受到的字节数

==0:对方断开连接

==-1:接收发生了错误

断开连接

若需要断开连接,客户端关闭socket即可

注意,一方关闭socket即可断开连接,一般是由客户端断开连接

因为服务器一般是一直运行的,不会关闭socket

关闭socket

通信完毕后,就需要调用close函数了。

close(int fd)

参数为要关闭的文件描述符对应的socket

成功返回0,失败则返回-1

以上内容整理自教学视频——01-概述_哔哩哔哩_bilibili爱编程的大丙

  • 25
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值