对于这个linux的网络接口,在linux中一且都是文件,所有跟文件一下有一个file descriptor来进行写入,读取
我们使用socket得到一个socket descriptor来写入读取
使用send() 和recv() 进行发送,更好的控制流量
我们在这里学习的只有两种internet Socket 除了这个还有rawSocket等
两种internet socket 为 Stream Socket 和 Datagram Socket
Stream Socket 是可靠的双向连接,它使用的是TCP(The Transmission Control Protocol) 传输控制协议 Datagram Socket 是无连接的,使用UDP(User Datagram Protocal) 用户数据包协议
我们可以使用UDP 实现可靠的传输协议吗? 是可以的
比如:tftp 协议会报告每个收送的数据包,到达端必须送回一个数据包表示:"我收到了!"[一个 "ACK"数据包]。若原本数据包的传送端在五秒内没有收到回应,这表示它该重送这个数据包,直到收到 ACK 为止。在实作可靠的 SOCK_DGRAM 应用程序时,这个回报的过程很重要。
对于ip address
传输的字节使用的Network Byte Order 大端
但在在电脑会使用Host Byte Order 小端
比较重要的数据结构
socket descriptor 类型为int
addrinfo
1 | struct addrinfo { |
sockaddr 和 sockaddr_in 和 struct sockaddr_storage
可以互相转化
sockaddr
1 | struct sockaddr { |
sockaddr_in
与这个sockaddr 对齐平等的
1 | struct sockaddr_in { |
sin_addr
1 | // (仅限 IPv4 — IPv6 请参考 struct in6_addr) |
还有一个可以存储这个IPv4 与 IPv6 structures
struct sockaddr_storage
1 | struct sockaddr_storage { |
简述一下函数的意思
tcp:
getaddrinfo() 通过ip或域名 得到一个addrinfo
socket() 得到一个socket descriptor
bind() 绑定一个端口
connect() 链接一个端口
listen() 监控一个端口
accept() 接收一个新的连接
send() recv() 发送数据 和接收数据
close() shutdown() 关闭socket descriptor
对于tcp
1 | server: +-------------+ client :+-------------+ |
到这个addrinfo
如果有错误会返回非零的数,可以使 a _ rerror()打印出来
1 |
|
使用socket 返回的是socket descriptor
可以设定想要的类型
错误时返回 -1
1 |
|
example
1 | int s; |
是为了让socket 绑定当前一个port number
bind() 在错误时也会返回 -1,并将 errno 设置为该错误的值。
1 |
|
小tips:
1.另一件调用 bind() 时要小心的事情是:不要用太小的 port number。全部 1024 以下的 ports 都是保留的[除非你是系统管理员]!你可以使用任何 1024 以上的 port number,最高到 65535[提供尚未被其它程序使用的]。
2.时候你试着重新运行 server,而 bind() 却失败了,它声称"Address already in use."(地址正在使用)。这是什么意思呢?很好,有些连接到 socket 的连接还悬在 kernel 里面,而它占据了这个 port。你可以等待它自行清除[一分钟之类],或者在你的程序中新增代码,让它重新使用这个 port,类似这样:
1 | int yes=1; |
3.最后一个对 bind() 的额外小提醒:在你不愿意调用 bind() 时。若你正使用 connect() 连接到远端的机器,你可以不用管 local port 是多少(以 telnet 为例,你只管远端的 port 就好),你可以单纯地调用 connect(),它会检查 socket 是否尚未绑定(unbound),并在有需要的时候自动将 socket bind() 到一个尚未使用的 local port
确定有检查 connect() 返回的值,它在错误时会返回 -1,并设定 errno 变量。
1 |
|
小tips
使用connect()不在意本地port number 系统会帮我们选择一个的
1 | int listen(int sockfd, int backlog); |
sockfd 是指 socket descriptor
backlog 是指 backlog 是进入的队列(incoming queue)中所允许的连接数目,连接将会在这个队列中排队等待,直到你 accept()
accept() 在错误发生时返回 -1 并设置 errno
1 |
|
错误时会返回 -1,并将 errno 设置为错误码
1 | int send(int sockfd, const void *msg, int len, int flags); |
错误时返回 -1[并设置相对的 errno]。
对面关闭时 返回 0
1 | int recv(int sockfd, void *buf, int len, int flags); |
体现出socket descriptor 是一文件描述符
UNIX file descriptor close()
成功时返回0 不成功时返回 -1
1 | int shutdown(int sockfd, int how); |
how可以一选择
0 不允许再接收数据
1 不允许再传送数据
2 不允许再传送与接收数据[就像 close()]
重要的是 shutdown() 实际上没有关闭 file descriptor,它只是改变了它的可用性。如果要释放 socket descriptor,你还是需要使用 close()。