在客户/服务器通讯模式中,客户端须要主动创建与服务器链接的Socket,服务器端收到客户端的链接请求,也会建立与客户端链接的Socket。Socket能够看作是通讯链接两端的收发器,客户端和服务店都经过Socket来收发数据。java
一、构造Socket算法
public Socket() 经过系统默认类型的 SocketImpl 建立未链接套接字 服务器
public Socket(String host,int port) throws UnknownHostException,IOException 建立一个流套接字并将其链接到指定主机上的指定端口号。网络
public Socket(String host,int port,InetAddress localAddr,int localPort) throws IOException 建立一个套接字并将其链接到指定远程主机上的指定远程端口。socket 会经过调用 bind() 函数来绑定提供的本地地址及端口。app
public Socket(InetAddress address,int port)throws IOException建立一个流套接字并将其链接到指定 IP 地址的指定端口号。socket
public Socket(InetAddress address,int port,InetAddress localAddr,int localPort)throws IOException建立一个套接字并将其链接到指定远程地址上的指定远程端口。socket 会经过调用 bind() 函数来绑定提供的本地地址及端口。函数
除第一个外,其余4个构造方法都会试图和服务器创建链接,如何链接成功则返回Socket对象,若是链接失败就会抛出IOException。测试
实例1:扫描主机上1到1024的端口,判断这些端口是否被服务器监听package com.hanfeng.socket; google
import java.io.IOException; spa
import java.net.Socket;
/**
* 扫描主机上1到1024的端口,判断这些端口是否被服务器监听
*
* @author hanfeng
* @data 2012-8-24 上午10:25:57
*/
public class PortScanner {
public static void main(String[] args) {
String host = "localhost";
if (args.length > 0) {
host = args[0];
}
new PortScanner().scan(host);
}
public void scan(String host) {
Socket socket = null;
for (int port = 1; port < 1024; port++) {
try {
socket = new Socket(host, port);
System.out.println("链接到端口:" + port);
} catch (IOException e) {
System.out.println("没法链接到端口:" + port);
} finally {
try {
if (socket != null) {
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
1.1 设置等待链接超时时间
public void scan(String host) {
Socket socket = null;
socket = new Socket();
SocketAddress address = new InetSocketAddress(host, 1158);
try {
socket.connect(address, 60000);
System.out.println("链接成功!");
} catch (IOException e) {
System.out.println("链接超时!");
e.printStackTrace();
}
}
1.2设置服务器的IP地址
Socket(InetAddress address, int port) //第一个参数address表示主机的IP地址
Socket(String host, int port) //第一个参数host表示主机的名字
InetAddress类表示IP地址,其用法以下:
//返回本地主机的IP地址
InetAddress addr1=InetAddress.getLocalHost();
//返回表明"222.34.5.7"的IP地址
InetAddress addr2=InetAddress.getByName("222.34.5.7");
//返回域名为"www.google.com"的IP地址
InetAddress addr3=InetAddress.getByName("www.google.com");
public static void main(String[] args) {
try {
InetAddress address = InetAddress.getLocalHost();
System.out.println("本机IP地址:"+address);
InetAddress address2 = InetAddress.getByName("222.34.5.7");
System.out.println("返回远程IP地址:"+address2);
InetAddress address3 = InetAddress.getByName("www.google.com");
System.out.println("返回域名IP地址:"+address3);
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
1.3 设置客户端的IP地址
在一个Socket对象中,既包含远程服务器的IP地址和端口信息,也包含本地客户端的IP地址和端口信息。默认状况下,客户端的IP地址来自于客户程序所在的主机,客户端的端口则由操做系统随机分配。Socket类还有两个构造方法容许显式的设置客户端的IP地址和端口:
Socket(InetAddress address, int port, InetAddress localAddr, int localPort)throws IOException
Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException
实例:一个主机在Internet网络中的IP地址为”222.67.1.34“,在一个局域网中的IP地址为”112.5.4.3“。假定这个主机上的客户程序但愿和同一个局域网上的一个服务器程序(112.5.4.45:8000)通讯,则客户端可按照以下方式进行构造Socket对象:
<span style="white-space:pre"> </span>InetAddress remoteAddr = InetAddress.getByName("112.5.4.45");
InetAddress localAddr = InetAddress.getByName("112.5.4.3");
Socket socket = new Socket(remoteAddr,8000,localAddr,2345);
1.4客户端链接服务器可能抛出的异常
UnknowHostException:若是没法识别主机的名字或IP地址,就会抛出异常
ConnectException:若是没有服务器进行监听指定的端口,或则服务器进程拒绝链接,就会抛出异常
SocketTimeoutException:等待链接超时,抛出异常
BindExcrption:若是没法把Socket对象与具体的本地IP地址或端口绑定,就会抛出异常
以上4个异常都是IOException的直接或间接子类,以下图
实例:捕获各类异常
/**
*
* @author hanfeng
* @data 2012-8-24 上午11:16:32
*/
public class ConnectTest {
public static void main(String[] args) {
String host = "www.google.com";
int port = 80;
if (args.length>1) {
host = args[0];
port = Integer.parseInt(args[1]);
}
new ConnectTest().connect(host, port);
}
public void connect(String host,int port){
SocketAddress address = new InetSocketAddress(host, port);
Socket socket = null;
String result = "";
try {
long begin = System.currentTimeMillis();//计算开始链接的时间
socket = new Socket();//开始创建链接
socket.connect(address, 6000);//设置链接超时时间
long end = System.currentTimeMillis();// 计算机链接结束的时间
result = (end-begin)+"ms";
} catch (BindException e) {
result = "IP地址或端口绑定异常!";
} catch (UnknownHostException e) {
result = "未识别主机地址!";
}catch (SocketTimeoutException e) {
result = "链接超时!";
}catch (ConnectException e) {
result = "拒绝链接!";
}catch (Exception e) {
result = "失败啦!";
}finally{
if (socket!=null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("远程地址信息==>"+address+":"+result);
}
}
二、获取Socket信息
如下方法用于获取Socket的有关信息:
getInetAddress():得到远程服务器的IP地址。
getPort():得到远程服务器的端口。
getLocalAddress():得到客户本地的IP地址。
getLocalPort():得到客户本地的端口。
getInputStream():得到输入流。若是Socket尚未链接,或者已经关闭,或者已经经过shutdownInput()方法关闭输入流,那么此方法会抛出IOException。
getOutputStream():得到输出流。若是Socket尚未链接,或者已经关闭,或者已经经过shutdownOutput()方法关闭输出流,那么此方法会抛出IOException。
实例:HTTPClient类访问网页
package com.hanfeng.socket;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class HttpClientDemo {
String host ="www.google.com";
int port = 80;
Socket socket = null;
public void createSocket() throws Exception{
socket = new Socket(host, port);
}
public void communicate() throws Exception{
StringBuffer sb=new StringBuffer("GET "+" HTTP/1.1\r\n");
// sb.append("Host: www.javathinker.org\r\n");
// sb.append("Accept: */*\r\n");
// sb.append("Accept-Language: zh-cn\r\n");
// sb.append("Accept-Encoding: gzip, deflate\r\n");
// sb.append("User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)\r\n");
// sb.append("Connection: Keep-Alive\r\n\r\n");
//发出HTTP请求
OutputStream socketOut = socket.getOutputStream();
socketOut.write(sb.toString().getBytes());
socket.shutdownOutput();
//接收响应结果
InputStream socketInput = socket.getInputStream();
BufferedReader buffer = new BufferedReader(new InputStreamReader(socketInput));
String data = null;
while ((data=buffer.readLine())!=null) {
System.out.println(data);
}
}
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
HttpClientDemo httpClient = new HttpClientDemo();
httpClient.createSocket();
httpClient.communicate();
}
}
三、关闭Socket
当客户与服务器的通讯结束,应该及时关闭Socket,以释放Socket占用的包括端口在内的各类资源。Socket的close()方法负责关闭Socket。当一个Socket对象被关闭,就不能在经过他的输入流和输出流进行I/O操做,不然会致使IOException。
为了确保关闭Socket的操做总被执行,建议把这个操做放在finally代码块中。
finally {
try {
if(socket!=null)socket.close();
} catch (IOException e) {
e.printStackTrace();
}
Socket类提供了3个状态测试方法
isClosed():若是Socket已经链接到远程主机,而且尚未关闭,则返回true
isConnected():若是Socket曾经链接到远程主机,则返回true
isBound():若是Socket已经与一个本地端口绑定,则返回true
若是要判断一个Socket对象当前是否处于链接状态,能够采用如下方式:
boolean isConnected = socket.isConnected()&&!socket.isClosed();
四、半关闭Socket
4.1 有的时候,可能仅仅但愿关闭输出流或输入流之一。此时能够采用Socket类提供的半关闭方法:
shutdownInput():关闭输入流。
shutdownOutput(): 关闭输出流。
4.2 前后调用Socket的shutdownInput()和shutdownOutput()方法,仅仅关闭了输入流和输出流,并不等价于调用Socket的close()方法。在通讯结束后,仍然要调用Socket的close()方法,由于只有该方法才会释放Socket占用的资源,好比占用的本地端口等。
4.3 Socket类还提供了两个状态测试方法,用来判断输入流和输出流是否关闭:
public boolean isInputShutdown()
public boolean isOutputShutdown()
五、设置Socket的选项
Socket有如下几个选项:
n TCP_NODELAY:表示当即发送数据。
n SO_RESUSEADDR:表示是否容许重用Socket所绑定的本地地址。
n SO_TIMEOUT:表示接收数据时的等待超时时间。
n SO_LINGER:表示当执行Socket的close()方法时,是否当即关闭底层的Socket。
n SO_SNFBUF:表示发送数据的缓冲区的大小。
n SO_RCVBUF:表示接收数据的缓冲区的大小。
n SO_KEEPALIVE:表示对于长时间处于空闲状态的Socket,是否要自动把它关闭。
n OOBINLINE:表示是否支持发送一个字节的TCP紧急数据。
1. TCP_NODELAY选项
1) 设置该选项:public void setTcpNoDelay(boolean on) throws SocketException
2) 读取该选项:public boolean getTcpNoDelay() throws SocketException
3) TCP_NODEALY的默认值为false,表示采用Negale算法。若是调用setTcpNoDelay(true)方法,就会关闭Socket的缓冲,确保数据及时发送:
if(!socket.getTcpNoDelay()) socket.setTcpNoDelay(true);
4) 若是Socket的底层实现不支持TCP_NODELAY选项,那么getTcpNoDelay()和setTcpNoDelay()方法会抛出SocketException。
2. SO_RESUSEADDR选项
1) 设置该选项:public void setResuseAddress(boolean on) throws SocketException
2) 读取该选项:public boolean getResuseAddress() throws SocketException
3) 为了确保一个进程关闭了Socket后,即便它还没释放端口,同一个主机上的其余进程还能够马上重用该端口,能够调用Socket的setResuseAddress(true)方法:
if(!socket.getResuseAddress()) socket.setResuseAddress(true);
4) 值得注意的是socket.setResuseAddress(true)方法必须在Socket尚未绑定到一个本地端口以前调用,不然执行socket.setResuseAddress(true)方法无效。所以必须按照如下方式建立Socket对象,而后再链接远程服务器:
Socket socket = new Socket(); //此时Socket对象未绑定到本地端口,而且未链接远程服务器
socket.setResuseAddress(true);
SocketAddress remoteAddr = new InetSocketAddress("remotehost",8000);
socket.connect(remoteAddr); //链接远程服务器,而且绑定匿名的本地端口
或者:
Socket socket = new Socket(); //此时Socket对象未绑定到本地端口,而且未链接远程服务器
socket.setResuseAddress(true);
SocketAddress localAddr = new InetSocketAddress("localhost",9000);
SocketAddress remoteAddr = new InetSocketAddress("remotehost",8000);
socket.bind(localAddr); //与本地端口绑定
socket.connect(remoteAddr); //链接远程服务器,而且绑定匿名的本地端口
3. SO_TIMEOUT选项
1) 设置该选项:public void setSoTimeout(int milliseconds) throws SocketException
2) 读取该选项:public int getSoTimeOut() throws SocketException
3) 当经过Socket的输入流读数据时,若是尚未数据,就会等待。Socket类的SO_TIMEOUT选项用于设定接收数据的等待超时时间,单位为毫秒,它的默认值为0,表示会无限等待,永远不会超时。
4) Socket的setSoTimeout()方法必须在接收数据以前执行才有效。此外,当输入流的read()方法抛出SocketTimeoutException后,Socket仍然是链接的,能够尝试再次读取数据。
4. SO_LINGER选项
1) 设置该选项:public void setSoLinger(boolean on, int seconds) throws SocketException
2) 读取该选项:public int getSoLinger() throws SocketException
3) SO_LINGER选项用来控制Socket关闭时的行为。
l socket.setSoLinger(true,0):执行Socket的close()方法时,该方法也会当即返回,但底层的Socket也会当即关闭,全部未发送完的剩余数据被丢弃。
l socket.setSoLinger(true,3600):执行Socket的close()方法时,该方法不会当即返回,而进入阻塞状态,同时,底层的Socket会尝试发送剩余的数据。只有知足如下两个条件之一,close()方法才返回:
n 底层的Socket已经发送完全部的剩余数据。
n 尽管底层的Socket尚未发送完全部的剩余数据,但已经阻塞了3600秒。close()方法的阻塞时间超过3600秒,也会返回,剩余未发送的数据被丢弃。
以上两种状况内,当close()方法返回后,底层的Socket会被关闭,断开链接。
4) setSoLinger(boolean on ,int second)方法中的seconds参数以秒为单位,而不是以毫秒为单位。
5. SO_RCVBUF选项
1) 设置该选项:public void setReceiveBufferSize(int size) throws SocketException
2) 读取该选项:public int getReceiveBufferSize() throws SocketException
3) SO_RCVBUF表示Socket的用于输入数据的缓冲区的大小。
4) 若是底层Socket不支持SO_RCVBUF选项,那么setReceiveBufferSize()方法会抛出SocketException。
6. SO_SNDBUF选项
1) 设置该选项:public void setSendBufferSize(int size) throws SocketException
2) 读取该选项:public int getSendBufferSize() throws SocketException
3) SO_SNDBUF表示Socket的用于输出数据的缓冲区的大小。
4) 若是底层Socket不支持SO_SNDBUF选项,setSendBufferSize()方法会抛出SocketException。
7. SO_KEEPALIVE选项
1) 设置该选项:public void setKeepAlive(boolean on) throws SocketException
2) 读取该选项:public int getKeepAlive() throws SocketException
3) 当SO_KEEPALIVE选项为true,表示底层的TCP实现会监视该链接是否有效。
4) SO_KEEPALIVE选项的默认值为false,表示TCP不会监视链接是否有效,不活动的客户端可能会永久存在下去,而不会注意到服务器已经崩溃。
8. OOBINLINE选项
1) 设置该选项:public void setOOBInline(int size) throws SocketException
2) 读取该选项:public int getOOBInline () throws SocketException
3) 当OOBINLINE为true时,表示支持发送一个字节的TCP紧急数据。Socket类的sendUrgentDate(int data)方法用于发送一个字节的TCP紧急数据。
4) OOBINLINE的默认值为false,在这种状况下,当接收方收到紧急数据时不做任何处理,直接将其丢弃。若是用户但愿发送紧急数据,应该把OOBINLINE设为true:socket.setOOBInline(true); 此时接收方会把接收到的紧急数据与普通数据放在一样的队列中。值得注意的是,除非使用一些更高层次的协议,不然接收方处理紧急数据的能力很是有限,当紧急数据到来时,接收方不会获得任何通知,所以接收方很难区分普通数据与紧急数据,只好按照一样的方式处理它们。
在客户/服务器通讯模式中,客户端须要主动创建与服务器链接的Socket,服务器端收到客户端的链接请求,也会建立与客户端链接的Socket。Socket能够看作是通讯链接两端的收发器,客户端和服务店都经过Socket来收发数据。
一、构造Socket
public Socket() 经过系统默认类型的 SocketImpl 建立未链接套接字
public Socket(String host,int port) throws UnknownHostException,IOException 建立一个流套接字并将其链接到指定主机上的指定端口号。
public Socket(String host,int port,InetAddress localAddr,int localPort) throws IOException 建立一个套接字并将其链接到指定远程主机上的指定远程端口。socket 会经过调用 bind() 函数来绑定提供的本地地址及端口。
public Socket(InetAddress address,int port)throws IOException建立一个流套接字并将其链接到指定 IP 地址的指定端口号。
public Socket(InetAddress address,int port,InetAddress localAddr,int localPort)throws IOException建立一个套接字并将其链接到指定远程地址上的指定远程端口。socket 会经过调用 bind() 函数来绑定提供的本地地址及端口。
除第一个外,其余4个构造方法都会试图和服务器创建链接,如何链接成功则返回Socket对象,若是链接失败就会抛出IOException。
实例1:扫描主机上1到1024的端口,判断这些端口是否被服务器监听
[java] view plain copy
1. package com.hanfeng.socket;
2.
3. import java.io.IOException;
4. import java.net.Socket;
5.
6. /**
7. * 扫描主机上1到1024的端口,判断这些端口是否被服务器监听
8. *
9. * @author hanfeng
10. * @data 2012-8-24 上午10:25:57
11. */
12. public class PortScanner {
13.
14. public static void main(String[] args) {
15. String host = "localhost";
16. if (args.length > 0) {
17. host = args[0];
18. }
19. new PortScanner().scan(host);
20. }
21.
22. public void scan(String host) {
23. Socket socket = null;
24.
25. for (int port = 1; port < 1024; port++) {
26. try {
27. socket = new Socket(host, port);
28. System.out.println("链接到端口:" + port);
29. } catch (IOException e) {
30. System.out.println("没法链接到端口:" + port);
31. } finally {
32. try {
33. if (socket != null) {
34. socket.close();
35. }
36. } catch (Exception e) {
37. e.printStackTrace();
38. }
39. }
40. }
41. }
42.
43. }
1.1 设置等待链接超时时间
[java] view plain copy
1. public void scan(String host) {
2. Socket socket = null;
3. socket = new Socket();
4. SocketAddress address = new InetSocketAddress(host, 1158);
5. try {
6. socket.connect(address, 60000);
7. System.out.println("链接成功!");
8. } catch (IOException e) {
9. System.out.println("链接超时!");
10. e.printStackTrace();
11. }
12. }
1.2设置服务器的IP地址
Socket(InetAddress address, int port) //第一个参数address表示主机的IP地址
Socket(String host, int port) //第一个参数host表示主机的名字
InetAddress类表示IP地址,其用法以下:
//返回本地主机的IP地址
InetAddress addr1=InetAddress.getLocalHost();
//返回表明"222.34.5.7"的IP地址
InetAddress addr2=InetAddress.getByName("222.34.5.7");
//返回域名为"www.google.com"的IP地址
InetAddress addr3=InetAddress.getByName("www.google.com");
[java] view plain copy
1. public static void main(String[] args) {
2. try {
3. InetAddress address = InetAddress.getLocalHost();
4. System.out.println("本机IP地址:"+address);
5. InetAddress address2 = InetAddress.getByName("222.34.5.7");
6. System.out.println("返回远程IP地址:"+address2);
7. InetAddress address3 = InetAddress.getByName("www.google.com");
8. System.out.println("返回域名IP地址:"+address3);
9. } catch (UnknownHostException e) {
10. e.printStackTrace();
11. }
12. }
1.3 设置客户端的IP地址
在一个Socket对象中,既包含远程服务器的IP地址和端口信息,也包含本地客户端的IP地址和端口信息。默认状况下,客户端的IP地址来自于客户程序所在的主机,客户端的端口则由操做系统随机分配。Socket类还有两个构造方法容许显式的设置客户端的IP地址和端口:
Socket(InetAddress address, int port, InetAddress localAddr, int localPort)throws IOException
Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException
实例:一个主机在Internet网络中的IP地址为”222.67.1.34“,在一个局域网中的IP地址为”112.5.4.3“。假定这个主机上的客户程序但愿和同一个局域网上的一个服务器程序(112.5.4.45:8000)通讯,则客户端可按照以下方式进行构造Socket对象:
[java] view plain copy
1. <span style="white-space:pre"> </span>InetAddress remoteAddr = InetAddress.getByName("112.5.4.45");
2. InetAddress localAddr = InetAddress.getByName("112.5.4.3");
3. Socket socket = new Socket(remoteAddr,8000,localAddr,2345);
1.4客户端链接服务器可能抛出的异常
UnknowHostException:若是没法识别主机的名字或IP地址,就会抛出异常
ConnectException:若是没有服务器进行监听指定的端口,或则服务器进程拒绝链接,就会抛出异常
SocketTimeoutException:等待链接超时,抛出异常
BindExcrption:若是没法把Socket对象与具体的本地IP地址或端口绑定,就会抛出异常
以上4个异常都是IOException的直接或间接子类,以下图
实例:捕获各类异常
[java] view plain copy
1. package com.hanfeng.socket;
2.
3. import java.io.IOException;
4. import java.net.BindException;
5. import java.net.ConnectException;
6. import java.net.InetSocketAddress;
7. import java.net.Socket;
8. import java.net.SocketAddress;
9. import java.net.SocketTimeoutException;
10. import java.net.UnknownHostException;
11.
12. /**
13. *
14. * @author hanfeng
15. * @data 2012-8-24 上午11:16:32
16. */
17. public class ConnectTest {
18.
19. public static void main(String[] args) {
20. String host = "www.google.com";
21. int port = 80;
22. if (args.length>1) {
23. host = args[0];
24. port = Integer.parseInt(args[1]);
25. }
26. new ConnectTest().connect(host, port);
27. }
28.
29. public void connect(String host,int port){
30. SocketAddress address = new InetSocketAddress(host, port);
31. Socket socket = null;
32. String result = "";
33.
34. try {
35. long begin = System.currentTimeMillis();//计算开始链接的时间
36. socket = new Socket();//开始创建链接
37. socket.connect(address, 6000);//设置链接超时时间
38. long end = System.currentTimeMillis();// 计算机链接结束的时间
39. result = (end-begin)+"ms";
40. } catch (BindException e) {
41. result = "IP地址或端口绑定异常!";
42. } catch (UnknownHostException e) {
43. result = "未识别主机地址!";
44. }catch (SocketTimeoutException e) {
45. result = "链接超时!";
46. }catch (ConnectException e) {
47. result = "拒绝链接!";
48. }catch (Exception e) {
49. result = "失败啦!";
50. }finally{
51. if (socket!=null) {
52. try {
53. socket.close();
54. } catch (IOException e) {
55. e.printStackTrace();
56. }
57. }
58. }
59. System.out.println("远程地址信息==>"+address+":"+result);
60. }
61. }
二、获取Socket信息
如下方法用于获取Socket的有关信息:
getInetAddress():得到远程服务器的IP地址。
getPort():得到远程服务器的端口。
getLocalAddress():得到客户本地的IP地址。
getLocalPort():得到客户本地的端口。
getInputStream():得到输入流。若是Socket尚未链接,或者已经关闭,或者已经经过shutdownInput()方法关闭输入流,那么此方法会抛出IOException。
getOutputStream():得到输出流。若是Socket尚未链接,或者已经关闭,或者已经经过shutdownOutput()方法关闭输出流,那么此方法会抛出IOException。
实例:HTTPClient类访问网页
[java] view plain copy
1. package com.hanfeng.socket;
2.
3. import java.io.BufferedReader;
4. import java.io.ByteArrayOutputStream;
5. import java.io.InputStream;
6. import java.io.InputStreamReader;
7. import java.io.OutputStream;
8. import java.net.Socket;
9.
10.
11. public class HttpClientDemo {
12. String host ="www.google.com";
13. int port = 80;
14. Socket socket = null;
15.
16. public void createSocket() throws Exception{
17. socket = new Socket(host, port);
18. }
19.
20. public void communicate() throws Exception{
21. StringBuffer sb=new StringBuffer("GET "+" HTTP/1.1\r\n");
22. // sb.append("Host: www.javathinker.org\r\n");
23. // sb.append("Accept: */*\r\n");
24. // sb.append("Accept-Language: zh-cn\r\n");
25. // sb.append("Accept-Encoding: gzip, deflate\r\n");
26. // sb.append("User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)\r\n");
27. // sb.append("Connection: Keep-Alive\r\n\r\n");
28.
29. //发出HTTP请求
30. OutputStream socketOut = socket.getOutputStream();
31. socketOut.write(sb.toString().getBytes());
32. socket.shutdownOutput();
33.
34. //接收响应结果
35. InputStream socketInput = socket.getInputStream();
36. BufferedReader buffer = new BufferedReader(new InputStreamReader(socketInput));
37. String data = null;
38. while ((data=buffer.readLine())!=null) {
39. System.out.println(data);
40. }
41. }
42.
43. /**
44. * @param args
45. * @throws Exception
46. */
47. public static void main(String[] args) throws Exception {
48. HttpClientDemo httpClient = new HttpClientDemo();
49. httpClient.createSocket();
50. httpClient.communicate();
51. }
52.
53. }
三、关闭Socket
当客户与服务器的通讯结束,应该及时关闭Socket,以释放Socket占用的包括端口在内的各类资源。Socket的close()方法负责关闭Socket。当一个Socket对象被关闭,就不能在经过他的输入流和输出流进行I/O操做,不然会致使IOException。
为了确保关闭Socket的操做总被执行,建议把这个操做放在finally代码块中。
[java] view plain copy
1. finally {
2. try {
3. if(socket!=null)socket.close();
4. } catch (IOException e) {
5. e.printStackTrace();
6. }
7. }
Socket类提供了3个状态测试方法
isClosed():若是Socket已经链接到远程主机,而且尚未关闭,则返回true
isConnected():若是Socket曾经链接到远程主机,则返回true
isBound():若是Socket已经与一个本地端口绑定,则返回true
若是要判断一个Socket对象当前是否处于链接状态,能够采用如下方式:
boolean isConnected = socket.isConnected()&&!socket.isClosed();
四、半关闭Socket
4.1 有的时候,可能仅仅但愿关闭输出流或输入流之一。此时能够采用Socket类提供的半关闭方法:
shutdownInput():关闭输入流。
shutdownOutput(): 关闭输出流。
4.2 前后调用Socket的shutdownInput()和shutdownOutput()方法,仅仅关闭了输入流和输出流,并不等价于调用Socket的close()方法。在通讯结束后,仍然要调用Socket的close()方法,由于只有该方法才会释放Socket占用的资源,好比占用的本地端口等。
4.3 Socket类还提供了两个状态测试方法,用来判断输入流和输出流是否关闭:
public boolean isInputShutdown()
public boolean isOutputShutdown()
五、设置Socket的选项
Socket有如下几个选项:
n TCP_NODELAY:表示当即发送数据。
n SO_RESUSEADDR:表示是否容许重用Socket所绑定的本地地址。
n SO_TIMEOUT:表示接收数据时的等待超时时间。
n SO_LINGER:表示当执行Socket的close()方法时,是否当即关闭底层的Socket。
n SO_SNFBUF:表示发送数据的缓冲区的大小。
n SO_RCVBUF:表示接收数据的缓冲区的大小。
n SO_KEEPALIVE:表示对于长时间处于空闲状态的Socket,是否要自动把它关闭。
n OOBINLINE:表示是否支持发送一个字节的TCP紧急数据。
1. TCP_NODELAY选项
1) 设置该选项:public void setTcpNoDelay(boolean on) throws SocketException
2) 读取该选项:public boolean getTcpNoDelay() throws SocketException
3) TCP_NODEALY的默认值为false,表示采用Negale算法。若是调用setTcpNoDelay(true)方法,就会关闭Socket的缓冲,确保数据及时发送:
if(!socket.getTcpNoDelay()) socket.setTcpNoDelay(true);
4) 若是Socket的底层实现不支持TCP_NODELAY选项,那么getTcpNoDelay()和setTcpNoDelay()方法会抛出SocketException。
2. SO_RESUSEADDR选项
1) 设置该选项:public void setResuseAddress(boolean on) throws SocketException
2) 读取该选项:public boolean getResuseAddress() throws SocketException
3) 为了确保一个进程关闭了Socket后,即便它还没释放端口,同一个主机上的其余进程还能够马上重用该端口,能够调用Socket的setResuseAddress(true)方法:
if(!socket.getResuseAddress()) socket.setResuseAddress(true);
4) 值得注意的是socket.setResuseAddress(true)方法必须在Socket尚未绑定到一个本地端口以前调用,不然执行socket.setResuseAddress(true)方法无效。所以必须按照如下方式建立Socket对象,而后再链接远程服务器:
Socket socket = new Socket(); //此时Socket对象未绑定到本地端口,而且未链接远程服务器 socket.setResuseAddress(true); SocketAddress remoteAddr = new InetSocketAddress("remotehost",8000); socket.connect(remoteAddr); //链接远程服务器,而且绑定匿名的本地端口 或者: Socket socket = new Socket(); //此时Socket对象未绑定到本地端口,而且未链接远程服务器 socket.setResuseAddress(true); SocketAddress localAddr = new InetSocketAddress("localhost",9000); SocketAddress remoteAddr = new InetSocketAddress("remotehost",8000); socket.bind(localAddr); //与本地端口绑定 socket.connect(remoteAddr); //链接远程服务器,而且绑定匿名的本地端口 |
3. SO_TIMEOUT选项
1) 设置该选项:public void setSoTimeout(int milliseconds) throws SocketException
2) 读取该选项:public int getSoTimeOut() throws SocketException
3) 当经过Socket的输入流读数据时,若是尚未数据,就会等待。Socket类的SO_TIMEOUT选项用于设定接收数据的等待超时时间,单位为毫秒,它的默认值为0,表示会无限等待,永远不会超时。
4) Socket的setSoTimeout()方法必须在接收数据以前执行才有效。此外,当输入流的read()方法抛出SocketTimeoutException后,Socket仍然是链接的,能够尝试再次读取数据。
4. SO_LINGER选项
1) 设置该选项:public void setSoLinger(boolean on, int seconds) throws SocketException
2) 读取该选项:public int getSoLinger() throws SocketException
3) SO_LINGER选项用来控制Socket关闭时的行为。
l socket.setSoLinger(true,0):执行Socket的close()方法时,该方法也会当即返回,但底层的Socket也会当即关闭,全部未发送完的剩余数据被丢弃。
l socket.setSoLinger(true,3600):执行Socket的close()方法时,该方法不会当即返回,而进入阻塞状态,同时,底层的Socket会尝试发送剩余的数据。只有知足如下两个条件之一,close()方法才返回:
n 底层的Socket已经发送完全部的剩余数据。
n 尽管底层的Socket尚未发送完全部的剩余数据,但已经阻塞了3600秒。close()方法的阻塞时间超过3600秒,也会返回,剩余未发送的数据被丢弃。
以上两种状况内,当close()方法返回后,底层的Socket会被关闭,断开链接。
4) setSoLinger(boolean on ,int second)方法中的seconds参数以秒为单位,而不是以毫秒为单位。
5. SO_RCVBUF选项
1) 设置该选项:public void setReceiveBufferSize(int size) throws SocketException
2) 读取该选项:public int getReceiveBufferSize() throws SocketException
3) SO_RCVBUF表示Socket的用于输入数据的缓冲区的大小。
4) 若是底层Socket不支持SO_RCVBUF选项,那么setReceiveBufferSize()方法会抛出SocketException。
6. SO_SNDBUF选项
1) 设置该选项:public void setSendBufferSize(int size) throws SocketException
2) 读取该选项:public int getSendBufferSize() throws SocketException
3) SO_SNDBUF表示Socket的用于输出数据的缓冲区的大小。
4) 若是底层Socket不支持SO_SNDBUF选项,setSendBufferSize()方法会抛出SocketException。
7. SO_KEEPALIVE选项
1) 设置该选项:public void setKeepAlive(boolean on) throws SocketException
2) 读取该选项:public int getKeepAlive() throws SocketException
3) 当SO_KEEPALIVE选项为true,表示底层的TCP实现会监视该链接是否有效。
4) SO_KEEPALIVE选项的默认值为false,表示TCP不会监视链接是否有效,不活动的客户端可能会永久存在下去,而不会注意到服务器已经崩溃。
8. OOBINLINE选项
1) 设置该选项:public void setOOBInline(int size) throws SocketException
2) 读取该选项:public int getOOBInline () throws SocketException
3) 当OOBINLINE为true时,表示支持发送一个字节的TCP紧急数据。Socket类的sendUrgentDate(int data)方法用于发送一个字节的TCP紧急数据。
4) OOBINLINE的默认值为false,在这种状况下,当接收方收到紧急数据时不做任何处理,直接将其丢弃。若是用户但愿发送紧急数据,应该把OOBINLINE设为true:socket.setOOBInline(true); 此时接收方会把接收到的紧急数据与普通数据放在一样的队列中。值得注意的是,除非使用一些更高层次的协议,不然接收方处理紧急数据的能力很是有限,当紧急数据到来时,接收方不会获得任何通知,所以接收方很难区分普通数据与紧急数据,只好按照一样的方式处理它们。