java网络通信
一、tcp和udp的区别
1)tcp面向连接(connect,三次握手),udp无连接2) tcp保证可靠(要求对端确认),udp不提供可靠的实时传输
3)tcp提供流量控制(通告窗口),udp无流量控制
当然,udp实时,开销小
二、tcp深入
1.tcp客户端的流程:
socket()——>connect()——>write() or read() 完成交互——> close()
其中,connnect发起主动连接;客户端不需要绑定端口,需要绑定服务器端的ip,(udp里面是数据DatagramPaket自己封装ip和端口)系统会进行自动分配
2.tcp服务器端的流程:
socket()——>bind()——>listen()——>accept()——>read() or write()完成交互——>close()
其中,bind()绑定ip和端口;listen()则把一个未连接的套接口转换成一个被动套接口(创建一个套接口后,被假设为一个主动套接口);accept()等待接收;close()关闭套接口,具体的说,close操作只是使得相应套接字的引用计数减一,只有当计数减少为0的时候,才会触发tcp客户端向服务器发送终止连接请求(主要考虑多线程)。
3,TCP半关闭
如果是半关闭(主要因为tcp是全双工的),不使用close,而是利用shutdown(SHUR_WR)
int shutdown(int sockfd,int howto);其中howto可以为
SHUT_RD,不能接收数据,当前接收缓冲区的都被丢弃,但仍可写,可以发出数据
SHUT_WR,不能发出写数据,但仍可读,留在发送缓冲区的数据将被发送到。这成为半关闭。
SHUT_RDWR,同时关闭读和写。
另外,close只是令计数减1,而shutdown后其他进程将无法利用此套接字通信。
4,三次握手和四次握手
tcp建立连接:三次握手:
以客户端主动建立连接为例:
1)客户端发送一个SYN J [connect 函数]
2)服务器返回一个ACK J+1,发送一个SYN K [accept 函数,客户端的connect返回]
3)客户端发挥一个ACK K+1 [accept函数,服务器端的accept函数返回]
三次握手的原因:防止失效的连接请求报文突然又传送放到服务器。设想这么一种场景:客户端第一次发送的连接请求并没有丢失,而是网络问题导致延迟到达服务器,服务器以为是客户端又发起的新连接,于是同意,并向客户端发挥确认,而客户端不予理会,服务器就一直等待客户端发送请求,导致资源浪费。
tcp释放连接:四次握手:
1)一个进程调用close发送FIN,表示数据发送完毕。
2)另一端在收到FIN后,执行被动关闭,同时发回确认。
3)一段时间后,另一端调用close发送FIN
4)接收到FIN的原发送端,对它进行确认。
四次握手的原因:TCP是全双工的,必须保证每个方向的连接都被释放掉。
5,利用java语言实现TCP连接
java利用serverSocket代表服务器端的套接字,其构造函数中同时完成bind绑定
Socket代表客户端的套接字
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.bobo.interview; import java.io.IOException; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; public class MyServer { private final static int PORT=30000; /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { ServerSocket server=new ServerSocket(PORT); while(true){ Socket s=server.accept(); //如果有连接请求,上面的accept函数返回 PrintStream ps=new PrintStream(s.getOutputStream()); ps.println("您好,您收到了服务器的信息。。。。"); ps.close(); s.close(); } } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.bobo.interview; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; public class MyClient { private final static int PORT = 30000; private final static String ip = "127.0.0.1"; /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { Socket client = new Socket(ip, PORT); BufferedReader br = new BufferedReader(new InputStreamReader( client.getInputStream())); System.out.println(br.readLine()); br.close(); client.close(); } }
java里面使用shutdownInput或者shutdownOutput来实现半关闭
二、udp深入
udp没有TCP那么复杂的流程,java里面利用DatagramSocket达标udp的套接字,其本身只是码头,负责数据发送,不维护状态;
UDP严格的说,没有服务器端和客户端之分
通常创建充当服务器端的DatagramSocket的时候,需要指定端口,充当客户端则使用随机端口
DatagramSocket发送和接收数据采用的是DatagramPacket,代表数据报,数据报本身指定发送的ip和端口。
一下是udp通信的java实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.bobo.interview; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; public class UDPserver { // 服务器端需要指定端口 private static int PORT = 4096; // 指定接收数据的数据报 private static int DATA_LEN = 4096; private static byte[] inBuff = new byte[DATA_LEN]; private static DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length); // 指定发送数据的数据报 private static DatagramPacket outPacket; // 构造发送的数据 static String outData = "服务器端发送的数据"; /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { DatagramSocket udpServer = new DatagramSocket(PORT); // 利用循环接收数据 for (int i = 0; i < 1000; i++) { udpServer.receive(inPacket); System.out.println(inBuff == inPacket.getData()); System.out.println(new String(inPacket.getData())); byte[] sendData = outData.getBytes(); outPacket = new DatagramPacket(sendData, sendData.length, inPacket.getSocketAddress()); udpServer.send(outPacket); } } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.bobo.interview; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; public class UDPclient { // 服务器端需要指定端口 private static int PORT = 4096; private static String ip = "127.0.0.1"; // 指定接收数据的数据报 private static int DATA_LEN = 4096; private static byte[] inBuff = new byte[DATA_LEN]; private static DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length); // 指定发送数据的数据报 private static DatagramPacket outPacket; /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { DatagramSocket udpClient = new DatagramSocket(PORT); outPacket = new DatagramPacket("客户端发送的数据".getBytes(), "客户端发送的数据".getBytes().length, InetAddress.getByName(ip), PORT); udpClient.send(outPacket); udpClient.receive(inPacket); System.out.println(new String(inPacket.getData())); } }
java实现http通信的代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package wzh.Http; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.Map; public class HttpRequest { /** * 向指定URL发送GET方法的请求 * * @param url * 发送请求的URL * @param param * 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @return URL 所代表远程资源的响应结果 */ public static String sendGet(String url, String param) { String result = ""; BufferedReader in = null; try { String urlNameString = url + "?" + param; URL realUrl = new URL(urlNameString); // 打开和URL之间的连接 URLConnection connection = realUrl.openConnection(); // 设置通用的请求属性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 建立实际的连接 connection.connect(); // 获取所有响应头字段 Map<String, List<String>> map = connection.getHeaderFields(); // 遍历所有的响应头字段 for (String key : map.keySet()) { System.out.println(key + "--->" + map.get(key)); } // 定义 BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader( connection.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送GET请求出现异常!" + e); e.printStackTrace(); } // 使用finally块来关闭输入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; } /** * 向指定 URL 发送POST方法的请求 * * @param url * 发送请求的 URL * @param param * 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @return 所代表远程资源的响应结果 */ public static String sendPost(String url, String param) { PrintWriter out = null; BufferedReader in = null; String result = ""; try { URL realUrl = new URL(url); // 打开和URL之间的连接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送请求参数 out.print(param); // flush输出流的缓冲 out.flush(); // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader( new InputStreamReader(conn.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送 POST 请求出现异常!"+e); e.printStackTrace(); } //使用finally块来关闭输出流、输入流 finally{ try{ if(out!=null){ out.close(); } if(in!=null){ in.close(); } } catch(IOException ex){ ex.printStackTrace(); } } return result; } }