客户端:指浏览器或者自定义的客户端。
服务端:像Tomcat服务器或者自定义客户端。
TCP:传输层协议。
IP:网络层协议。
TCP使用案例:用于RPC接口调用,发送电子邮件等需要可靠性传输的事情。
UDP使用案例:用于视频这类的传输。
相同点:
不同点:
客户端建立连接请求时的三次握手:
第一次握手:客户端向服务端发送syn报文和序号x;
第二次握手:服务端接收到客户端的请求,发送ack=x+1报文,并发送序号y,syn报文。
第三次握手:客户端接收到服务端的请求,发送ack= y+1报文,并发送序号z。
为什么是三次握手而不是二次或者四次五次呢?
因此根据三次握手我们客户端和服务端都可以知道自己发送正常,对方接收正常;而二次握手的话,服务端并不知道自己发送正常,只能知道客户端发送接收正常,自己接收正常,而不知道自己发送正常。而三次握手刚好就满足了这个条件。既然三次握手就满足了,四次五次就会显得多余了,虽然更多次的握手可以更加保证通信的正常,但是正常来说三次握手就能保证通信99%是可靠的,再多次的握手可靠性的提高并不高,意义不大。
客户端服务端都可以通过四次挥手关闭连接,但一般都是客户端发起四次挥手请求来关闭连接,因为我们服务端一般是24小时在线服务的。
以客户端发起断开连接为例:
客户端告诉服务端我要断开连接了。
服务端相应客户端说我收到你的断开连接请求了。
服务端断开连接,并发送请求告诉客户端我和你断开连接了。
客户端收到断开连接请求断开了连接,并发送确认断开的请求告诉服务我和你断开连接了,这时候服务端就接收不到客户端的请求了,如果接收到了就表示没有真正断开连接。
public class TCPTest { /** * 客户端 */ @Test public void client() throws IOException { Socket socket = null; OutputStream os = null; try { InetAddress inetAddress = InetAddress.getByName("127.0.0.1"); //1创建Socke连接对象,指明要连接的服务端的ip和端口号 socket = new Socket(inetAddress, 9000); //2获取输出流,用于输出数据 os = socket.getOutputStream(); //3写数据 os.write("我叫客户端,你好啊".getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { //4关闭流资源 if (os != null) { os.close(); } if (socket != null) { socket.close(); } } } @Test public void server() throws IOException { InputStream is = null; ByteArrayOutputStream baos = null; ServerSocket serverSocket = null; Socket socket = null; try { //创建服务端的ServerSocket,指明自己的端口号,给客户端通过指定的ip和该端口号确定进程进行连接 serverSocket = new ServerSocket(9000); //accept()用于接收来自客户端的连接 socket = serverSocket.accept(); //获取输入流 is = socket.getInputStream(); //读取输入流中的数据,ByteArrayOutputStream该流维护动态数组保存写入的数据 baos = new ByteArrayOutputStream(); byte[] buff = new byte[1024]; int len; while ((len = is.read(buff)) != -1) { baos.write(buff, 0, len); } System.out.println(baos.toString()); System.out.println("收到客户端请求了!"); } catch (IOException e) { e.printStackTrace(); } finally { //关闭资源 baos.close(); is.close(); socket.close(); serverSocket.close(); } } /** * 我叫客户端,你好啊 * 收到客户端请求了! */ }
public class TCPFileTest { /** * 客户端给服务端发生文件 */ @Test public void client() throws IOException { Socket socket = null; OutputStream os = null; FileInputStream fis = null; ByteArrayOutputStream baos = null; try { InetAddress inetAddress = InetAddress.getByName("127.0.0.1"); //1创建Socke连接对象,指明要连接的服务端的ip和端口号 socket = new Socket(inetAddress, 9000); //2获取输出流,用于输出数据 os = socket.getOutputStream(); fis = new FileInputStream("是大臣.jpg"); //3写数据 byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { os.write(buffer, 0, len); } System.out.println("向服务端发送图片成功!"); //关闭数据的输出,不然服务端会一直阻塞着接收不往下走 socket.shutdownOutput(); //接收服务端的应答 InputStream is = socket.getInputStream(); baos = new ByteArrayOutputStream(); byte[] buff = new byte[1024]; int length; while ((length = is.read(buff)) != -1) { baos.write(buff, 0, length); } System.out.println(baos.toString()); } catch (IOException e) { e.printStackTrace(); } finally { //4关闭流资源 if (os != null) { os.close(); } if (socket != null) { socket.close(); } } } /** * 向服务端发送图片成功! * 客户端,你的文件收到了 */ /** * 服务端接收文件保存到本地 * * @throws IOException */ @Test public void server() throws IOException { InputStream is = null; FileOutputStream fos = null; ServerSocket serverSocket = null; Socket socket = null; OutputStream os = null; try { //创建服务端的ServerSocket,指明自己的端口号,给客户端通过指定的ip和该端口号确定进程进行连接 serverSocket = new ServerSocket(9000); //accept()用于接收来自客户端的连接 socket = serverSocket.accept(); //获取输入流 is = socket.getInputStream(); //打开输出流写文件 fos = new FileOutputStream("是大臣-copy5.jpg"); byte[] buff = new byte[1024]; int len; while ((len = is.read(buff)) != -1) { fos.write(buff, 0, len); } System.out.println("收到客户端请求了!"); //给客户端写出应答 os = socket.getOutputStream(); os.write("客户端,你的文件收到了".getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { //关闭资源 fos.close(); is.close(); socket.close(); serverSocket.close(); os.close(); } } /** * 收到客户端请求了! */ }
public class UDPTest { /** * 发生端 */ @Test public void sender() throws IOException { //1创建socket,发送端不需要指定要发生的ip和端口,直接在要发生的包里指定接收的地址端口即可。 DatagramSocket socket = new DatagramSocket(); byte[] data = "我是发送端,这是给你的一封信!".getBytes(); //数据包要发送的地址 InetAddress address = InetAddress.getByName("127.0.0.1"); //2创建发送包 DatagramPacket packet = new DatagramPacket(data, 0, data.length, address, 9999); //3发送 socket.send(packet); System.out.println("发送数据成功!"); //4关闭流 socket.close(); } /** * 接收端 */ @Test public void receiver() throws IOException { //1创建接收端socket,要指定接收端的端口号,才能接收 DatagramSocket socket = new DatagramSocket(9999); //2创建用于接收数据流的数组容器 byte[] buffer = new byte[1024]; //2创建接收包,用于接收数据包 DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length); //3发送 socket.receive(packet); //4打印接收到的数据 System.out.println(new String(packet.getData(),0,packet.getLength())); System.out.println("接收数据成功!"); //5关闭流 socket.close(); } }
public class URLTest { /** * 使用url去下载网络文件 * * @throws IOException */ @Test public void urlTest() throws IOException { //URL相当于种子 URL url = new URL("https://pics4.baidu.com/feed/d009b3de9c82d1586f9efd2871ac9ad1bd3e42bf.jpeg?token=dd219671927c0cb92aebbd8808110a70"); //根据不同的协议强转不同的urlConnection,HttpsURLConnection或者HttpURLConnection HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); InputStream is = urlConnection.getInputStream(); //写到本地 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("pic.jpeg")); byte[] buff = new byte[1024]; int len; while ((len = is.read(buff)) != -1) { bos.write(buff, 0, len); } System.out.println("下载成功了!"); //关闭流 is.close(); bos.close(); urlConnection.disconnect(); } }
Java入门视频教程
618-630