@lemonguge
2015-06-29T13:16:42.000000Z
字数 6038
阅读 490
Socket
UDP传输协议的应用可以类比为对讲机,按住对讲机的开关就可以说话,而不关心有没有人听他说话,不需要建立连接。TCP传输协议可以类比为打电话,只有形成传输数据的通道才能进行数据的传输,需要通过三次握手完成连接。什么是三次握手呢?觉个例子,我和你分别在两座山头上,互相喊话,假设我先喊了一句“听到我说话了吗?”,如果你听到了,这就说明我向你说话是可行的,但是我不知道你听到没有,因此你说“我听到了”,这样我就知道我向你说话是可行的,同时你向我说话也是可行的,可你并不知道你向我说话,我到底听到了没有,所以我应该再向你说一句“OK,我知道啦”,这样你也知道你向我说话是可行的。
TCP传输协议对应的端点Socket有两个,分别为客户端套接字Socket和服务器套接字ServerSocket。当建立连接之后,通过客户端套接字Socket中的IO流(socket流)进行数据的传输。
同样明确客户端的实现思路:
Socket套接字端点,应该明确服务器的IP和端口,必须先运行服务器端,这将建立起连接;socket流在底层建立完成,可以通过getInputStream()和getOutputStream()来获取输入和输出字节流);Socket套接字,释放资源。
import java.io.IOException;import java.io.OutputStream;import java.net.Socket;public class Client {public static void main(String[] args) throws IOException {// 要先运行服务端,否则将不能连接成功(Connection refused: connect)Socket socket = new Socket("JieHong-PC", 8011);// 获取socket流中的输出流OutputStream out = socket.getOutputStream();// 使用输出流将指定的数据写出去out.write("TCP传输协议:客户端运行".getBytes());// 关闭资源socket.close();}} ///~
同样明确服务端的实现思路:
ServerSocket套接字端点,应该明确服务器所侦听的端口;accept()方法获取连接过来的客户端对象;socket流来读取客户端发来的数据;
import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {// 创建服务端对象,侦听8011端口ServerSocket ss = new ServerSocket(8011);// 获取连接过来的客户端对象Socket s = ss.accept(); // 阻塞式方法String ip = s.getInetAddress().getHostAddress();int port = s.getPort();// 获取输入流来读取 客户端发过来的数据InputStream in = s.getInputStream();byte[] buf = new byte[1024];int length = in.read(buf);String data = new String(buf, 0, length);System.out.println(ip + ":" + port + ":" + data);s.close();ss.close(); // 一般不关闭}} ///~
当客户端向服务器端发送请求,服务端收到请求后并处理,接着返回请求处理后的信息给客户端。
import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;// 服务端public class Server {public static void main(String[] args) throws IOException {// 创建服务端对象,侦听8011端口ServerSocket ss = new ServerSocket(8011);// 获取连接过来的客户端对象Socket s = ss.accept(); // 阻塞式方法String ip = s.getInetAddress().getHostAddress();int port = s.getPort();// 获取输入流来读取 客户端发过来的数据InputStream in = s.getInputStream();byte[] buf = new byte[1024];int length = in.read(buf);String data = new String(buf, 0, length);System.out.println(ip + ":" + port + ":" + data);// 使用客户端socket对象的输出流给客户端返回数据OutputStream out = s.getOutputStream();out.write("处理完成".getBytes());s.close();ss.close(); // 一般不关闭}} /* Output:192.168.2.109:50447:TCP传输协议:客户端运行*///:~
import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;public class Client {public static void main(String[] args) throws IOException {// 要先运行服务端,否则将不能连接成功Socket socket = new Socket("JieHong-PC", 8011);// 获取socket流中的输出流OutputStream out = socket.getOutputStream();// 使用输出流将指定的数据写出去out.write("TCP传输协议:客户端运行".getBytes());// 读取服务端返回的数据,使用socket读取流InputStream in = socket.getInputStream();byte[] buf = new byte[1024];int length = in.read(buf);String back = new String(buf, 0, length);System.out.println(back);// 关闭资源socket.close();}} /* Output:处理完成*///:~
当我们从客户端Socket获取到InputStream和OutputStream对象时,便可以使用Java I/O的装饰类来方便我们操作。
当我们上传文件到服务器端,上传完成之后,需要调用shutdownOutput()方法来告诉服务端,客户端写完了,否则服务器无法知道文件的结束标识符。
我们可以通过浏览器进行访问,那么浏览器就是一个客户端,我们可以自己写一个服务器端,来看看浏览器会给我发送哪些信息。
import java.io.BufferedOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;public class BrowserServer {public static void main(String[] args) throws IOException {ServerSocket ss = new ServerSocket(8012);Socket s = ss.accept();// 获取ipString ip = s.getInetAddress().getHostAddress();System.out.println(ip + " connected..");InputStream in = s.getInputStream();byte[] buf = new byte[1024];int len = in.read(buf);String text = new String(buf, 0, len);System.out.println(text);System.out.println("--END--");PrintWriter pw = new PrintWriter(new BufferedOutputStream(s.getOutputStream()), true);pw.println("<font color='red' size='7'>请求已收到</font>"); //在浏览器可以看到返回的信息s.close();ss.close();}} /* Output: // 浏览器输入http://www.jiehong.com:8012127.0.0.1 connected..GET / HTTP/1.1Accept: text/html, application/xhtml+xml, */*Accept-Language: zh-CNUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like GeckoAccept-Encoding: gzip, deflateHost: www.jiehong.com:8012DNT: 1Connection: Keep-Alive--END--*///:~
由于我已经在本地域名解析配置了"127.0.0.1 www.jiehong.com",所以看到的IP是"127.0.0.1"。
在java.net包中包含两个有趣的类:URL类和URLConnection类,这两个类可以用来创建客户端到Web服务器(HTTP服务器)的连接,以下是一个简单的演示:
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.URL;import java.net.URLConnection;public class URLDemo {public static void main(String[] args) throws IOException {URL url = new URL("http://www.baidu.com"); // http是协议不可省(no protocol)System.out.println(url.getProtocol()); // 输出http// 获取url对象的Url连接器对象URLConnection urlConnection = url.openConnection();// 读取从服务端返回的数据BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));String line = null;while ((line = br.readLine()) != null)System.out.println(line);br.close();}} ///:~
以上的小示例,输出很多所以不展示。默认情况下URLConnection发送一个HTTP GET请求到Web服务器。如果你想发送一个HTTP POST请求,要调用URLConnection.setDoOutput(true)方法,接下来你就可以打开URLConnection的OutputStream,可以使用这个OutputStream向相应的HTTP请求中写任何数据,但你要记得将其转换成URL编码。
URL也被叫做统一资源定位符,URL类是另外一种打开文件的方式,下面是一个如何使用URL类打开一个本地文件系统文件的例子:
import java.io.BufferedReader;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.URL;import java.net.URLConnection;// D盘有一个file.txt文件,文件有两行内容,分别为“123你好”和“abc”public class URLDemo {public static void main(String[] args) throws IOException {ReadFileByURL();}public static void ReadFileByURL() throws IOException {URL url = new URL("file:" + File.separator + "d:" + File.separator + "file.txt");System.out.println("Protocol:" + url.getProtocol());URLConnection urlConnection = url.openConnection();InputStream input = urlConnection.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(input));String line = null;while ((line = br.readLine()) != null)System.out.println(line);}} /* Output:Protocol:file123你好abc*///:~
对Java网络编程的Socket讲解结束,这一块的内容不多且很简单。后记:很早就想写写这一块的内容了,尤其是当我向着手写Java nio的时候,这种想法特别强烈,写于2015年6月29日。