目录
3.小DEMO:通过与time.nist.gov对话构造一个Date
1.shutdownInput()和shutdownOutput()方法
2.isInputShutdown()和isOutputShutdown()方法
第一部分:使用Socket
一.数据报
数据按照有限大小的包传输,这些包称为数据报。
每个数据报包含一个首部和一个有效载荷。
首部包含包发送到的地址和端口、包来自的地址和端口、检测数据是否被破坏的校验和,以及用于保证可靠传输的各种其他管理信息;
有效载荷包含数据本身。
由于数据报文长度是有限的,通常必须将数据分解为多个包,再在目的的重新组合。
也有可能一个包或者多个包在传输中丢失或者遭到破坏,需要重新传;
或者包乱序到达,需要重新排序。所有这些(将数据分解为包、生成首部、解析入站包的首部、跟踪哪些包已经收到而哪些没有收到and so on),是不是看起来都很麻烦,但是你不需要完成这些任务,前面的内容就是让大家了解一下哦。
Socket对程序员掩盖了网络的底层细节,就比如:错误检测、包大小、包分解、包重传、网络地址...
二.socket的功能
Socket是两台主机之间的一个连接,可以完成以下7个基本操作:
1.连接远程机器
2.发送数据
3.接收数据
4.关闭连接
5.绑定端口
6.监听入站数据
7.在绑定端口上接受来自远程机器的连接
Java程序采用以下方式使用客户端socket
* 程序用构造函数创建一个新的socket
*Socket尝试连接远程机器
详细了解一下:
一旦建立了连接,本地和远程机就从这个socket得到输入流和输出流,使用这两个流相互发送数据;
连接是全双工的,两台主机都可以同时发送和接受数据。数据的含义取决与协议,发送给FTP服务器的命令与发送给HTTP服务器的命令就有所不同。一般会先完成某种协商握手,然后再具体传输数据
第二部分:了解SOCKET类
1.socket类
Socket
类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。
2.构造方法
public Socket(String host, int port)
eg:
Socket clients = new Socket("127.0.0.1",2628);
创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为回送地址。
*~*回送地址(127.x.x.x) 是本机回送地址(Loopback Address),主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,立即返回,不进行任何网络传输。
3.常用方法介绍
public InputStream getInputStream() : 返回此套接字的输入流。
如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。
关闭生成的InputStream也将关闭相关的Socket。
public OutputStream getOutputStream() : 返回此套接字的输出流。如果此Socket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道。
关闭生成的OutputStream也将关闭相关的Socket。
public void close() :关闭此套接字。一旦一个socket被关闭,它不可再使用。
关闭此socket也将关闭相关的InputStream和OutputStream 。
public void shutdownOutput() : 禁用此套接字的输出流。任何先前写出的数据将被发送,随后终止输出流。
4.DEMO:客户端和服务端通信
1.两端通信时步骤:
服务端程序,需要事先启动,等待客户端的连接。
客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。
2.在Java中,提供了两个类用于实现TCP通信程序:
客户端:java.net.Socket 类表示。创建Socket对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信。
服务端:java.net.ServerSocket 类表示。创建ServerSocket对象,相当于开启一个服务,并等待客户端的连接。
【服务端我会在下一篇博客中详细讲到,大家可以先了解一下下哦】
客户端:
package TEMP.Temp2;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args){
//socket对象初始化
Socket socket = null;
//输出流 os对象初始化
OutputStream os = null;
try {
//1、创建Socket对象,它的第一个参数需要的是服务端的IP,第二个参数是服务端的端口
InetAddress inet = InetAddress.getByName("127.0.0.1");
socket = new Socket(inet,2022);//inet是服务端ip
//2、获取一个输出流,用于写出要发送的数据
os = socket.getOutputStream();
//3、写出数据
os.write("你好,我是客户端小杨同学!".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4、释放资源,别忘了哦!!!!
if(socket!=null){
try {
socket.close();//关闭
} catch (IOException e) {
e.printStackTrace();
}
}
if(os!=null){
try {
os.close();//关闭
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务端:
package TEMP.Temp2;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
//1、创建服务端的ServerSocket,指明自己的端口号
serverSocket = new ServerSocket(2022);
//2、调用accept接收到来自于客户端的socket
socket = serverSocket.accept();//阻塞式监听,会一直等待客户端的接入,接入了之后才会显示消息
//3、获取socket的输入流
is = socket.getInputStream();
//4、读取输入流中的数据
//ByteArrayOutputStream的好处是它可以根据数据的大小自动扩充
baos = new ByteArrayOutputStream();
int len=0;
byte[] buffer = new byte[1024];
//判断是否将客户端发的消息读完了
while ((len=is.read(buffer))!=-1){
baos.write(buffer,0,len);
}
System.out.println("收到了来自于客户端"+
socket.getInetAddress().getHostName()
+"的消息:"+baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {//5、关闭资源
if(serverSocket!=null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(baos!=null){
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
第三部分:用Telnet研究协议
一、了解Telnet
1.Telnet是一个简单的远程终端协议。用户用Telnet就可种子其所在地通过TCP连接注册(即登录)到远地的另一个主机上(使用主机名或IP地址)。在终端使用者的电脑上使用telnet程序,用它连接到服务器。终端使用者可以在telnet程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。
2.Telnet客户进程和服务器进程一般只属于用户应用程序,终端用户通过键盘输入的数据送给操作系统内核的终端驱动进程,由终端驱动进程把用户的输入送到Telnet客户进程,Telnet客户进程把收到的数据传送给TCP,由TCP负责在客户端和服务器端建立TCP连接,数据就通过TCP连接送到了服务器端,服务器的TCP层将收到的数据送到相应的应用层Telnet服务器进程。
二、用Socket从服务器读取
1.在终端上用Telnet测试daytime服务器
2.小DEMO:Daytime协议客户端
package BOOK;
//Daytime协议客户端
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class DaytimeClient {
public static void main(String[] args) {
String hostname = args.length>0?args[0] : "time.nist.gov";
Socket socket = null;
try{
/**在端口13打开与time.nist.gov的连接
* 这里不仅仅是创建一个对象,实际上它会在网络上建立连接;
* 如果连接超时(或者由于服务器没有在端口13上监听而失败),
* 构造函数会抛出一个IOException异常
* 所以呢,要把这段代码放在一个try块中。
*/
//在端口13打开与time.nist.gov的连接
socket = new Socket(hostname,13);
//setSoTimeout()方法为连接设置一个超时时间,(单位:毫秒)
socket.setSoTimeout(15000);
//一旦打开socket并设置其超时时间,
// 可以调用getInputStream()来返回一个InputStream,用它从socket读取字节
InputStream in = socket.getInputStream();
//把字节存储在一个StringBuilder中
StringBuilder time =new StringBuilder();
//一般情况下,服务器可以发送任何字节
//但是呢,在特定的例子中,协议指定发送的字节必须是ASCII
InputStreamReader reader = new InputStreamReader(in,"ASCII");
//使用for确定将字节读完
for(int c=reader.read();c!=-1;c=reader.read()){
time.append((char) c);
}
System.out.println(time);
}catch(IOException ex){
System.err.println(ex);
}finally{
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
//输出
//59773 22-07-13 02:17:17 50 0 0 720.0 UTC(NIST) *
3.小DEMO:通过与time.nist.gov对话构造一个Date
package BOOK.DEMO;
//通过与time.nist.gov对话构建一个Date
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Daytime {
public Date getDateFromNetwork() throws IOException, ParseException {
try(Socket socket = new Socket("time.nist.gov",13)){
socket.setSoTimeout(15000);
InputStream in = socket.getInputStream();
StringBuilder time = new StringBuilder();
InputStreamReader reader = new InputStreamReader(in,"ASCII");
for(int c=reader.read(); c!=-1;c=reader.read()){
time.append((char) c);
}
return parseDate(time.toString());
}
}
static Date parseDate(String s) throws ParseException{
String[] pieces =s.split(" ");
String dateTime = pieces[1] + " " +pieces[2] + " UTC";
DateFormat format = new SimpleDateFormat("yy-MM-dd hh:mm:ss z");
return format.parse(dateTime);
}
}
注意点:从网络读取数据的时候,并不是所有协议都使用ASCII,甚至不一定使用文本。
4.小DEMO:时间协议客户端
package BOOK.DEMO;
//时间协议客户端
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.text.ParseException;
import java.util.Date;
public class Time {
private static final String HOSTNAME = "time.nist.gov";
public static void main(String[] args) {
Date d = null;
try {
d = Time.getDateFromNetwork();
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println("It is " + d);
}
public static Date getDateFromNetwork() throws IOException,ParseException{
long differenceBetweenEpochs = 2208988800l;
Socket socket = null;
try{
socket = new Socket(HOSTNAME,37);
socket.setSoTimeout(15000);
InputStream raw =socket.getInputStream();
long secondSince1900 =0;
for(int i=0;i<4;i++){
secondSince1900 =(secondSince1900 << 8) | raw.read();
}
long secondSince1970 =secondSince1900 - differenceBetweenEpochs;
long msSince1970 =secondSince1970*1000;
Date time = new Date(msSince1970);
return time;
}finally{
try{
if(socket!=null)
socket.close();
}catch(IOException ex){
}
}
}
}
//输出:
// It is Wed Jul 13 11:53:25 CST 2022
三、用Socket写入服务器
1.了解用socket如何写入服务器
写入服务器并不比读取服务器更困难。
只需要向Socket请求一个输出流以及一个输入流。使用输出流在socket上发送请求数据的时候,同时还可以使用输入流读取数据。但是呢,大多数协议都设计为客户端只读取socket或者只写入socket,而不是二者同时进行。
【最常见的模式】
客户端发送一个请求,然后服务器相应。客户端可能发送另一个请求,服务器再作出响应。这个过程会一直继续,直到客户端或者服务器完成了工作,然后关闭连接。
下面我们来康康具体的实例
dict是一个简单的双向TCP。在这个协议中,客户端向dict服务器的2628端口打开一个socket,并且发送命令。
2.用java程序来显示一个完整的dict客户端
package BOOK.DEMO;
import java.io.*;
import java.net.Socket;
public class DictClient {
public static final String SERVER ="dict.org";
public static final int PORT =2628;
public static int TIMEOUT =15000;
public static void main(String[] args) {
Socket socket =null;
try {
//先向一个dict服务器的端口2628打开一个Socket
socket = new Socket(SERVER,PORT);
//为了防止连接服务器是服务器挂起,设置一个超时时间
socket.setSoTimeout(TIMEOUT);
/**
* getOutputStream()方法返回一个原始的OutputStream
* 可以用它从你的应用向Socket的另一端写数据。
* 在使用之前,通常会把这个流串链到一个更方便的类【DataOututStream或OutputStreamWriter】
*/
//getOutputStream()方法返回一个原始的OutputStream
OutputStream out = socket.getOutputStream();
//包装在一个Writer中
Writer writer =new OutputStreamWriter(out,"UTF-8");
writer =new BufferedWriter(writer);
//用socket的输入流来读取信息
InputStream in =socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in,"UTF-8"));
for(String word :args){
define(word,writer,reader);
}
//通过socket写入命令
writer.write("quit\r\n");
//刷新输出,确保命令会通过网络发送
writer.flush();
} catch (IOException ex) {
System.out.println(ex);
}finally{
if(socket!=null){
try {
socket.close();
} catch (IOException ex) {
}
}
}
}
static void define(String word,Writer writer,BufferedReader reader)
throws IOException,UnsupportedEncodingException{
writer.write("DEFINE eng-lat " + word + "\r\n");
writer.flush();
for(String line =reader.readLine();line!=null;line=reader.readLine()){
if(line.startsWith("250 ")){
return ;
}else if(line.startsWith("552 ")){
System.out.println("No definition found for " + word);
return;
}
else if(line.matches("\\d\\d\\d .*"))
continue;
else if(line.trim().equals("."))
continue;
else System.out.println(line);
}
}
}
四、半关闭Socket
1.shutdownInput()和shutdownOutput()方法
close()方法同时关闭Socket的输入和输出;
有的时候你可能希望仅仅关闭连接的一半,关闭输入或者输出;shutdownInput()和shutdownOutput()方法可以只关闭连接的一半。
public void shutdownInput( ) throws IOException
public void shutdownOutput( ) throws IOException
小小解析:
这两个方法并不关闭Socket,实际上,它会调整与Socket连接的流,使它认为已经到了流的末尾;
*关闭输入之后再读取输入流会返回-1;
*关闭输出之后再写入socket会抛出一个IOException异常
注意哦!!!
即使半关闭了连接,或者将连接的两半都关闭,使用结束后依然需要关闭该socket;
shutdown方法只影响Socket的流,并不是方法与socket关联的资源,比如:占用的端口。
2.isInputShutdown()和isOutputShutdown()方法
isInputShutdown()和isOutputShutdown()方法分别指出输入流和输出流是打开还是关闭的
public boolean isInputShutdown()
public boolean isOnputShutdown()
第四部分:构造和连接Socket
java.netSocket类是Java完成客户端TCP操作的基础类。
其他建立TCP网络连接的面向客户端的类(比如:URL、URLConnection、Applet和JEditorPane)最终都会调用这个类的方法。这个类本身使用原生代码与主机操作系统的本地TCP栈进行通信。
一、基本构造函数
每个Socket构造函数指定要连接的主机和端口。
主机可以指定InetAddress或者String;
远程端口指定为1到65535之间的int值
public Socket(String host,int port) throws UnknownHostException,IOException
为创建一个流套接字并将其连接到指定主机上的指定端口号
【String host:表示主机名,int port :端口号】
public Socket(InetAddress host,int port) throws IOException
为创建一个流套接字并将其连接到指定IP地址的指定端口号
这两个构造函数会连接socket(也就是说,在构造函数返回之前,会与远程主机建立一个活动的网络连接);
如果由于某种原因不能打开连接,构造函数会抛出一个IOException或者UnknownHostException异常。
小DEMO:查看指定主机上前1024个端口中哪些安装的有TCP服务器
package BOOK.DEMO;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class LowPortScanner {
public static void main(String[] args) {
String host = args.length >0 ? args[0] : "localhost";
for(int i=1;i<1024;i++){
try{
Socket s = new Socket(host,i);
System.out.println("There is a server on port " + i + " of "+host);
s.close();
}catch(UnknownHostException ex){
System.err.println(ex);
}catch(IOException ex){
//当这个端口不是一个服务器的时候
}
}
}
}
//输出:There is a server on port 631 of localhost
二、选择从哪个本地接口连接
有两个构造函数可以指定要连接的主机和端口,以及从哪个接口和端口连接:
public Socket(String host,int port,InetAddress interface,int localPort)
throws IOException, UnknownHostException
创建一个套接字并将其连接到指定远程主机上的指定远程端口
参数解析:
String host:指定的主机( 远程主机的名称,或环回地址的
null
)int port:指定的端口号(远程端口)
InetAddress interface:套接字所绑定的本地地址,或
null
为anyLocal
地址。int localPort:套接字绑定的本地端口,或
zero
用于系统选择的自由端口public Socket(InetAddress host,int port,InetAddress interface,int localPort)
throws IOException
创建一个套接字并将其连接到指定远程地址上的指定远程端口
三、构造但是不连接
public socket()
如果没有为Socket构造函数提供任何参数,它就没有目标主机可以连接。
可以以后再为某个connect()方法传入一个SocketAddress来建立连接
eg:
还可以传入一个int作为第二个参数,来指定连接超时之前等待的时间(毫秒数)
public void connect(SocketAddress endpoint,int timeout) throws IOException
默认值0表示永远等待下去
你有没有思考一下下,为什么要有这个构造函数呢??!
有这个构造函数其实是为了支持不同类型的socket;
还有一个好处是:可以在try-catch-finally块中清理代码
明显可以看到:finally块中关闭一个Socket 的时候可以避免烦人的null检查哈哈哈哈。
四、Socket地址
SocketAddress类表示一个连接端点。这是一个空的抽象类,除了一个默认构造函数之外没有其他方法。
SocketAddress类的主要用途是为了暂时的socket连接信息(比如IP地址和端口)提供一个方便的存储,即使最初的socket已断开并且被垃圾回收了,这些信息也可以重用来创建新的Socket.
Socket提供了两个返回SocketAddress对象的方法
public SocketAddress getRemoteSocketAddress()
返回所连接系统的地址
public SocketAddress getLocalSocketAddress()
返回发起连接的地址
五、代理服务器
前面提到的最后一个构造函数创建一个未连接的Socket,它通过一个指定的代理服务器连接:
public Socket(Proxy proxy)
一般情况下,Socket使用的代理服务器由socksProxyHost和socksProxyPort系统属性控制,这些属性应用于系统中的所有Socket。
六、获取Socket的信息
1.Socket对象的一些属性
Socket对象有一些属性可以通过获取方法来访问:
1.远程地址
2.远程端口
3.本地地址
4.本地端口
2.获取方法
1. public InetAddress getInetAddress()
返回套接字所连接的地址。
2. public int getPort()
返回此套接字连接到的远程端口号。
3. public InetAddress getLocalAddress()
获取套接字所绑定的本地地址。
4. public int getLocalPort()
返回此套接字绑定到的本地端口号。
3.远程端口
对于客户端Socket来说,通常是由一个标准委员会预先分配的“已知端口”。
4.本地端口
通常是由系统在运行时从未使用的空闲端口中选择。
5.demo:获得Socket的信息
package BOOK.DEMO;
//获取SOCKET信息
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
public class SocketInfo {
public static void main(String[] args) {
for(String host :args){
try{
Socket theSocket = new Socket(host,80);
System.out.println("Connected to" + theSocket.getInetAddress()
+ " on port " + theSocket.getPort() + " from port "
+ theSocket.getLocalPort() + " of "
+ theSocket.getLocalAddress());
}catch (UnknownHostException ex){
System.err.println("I can't find " + host);
}catch(SocketException ex){
System.err.println("Could not connect to " + host);
}catch(IOException ex){
System.err.println(ex);
}
}
}
}
七、关闭还是连接
如果socket关闭,isClose()方法会返回true,否则返回false。
有的时候你不确定一个Socket的状态,可以用这个方法来检查,千万别冒着抛出IOException异常的风险。
but!!!! 这个方法也有缺陷,如果Socket从一开始就没有连接,isClosed()也返回false,尽管Socket实际上根本没有打开过。
Socket类还有一个isConnected()方法,它会指出Socket是否从未连接过一个远程主机。
八、toString()
Socket类只覆盖了java.lang.Object中的一个标准方法:toString().
第五部分:设置Socket选项
Socket有以下几个选项:
1. TCP_NODELY:表示立即发送数据
2. SO_RESUSEADDR:表示是否允许重用Socket所绑定的本地地址
3. SO_TIMEOUT:表示接收数据时的等待超时时间
4. SO_LINGER:表示当执行Socket的close()方法时,是否立即关闭底层的Socket
5. SO_SNFBUF:表示发送数据的缓冲区大小
6. SO_RCVBUF:表示接受数据的缓冲区大小
7. SO_KEEPALIVE:表示对于长时间处于空闲状态的Socket,是否要自动将它关闭
8. OOBINLINE:表示是否发送一个字节的TCP紧急数据
1.TCP_NODELY选项
设置该选项:public void setTcpNoDelay(boolean on)throws SocketException
读取该选项:public boolean getTcpNoDelay()throws SocketException
默认情况下,发送数数据采用Negale算法。Negale算法是指发送方发送 数据不会立刻发出,而是先放在缓冲区内,等缓冲区满了再发出。发送完一批数据后,会等待接收方对这批数据的回应,然后在发送下一批数据。Negale算法适用于发送方需要发送大批量数据,并且接受方会及时作出反应的场合,这种算法通过减少数据的通信的次数来提高通信效率。
如果发送方持续的发送小批量数据,并且接受方不一定会立即发送响应数据,那么Negale算法会使发送方运行很慢。对于GUI程序,如网络游戏程序(服务器需要实时跟踪客户端鼠标的移动),这个问题尤为突出。客户端鼠标位置的改动的信息需要实时发送到服务器上,由于Negale算法采用缓冲,大大减低了实时反应速度,导致客户端运行很慢。
TCP_NODELY的默认值为false,表示采用Negale算法。如果调用setTcpNoDelay(true)方法,就会关闭Socket的缓冲,确保数据及时发送:【eg】
if(!socket.getResuseAddress())
socket.setResuseAddress(true);
2.SO_RESUSEADDR选项
设置该选项:public void setResuseAddress(boolean on)throws SocketException
读取该选项 : public void getResuseAddress(boolean on)throws SocketException
当接受方通过Socket的close()方法关闭Socket时,如果网络上还有发送到这个Socket的数据,那么底层的Socket不会立刻释放本地端口,而是会等待一段时间,确保收到了网络上发送过来的延迟数据,然再释放该端口。Socket接受到延迟数据后,不会对这些数据做任何处理。Socket接受延迟数据的目的是,确保这些数据不会被其他碰巧绑定到同样端口的新进程接收到。
客户程序一般采用随机端口,因此会出现两个客户端程序绑定到同样端口的可能性不大。许多服务器都使用固定的端口。当服务器进程关闭后,有可能它的端口还会被占用一段时间,如果此时立刻在同一主机上重启服务器程序,由于端口已经被占用,使得服务器无法绑定到该端口,启动失败。为了确保一个进程被关闭后,及时它还没有释放该端口,同一个主机上的其他进程还可以立刻重用该端口,可以调用Socket的setResuseAddress(true)方法:
if(!socket.getResuseAddress())
socket.setResuseAddress(true);
值得注意的是:socket.setResuseAddress(true)方法必须在Socket还没有绑定到一个本地端口之前调用,否则执行socket.setResuseAddress(true)方法无效
所以呢必须按照以下方法创建Socket对象,然后在连接远程服务器:
Socket socket=new Socket();//此时socket端口未绑定本地端口,并且未连接远程服务器 socket.setReuseAddress(true); SocketAddress socketAddress=new InetSocketAddress("remotehost",2022); socket.connect(socketAddress); //或者 Socket socket=new Socket();//此时socket端口未绑定本地端口,并且未连接远程服务器 socket.setReuseAddress(true); socketAddress localAddr=new InetSocketAddress("localhost",2021); socketAddress remoteAddr=new InetSocketAddress("remotehost",2022); socket.bind(localAddr);//与本地端口绑定 socket.connect(remoteAddr);//连接远程服务器
此外,两个共用同一个端口的进程必须都调用socket.setReuseAddress(true)放方法才能使得一个进程关闭Socket后,另一个进程的Socket能够立刻重用相同的端口。
3.SO_TIMEOUT选项
设置该选项:public void setSoTimeout(int milliseconds)throws SocketException
读取该选项:public void getSoTimeout(int milliseconds)throws SocketException
当通过Socket流读取数据时,如果还没有数据,就会等待。
例如,在以下代码中,in.read(buf)方法从输入流中读入1024个字节:
byte[] luck=new byte[1024]; InputStream in=new socket.getInputStream(); in.read(luck);
如果输入流中还没有数据,in.read(buf)方法就会等待发送方发送数据,直到满足以下情况才停止等待:
1>. 输入流中有10个字节,read()方法把这些字节读入到buff中,再返回读取的字节数;
2>.当已经接近输入流的末尾,举例末尾还有小于1024个字节时,read()方法会把这些字节读入到buff中,在返回读物的字节数;
3>.已读到输入流的末尾,返回-1;
4>. 连接已断开,抛出IOException;
5>.如果通过Socket的setTimeout()方法设置了等待超时时间,单位为毫秒,它的默认值为0,表示会无限等待,永远不会超时。下面的代码把接受数据的等待时间设为5分钟;
if(socket.getTimeout()==0) socket.setTimeout(60000*5);
4.SO_LINGER选项
设置该选项:public void setSoLinger(boolean on,int seconds)throws SocketException
读取该选项:public void getSoLinger(boolean on,int seconds)throws SocketException
第六部分:Socket异常
Socket类的大多数方法都会声明抛出IOException或者它的子类java.net.SocketException.
(具体是什么原因我还没有了解,大家写代码的时候多多注意就好啦)