Java网络编程系列--Socket编程模型

2020-07-04  本文已影响0人  chanamper

一、Socket模型

套接字对:由客户端IP、Port和服务端IP、Port组成的四元组
(clientaddr:clientport, serveraddr: serverport)
TCP叫字节流套接字、UDP叫数据包套接字
Socket创建函数:

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

函数参数说明:

  • domain 就是指 PF_INET、PF_INET6 以及 PF_LOCAL 等,表示什么样的套接字。
  • type 可用的值是:
    SOCK_STREAM: 表示的是字节流,对应 TCP;
    SOCK_DGRAM: 表示的是数据报,对应 UDP;
    SOCK_RAW: 表示的是原始套接字。
  • 参数 protocol 原本是用来指定通信协议的,但现在基本废弃。
    因为协议已经通过前面两个参数指定完成。protocol 目前一般写成 0 即可。

几种套接字地址格式

socket domain.png

二、TCP Socket实现

tcp-socket.png

TCP三次握手过程

tcp three-way handshake.png

服务端

int socket(int domain, int type, int protocol)
bind(int fd, sockaddr * addr, socklen_t len)
int listen (int socketfd, int backlog)
int accept(int listensockfd, struct sockaddr *cliaddr, socklen_t *addrlen)

客户端

int socket(int domain, int type, int protocol)
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen)

TCP读写数据

发送数据时常用的有三个函数,分别是 write、send 和 sendmsg。

常见的文件写函数
ssize_t write (int socketfd, const void *buffer, size_t size)
可指定选项,发送带外数据(所谓带外数据,是一种基于 TCP 协议的紧急数据,用于客户端 - 服务器在特定场景下的紧急处理)
ssize_t send (int socketfd, const void *buffer, size_t size, int flags)
指定多重缓冲区传输数据
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)

发送缓冲区的大小可以通过套接字选项来改变,当我们的应用程序调用 write 函数时,实际所做的事情是把数据从应用程序中拷贝到操作系统内核的发送缓冲区中,并不一定是把数据通过套接字写出去。
读取数据时常用函数有read

ssize_t read (int socketfd, void *buffer, size_t size)

read 函数要求操作系统内核从套接字描述字 socketfd读取最多多少个字节(size),并将结果存储到 buffer 中。返回值告诉我们实际读取的字节数目,也有一些特殊情况,如果返回值为 0,表示 EOF(end-of-file),这在网络中表示对端发送了 FIN 包,要处理断连的情况;如果返回值为 -1,表示出错。

未完全理解部分:
阻塞式套接字最终发送返回的实际写入字节数和请求字节数是相等的。
发送成功仅仅表示的是数据被拷贝到了发送缓冲区中,并不意味着连接对端已经收到所有的数据。至于什么时候发送到对端的接收缓冲区,或者更进一步说,什么时候被对方应用程序缓冲所接收,对我们而言完全都是透明的。

对于 send 来说,返回成功仅仅表示数据写到发送缓冲区成功,并不表示对端已经成功收到。
对于 read 来说,需要循环读取数据,并且需要考虑 EOF 等异常条件。

三、UDP Socket实现

udp-socket.png

服务器端创建 UDP 套接字之后,绑定到本地端口,调用 recvfrom 函数等待客户端的报文发送;客户端创建套接字之后,调用 sendto 函数往目标地址和端口发送 UDP 报文,然后客户端和服务器端进入互相应答过程。

recvfrom 和 sendto 是 UDP 用来接收和发送报文的两个主要函数:

ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, 
          struct sockaddr *from, socklen_t *addrlen); 

ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags,
                const struct sockaddr *to, socklen_t addrlen); 

四、本地套接字

本地文件路径标识说明:
1.本地文件路径必须是“绝对路径”;
2.这个本地文件,必须是一个“文件”,不能是一个“目录”。如果文件不存在,后面 bind 操作时会自动创建这个文件。
本地字节流套接字识别服务器不再通过 IP 地址和端口,而是通过本地文件。

上一篇 下一篇

猜你喜欢

热点阅读