Java网络编程之Socket
一、Socket编程简介
套接字编程
利用套接字(Socket)开发网络应用程序早已被广泛的采用,以至于成为事实上的标准。
通信的两端都要有Socket,是两台机器间通信的端点
网络通信其实就是Socket间的通信。
Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
一般主动发起通信的应用程序属客户端,等待通信请求的为服务端
基于Socket的TCP编程
Java语言的基于套接字编程分为服务端编程和客户端编程,其通信模型如图所示
二、客户端Socket编程
客户端Socket的工作过程
①创建Socket:根据指定服务端的 IP 地址或端口号构造 Socket 类对象。若服务器端响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常。
②打开连接到 Socket 的输入/出流: 使用 getInputStream()方法获得输入流,使用 getOutputStream()方法获得输出流,进行数据传输
③按照一定的协议对 Socket 进行读/写操作:通过输入流读取服务器放入线路的信息(但不能读取自己放入线路的信息),通过输出流将信息写入线程。
④关闭 Socket:断开客户端到服务器的连接,释放线路
客户端创建Socket对象
客户端程序可以使用Socket类创建对象,创建的同时会自动向服务器方发起连接。Socket的构造方法有:
|
通过系统默认类型的 SocketImpl 创建未连接套接字 |
|
创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 |
|
已过时。 Use DatagramSocket instead for UDP transport. |
|
创建一个套接字并将其连接到指定远程地址上的指定远程端口。 |
|
创建一个未连接的套接字并指定代理类型(如果有),该代理不管其他设置如何都应被使用。 |
|
使用用户指定的 SocketImpl 创建一个未连接 Socket。 |
|
创建一个流套接字并将其连接到指定主机上的指定端口号。 |
|
已过时。 使用 DatagramSocket 取代 UDP 传输。 |
|
创建一个套接字并将其连接到指定远程主机上的指定远程端口。 |
常用的以下两个构造方法
Socket(String host,int port)throws UnknownHostException,IOException:向服务器(域名是host。端口号为port)发起TCP连接,若成功,则创建Socket对象,否则抛出异常。
Socket(InetAddress address,int port)throws IOException:根据InetAddress对象所表示的IP地址以及端口号port发起连接。
客户端建立socketAtClient对象的过程就是向服务器发出套接字连接请求
三、服务器端Socket编程
服务器程序的工作过程
①调用 ServerSocket(int port) :创建一个服务器端套接字,并绑定到指定端口上。用于监听客户端的请求。
②调用 accept():监听连接请求,如果客户端请求连接,则接受连接,返回通信套接字对象。
③调用 该Socket类对象的 getOutputStream() 和 getInputStream ():获取输出流和输入流,开始网络数据的发送和接收。
④关闭ServerSocket和Socket对象:客户端访问结束,关闭通信套接字。
服务器建立ServerSocket对象
ServerSocket 对象负责等待客户端请求建立套接字连接,类似邮局某个窗口中的业务员。也就是说,服务器必须事先建立一个等待客户请求建立套接字连接的ServerSocket对象。
所谓“接收”客户的套接字请求,就是accept()方法会返回一个 Socket 对象
ServerSocket构造方法
创建非绑定服务器套接字。 |
创建绑定到特定端口的服务器套接字。 |
利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。 |
使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。 |
四、java.net.Socket简介(客户端Socket)
Socket数据结构
Socket方法摘要
|
将套接字绑定到本地地址。 |
|
关闭此套接字。 |
|
将此套接字连接到服务器。 |
|
将此套接字连接到服务器,并指定一个超时值。 |
|
返回与此数据报套接字关联的唯一 |
|
返回套接字连接的地址。 |
|
返回此套接字的输入流。 |
|
测试是否启用 SO_KEEPALIVE。 |
|
获取套接字绑定的本地地址。 |
|
返回此套接字绑定到的本地端口。 |
|
返回此套接字绑定的端点的地址,如果尚未绑定则返回 |
|
测试是否启用 OOBINLINE。 |
|
返回此套接字的输出流。 |
|
返回此套接字连接到的远程端口。 |
|
获取此 Socket 的 SO_RCVBUF 选项的值,该值是平台在 Socket 上输入时使用的缓冲区大小。 |
|
返回此套接字连接的端点的地址,如果未连接则返回 |
|
测试是否启用 SO_REUSEADDR。 |
|
获取此 Socket 的 SO_SNDBUF 选项的值,该值是平台在 Socket 上输出时使用的缓冲区大小。 |
|
返回 SO_LINGER 的设置。 |
|
返回 SO_TIMEOUT 的设置。 |
|
测试是否启用 TCP_NODELAY。 |
|
为从此 Socket 上发送的包获取 IP 头中的流量类别或服务类型。 |
|
返回套接字的绑定状态。 |
|
返回套接字的关闭状态。 |
|
返回套接字的连接状态。 |
|
返回是否关闭套接字连接的半读状态 (read-half)。 |
|
返回是否关闭套接字连接的半写状态 (write-half)。 |
|
在套接字上发送一个紧急数据字节。 |
|
启用/禁用 SO_KEEPALIVE。 |
|
启用/禁用 OOBINLINE(TCP 紧急数据的接收者) 默认情况下,此选项是禁用的,即在套接字上接收的 TCP 紧急数据被静默丢弃。 |
|
设置此套接字的性能偏好。 |
|
将此 Socket 的 SO_RCVBUF 选项设置为指定的值。 |
|
启用/禁用 SO_REUSEADDR 套接字选项。 |
|
将此 Socket 的 SO_SNDBUF 选项设置为指定的值。 |
|
为应用程序设置客户端套接字实现工厂。 |
|
启用/禁用具有指定逗留时间(以秒为单位)的 SO_LINGER。 |
|
启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。 |
|
启用/禁用 TCP_NODELAY(启用/禁用 Nagle 算法)。 |
|
为从此 Socket 上发送的包在 IP 头中设置流量类别 (traffic class) 或服务类型八位组 (type-of-service octet)。 |
|
此套接字的输入流置于“流的末尾”。 |
|
禁用此套接字的输出流。 |
|
将此套接字转换为 |
五、java.net.ServerSocket简介(服务端Socket)
ServerSocket数据结构
ServerSocket方法摘要
|
侦听并接受到此套接字的连接。 |
|
将 |
|
将 |
|
关闭此套接字。 |
|
返回与此套接字关联的唯一 |
|
返回此服务器套接字的本地地址。 |
|
返回此套接字在其上侦听的端口。 |
|
返回此套接字绑定的端点的地址,如果尚未绑定则返回 |
|
获取此 ServerSocket 的 SO_RCVBUF 选项的值,该值是将用于从此 ServerSocket 接受的套接字的建议缓冲区大小。 |
|
测试是否启用 SO_REUSEADDR。 |
|
获取 SO_TIMEOUT 的设置。 |
|
ServerSocket 的子类使用此方法重写 accept() 以返回它们自己的套接字子类。 |
|
返回 ServerSocket 的绑定状态。 |
|
返回 ServerSocket 的关闭状态。 |
|
设置此 ServerSocket 的性能首选项。 |
|
为从此 ServerSocket 接受的套接字的 SO_RCVBUF 选项设置默认建议值。 |
|
启用/禁用 SO_REUSEADDR 套接字选项。 |
|
为应用程序设置服务器套接字实现工厂。 |
|
通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。 |
|
作为 |
六、示例分析
从客户端发送文件给服务端,服务端保存到本地,并返回发送成功给客户端
1 package me.net.socket;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileNotFoundException;
6 import java.io.FileOutputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.OutputStream;
10 import java.net.InetAddress;
11 import java.net.ServerSocket;
12 import java.net.Socket;
13 import java.net.UnknownHostException;
14
15 import org.junit.Test;
16
17 /**
18 * 从客户端发送文件给服务端,服务端保存到本地,并返回发送成功给客户端
19 *
20 * @author Administrator
21 *
22 */
23 public class TestTCP3 {
24
25 /**
26 * 模拟客户端
27 */
28 @Test
29 public void client() {
30 Socket socket = null;
31 FileInputStream fis = null;
32 InputStream is = null;
33 OutputStream os = null;
34 try {
35 // 1.创建socket对象
36 socket = new Socket(InetAddress.getByName("127.0.0.1"), 8989);
37 // 2.向服务端发送数据
38 os = socket.getOutputStream();
39 fis = new FileInputStream(new File("1.jpg"));
40 byte[] b = new byte[20];
41 int len = 0;
42 while ((len = fis.read(b)) != -1) {
43 os.write(b, 0, len);
44 }
45 os.flush();
46 socket.shutdownOutput();
47 // 3.接受来自服务端的响应
48 is = socket.getInputStream();
49 byte[] b1 = new byte[200];
50 int len1 = 0;
51 while ((len1 = is.read(b1, 0, b1.length)) != -1) {
52 System.out.print(new String(b1, 0, len1, "UTF-8"));
53 }
54 socket.shutdownInput();
55 } catch (UnknownHostException e) {
56 e.printStackTrace();
57 } catch (FileNotFoundException e) {
58 e.printStackTrace();
59 } catch (IOException e) {
60 e.printStackTrace();
61 } finally {
62 // 4.关闭io流
63 if (is != null) {
64 try {
65 is.close();
66 } catch (IOException e) {
67 e.printStackTrace();
68 }
69 }
70 if (fis != null) {
71 try {
72 fis.close();
73 } catch (IOException e) {
74 e.printStackTrace();
75 }
76 }
77 if (os != null) {
78 try {
79 os.close();
80 } catch (IOException e) {
81 e.printStackTrace();
82 }
83 }
84 if (socket != null) {
85 try {
86 socket.close();
87 } catch (IOException e) {
88 e.printStackTrace();
89 }
90 }
91 }
92 }
93
94 /**
95 * 模拟服务器端
96 */
97 @Test
98 public void server() {
99 ServerSocket ss = null;
100 Socket socket = null;
101 InputStream is = null;
102 FileOutputStream fos = null;
103 OutputStream os = null;
104 try {
105 // 1.创建服务器端socket对象
106 ss = new ServerSocket(8989);
107 socket = ss.accept();
108 // 2.收取来自客户端的数据
109 is = socket.getInputStream();
110 fos = new FileOutputStream(new File("2.jpg"));
111 byte[] b = new byte[20];
112 int len = 0;
113 while ((len = is.read(b)) != -1) {
114 fos.write(b, 0, len);
115 }
116 fos.flush();
117 System.out.println("收到来自于" + socket.getInetAddress().getHostName() + "的文件!");
118 socket.shutdownInput();
119 // 3.向客户端发出回应
120 os = socket.getOutputStream();
121 os.write("已收到客户端发来的图片".getBytes("UTF-8"));
122 os.flush();
123 socket.shutdownOutput();
124 } catch (FileNotFoundException e) {
125 e.printStackTrace();
126 } catch (IOException e) {
127 e.printStackTrace();
128 } finally {
129 // 4.关闭io流
130 if (os != null) {
131 try {
132 os.close();
133 } catch (IOException e) {
134 e.printStackTrace();
135 }
136 }
137 if (fos != null) {
138 try {
139 fos.close();
140 } catch (IOException e) {
141 e.printStackTrace();
142 }
143 }
144 if (is != null) {
145 try {
146 is.close();
147 } catch (IOException e) {
148 e.printStackTrace();
149 }
150 }
151 if (socket != null) {
152 try {
153 socket.close();
154 } catch (IOException e) {
155 e.printStackTrace();
156 }
157 }
158 if (ss != null) {
159 try {
160 ss.close();
161 } catch (IOException e) {
162 e.printStackTrace();
163 }
164 }
165 }
166 }
167 }
如果,您对我的这篇博文有什么疑问,欢迎评论区留言,大家互相讨论学习。
如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博文感兴趣,可以关注我的后续博客,我是【AlbertRui】。转载请注明出处和链接地址,欢迎转载,谢谢!