java Socket与ServerSocket 详解

socket 介绍

我们知道两个进程如果需要进行通讯最基本的一个前提能能够唯一的标示一个进程,在本地进程通讯中我们可以使用PID来唯一标示一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,这时候我们需要另辟它径了,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。

能够唯一标示网络中的进程后,它们就可以利用socket进行通信了,什么是socket呢?我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

640

socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。

socket 通讯详解

socket是"打开—读/写—关闭"模式的实现,以使用TCP协议通讯的socket为例,其交互流程大概是这样子的

640

三次握手

在TCP/IP协议中,TCP协议通过三次握手建立一个可靠的连接

640

第一次握手:客户端尝试连接服务器,向服务器发送syn包(同步序列编号Synchronize Sequence Numbers),syn=j,客户端进入SYN_SEND状态等待服务器确认

第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态

第三次握手:第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手

定睛一看,服务器socket与客户端socket建立连接的部分其实就是大名鼎鼎的三次握手

640

socket 异常讲解

在了解Socket的内容之前,先要了解一下涉及到的一些异常类型。以下四种类型都是继承于IOException,所以很多之后直接弹出IOException即可。

UnkownHostException:      主机名字或IP错误

ConnectException:        服务器拒绝连接、服务器没有启动、(超出队列数,拒绝连接)

SocketTimeoutException:      连接超时

BindException:          Socket对象无法与制定的本地IP地址或端口绑定

socket api 讲解

Socket与ServerSocket的交互,下面的图片我觉得已经说的很详细很清楚了。

640

Socket

构造函数

Socket()

Socket(InetAddress address, int port)throws UnknownHostException, IOException

Socket(InetAddress address, int port, InetAddress localAddress, int localPort)throws IOException

Socket(String host, int port)throws UnknownHostException, IOException

Socket(String host, int port, InetAddress localAddress, int localPort)throws IOException

除去第一种不带参数的之外,其它构造函数会尝试建立与服务器的连接。如果失败会抛出IOException错误。如果成功,则返回Socket对象。

InetAddress是一个用于记录主机的类,其静态getHostByName(String msg)可以返回一个实例,其静态方法getLocalHost()也可以获得当前主机的IP地址,并返回一个实例。Socket(String host, int port, InetAddress localAddress, int localPort)构造函数的参数分别为目标IP、目标端口、绑定本地IP、绑定本地端口。

Socket方法

getInetAddress(); 远程服务端的IP地址

getPort(); 远程服务端的端口

getLocalAddress() 本地客户端的IP地址

getLocalPort() 本地客户端的端口

getInputStream(); 获得输入流

getOutStream(); 获得输出流

值得注意的是,在这些方法里面,最重要的就是getInputStream()和getOutputStream()了。

Socket状态

 
 

如果要确认Socket的状态是否处于连接中,下面语句是很好的判断方式。

半关闭Socket

很多时候,我们并不知道在获得的输入流里面到底读多长才结束。下面是一些比较普遍的方法:

  • 自定义标识符(譬如下面的例子,当受到“bye”字符串的时候,关闭Socket)

  • 告知读取长度(有些自定义协议的,固定前几个字节表示读取的长度的)

  • 读完所有数据

  • 当Socket调用close的时候关闭的时候,关闭其输入输出流

ServerSocket

构造函数

ServerSocket()throws IOException

ServerSocket(int port)throws IOException

ServerSocket(int port, int backlog)throws IOException

ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOException

注意点:

1. port服务端要监听的端口;backlog客户端连接请求的队列长度;bindAddr服务端绑定IP

2. 如果端口被占用或者没有权限使用某些端口会抛出BindException错误。譬如1~1023的端口需要管理员才拥有权限绑定。

3. 如果设置端口为0,则系统会自动为其分配一个端口;

4. bindAddr用于绑定服务器IP,为什么会有这样的设置呢,譬如有些机器有多个网卡。

5. ServerSocket一旦绑定了监听端口,就无法更改。ServerSocket()可以实现在绑定端口前设置其他的参数。

单线程的ServerSocket例子

public void service(){ 
 while(true){
 Socket socket=null; try{
 socket=serverSocket.accept();//从连接队列中取出一个连接,如果没有则等待
 System.out.println("新增连接:"+socket.getInetAddress()+":"+socket.getPort());
 ...//接收和发送数据
 }catch(IOException e){e.printStackTrace();}finally{ 
 try{ 
 if(socket!=null) socket.close();//与一个客户端通信结束后,要关闭Socket
 }catch(IOException e)
 {e.printStackTrace();
 }
 }
 }
}                                                                                                          
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值