答案:当然不是这样的,事实上是应用层在通信 解析:网络协议中的下三层,主要是解决的是将数据安全可靠的送到远端机器,用户使用应用层软件实现数据的发送和接收。而在使用软件的时候,必须要先启动这个软件,例如我们现在要使用微信,运行之后这个就是进程。所以网络在进行通信的时候就是进程间通信!!!只不过是进程之间遵守了网络协议栈,用的是网络协议的系统调用接口罢了,但是其本质还是进程间通信!!!手段是两台主机通信。而目的和本质是进程之间的通信,是凌驾于应用层上的进程间通信。通过网络协议栈来读取网络资源(共享内存资源)来让两台主机读取/存放信息。可以用读者/写者问题来理解这个问题,我们的网络资源就对应着缓冲区这个概念,读者写者在这个缓冲区(网络资源)中读取和存放资源。
端口号是一个两字节十六位的整数 端口号用来标识一个进程,告诉操作系统,当前的数据要交给哪个进程来处理 我们上一篇文章说过,IP是独一无二的,每个主机只有一个IP,所以IP地址+端口号就可以描述一个独一无二的进程。一个端口号只能被一个进程所占用。
特别注意的概念:
在公网上,ip值能标识唯一的一台主机,port端口号能标识该主机上唯一的一个进程。如ip:port能标识全网唯一一个进程。 socket:客户端和服务器在进行通信的时候,客户端拥有唯一的ip值加端口号,服务端进程也有唯一的ip值+端口号,所以客户端和服务端只需要记住对方的源和目的的IP值+端口号就就可以进行两者之间的通信。这就是socket
我们之前在讲进程的时候说过,一个进程有唯一的pid,既然pid已经能够标识一台主机上的一个进程的唯一性,那为什么还要有端口号这个概念呢? 答案:引入端口号是为了实现系统和网络之间的解耦合,以免后期造成其他不必要的影响
每一个服务器的端口号必须是众所周知的精心设计的,要被客户端熟知的,抖音自己的开发商在开发的时候,客户端和服务端的端口号都是内置的,都是被自己熟知的,所以我们使用者感觉不到这个,但是作为开发人员是要熟知服务器的端口号的。
显而易见:一个进程可以绑定多个端口号,但一个端口号不能被多个进程绑定。
传输层协议 有连接 可靠传输(前提网络要联通,复杂,维护性要更强) 面向字节流
传输层协议 无连接 不可靠传输(简单,但丢包问题解决不了) 面向数据报
我们已经知道,内存中的多字节数据相对于内存地址有着大端和小端之分,磁盘文件中的数据相对于文件中的偏移地址也有着大端小端之分,网络数据流同样有着大端小端之分,那么如何定义网络数据流的地址呢?
发送主机通常按照内存从低到高的顺序往内存缓冲区中发送数据,接受主机也是按照内存从低到高的顺序去接说数据,然后保存 因此,网络数据流的地址应该这样规定,先发出的地址是低地址,后发出的地址是高地址 TCP/IP协议规定,网络数据流应该采用大端字节序,即低地址高字节 不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据; 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
这些函数名很好记:h标识host,n标识network,l标识32位的长整数,s标识十六位短整数 例如htonl表示的就是将32位的长整数从主机字节序转换为网络字节序 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回; 如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,
socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
问题:为什么struct sockaddr* 不能写成void*呢? 因为刚开始网络出现的时候c语言还没有void*这个用法。
udp用的是SOCK_DGRAM,那么就是无连接不可靠的协议。
客户端是要有具体的ip值和port端口号的,所以是需要绑定的,但是很多人说不需要绑定,所以这种说法一定是错误的。有这种说法的原因是一般客户不需要自己显示的去绑定,但是操作系统是会进行随机选择的绑定的。 解释:前面我们说过了,一个端口号只能被一个进程所绑定,server端如此,client端也是如此,但是计算机有着成千上万个应用,虽然我们不可能一次性全部打开,但是我们总是会有开很多的情况, 我们假如说是这前十几个应用把常见的端口号都占了的话,我们后面的应用进程开启来的时候,就不能用这几个常见端口号,打开一次失败一次,试完了这几个常见端口号以后,再用其他冷门的端口号启动就变得很慢,用户自定义的端口号是很粗糙很不安全的,所以由OS操作系统随机的分配更加的安全和有效公平。其实客户端的端口号是多少并不重要,只要能够保证主机上的唯一性即可。 系统什么时候给我绑定端口号的呢?首次发送数据的时候,客户端就进行随机绑定端口号了(即客户端代码跑到sendto的时候)。
代码:Liunx仓库(1): Linux学习相关代码 - Gitee.com
字符串转in_addr的函数:
in_addr转字符串的函数:
其中inet_pton和inet_ntop不仅可以转换IPv4的in_addr,还可以转换IPv6的in6_addr,因此函数接口是void*addrptr
inet_ntoa这个函数返回了一个char*, 很显然是这个函数自己在内部为我们申请了一块内存来保存ip的结果. 那么是否需要调用者手动释放呢?
man手册上说, inet_ntoa函数, 是把这个返回结果放到了静态存储区. 这个时候不需要我们手动进行释放. 那么问题来了, 如果我们调用多次这个函数, 会有什么样的效果呢? 参见如下代码:
因为inet_ntoa把结果放到自己内部的一个静态存储区, 这样第二次调用时的结果会覆盖掉上一次的结果.