1、什么是网络编程
使用IP地址或域名和端口连接到另一台计算机上对应的程序,按照规定的协议(数据格式)来交换数据。
网络模型图:
2、TCP与UDP在概念上的区别
UDP:
- 面向无连接, 将数据封装成数据包中
- 每个数据报的大小在限制64k内
- 因无连接,是不可靠协议
- 不需要建立连接,速度快
TCP:
- 建立连接,形成传输数据的通道.
- 在连接中以字节流方式进行大数据量传输
- 通过三次握手完成连接,是可靠协议
- 必须建立连接,效率会稍低
3、UDP通讯案例
通过UDP协议实现客户端与服务器端进行传输
package com.zhang.socket;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
//socket服务器端
public class UdpSocketServer {
public static void main(String[] args) throws IOException {
System.out.println("udp服务器端启动连接....");
DatagramSocket datagramSocket=new DatagramSocket(8099);
byte[] bytes=new byte[1024];
DatagramPacket datagramPacket=new DatagramPacket(bytes,bytes.length);
// 阻塞,等待接受客户端发送请求
datagramSocket.receive(datagramPacket);
System.out.println("来源:"+datagramPacket.getAddress()+",端口号:"+datagramPacket.getPort());
// 获取客户端请求内容
String string=new String(datagramPacket.getData(),0,datagramPacket.getLength());
System.out.println(string);
datagramSocket.close();
} }
package com.zhang.socket;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//客户端
public class UdpClient {
public static void main(String[] args) throws IOException {
System.out.println("udp客户端启动连接....");
DatagramSocket ds = new DatagramSocket();
String str="哈哈哈";
byte[] bytes= str.getBytes();
DatagramPacket dp= new DatagramPacket(bytes, bytes.length, InetAddress.getByName("127.0.0.1"),8099);
ds.send(dp);
ds.close();
}
}
4、TCP三次握手协议
第一次握手:建立连接时,客户端发送SYN包(SYN=J)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到SYN包,必须确认客户的SYN(ACK=J+1),同时自己也发送一个SYN包(SYN=K),即SYN+ACK包,此时服务器V状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ACK=K+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手,客户端与服务器开始传送数据。
5、TCP四次分手协议
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
- 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
- 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
- 服务器B关闭与客户端A的连接,发送一个FIN给客户端A。
- 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。
6、为什么建立连接协议是三次握手,而关闭连接却是四次握手呢
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以 未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报 文和FIN报文多数情况下都是分开发送的。
7、为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态
这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。
8、TCP通讯案例
package com.zhang.socket;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
//服务端
class TcpServer {
public static void main(String[] args) throws IOException {
System.out.println("socket tcp服务器端启动....");
ServerSocket serverSocket = new ServerSocket(8099);
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
String string = new String(bytes, 0, len);
System.out.println(string);
serverSocket.close();
}
}
package com.zhang.socket;
import java.io.OutputStream;
import java.net.Socket;
//客户端
public class TcpClient {
public static void main(String[] args) throws Exception {
System.out.println("socket tcp 客户端启动....");
Socket socket = new Socket("127.0.0.1", 8099);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("哈哈哈".getBytes());
socket.close();
}
}
9、使用多线程支持多个请求
服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善
package com.zhang.tcp;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
//tcp服务器端...
class TcpServer {
public static void main(String[] args) throws Exception {
System.out.println("socket tcp服务器端启动....");
ServerSocket serverSocket = new ServerSocket(8099);
try {
while (true) {
final Socket socket = serverSocket.accept();
new Thread(new Runnable() {
public void run() {
try {
InputStream stream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = stream.read(bytes);
String string = new String(bytes, 0, len);
System.out.println("接收到的数据:"+string);
} catch (Exception e) {
e.fillInStackTrace();
}
}
}).start();
}
} catch (Exception e) {
e.fillInStackTrace();
} finally {
serverSocket.close();
}
}
}
package com.zhang.tcp;
import java.io.OutputStream;
import java.net.Socket;
//客户端
public class TcpClient {
public static void main(String[] args) throws Exception {
System.out.println("socket tcp 客户端启动....");
Socket socket = new Socket("127.0.0.1", 8099);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("哈哈哈hhhhh".getBytes());
socket.close();
}
}
利用线程池
package com.zhang.tcp;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//tcp服务器端...
class TcpServer2 {
public static void main(String[] args) throws Exception {
System.out.println("socket tcp服务器端启动....");
ExecutorService newCachedThreadPool=Executors.newCachedThreadPool();
ServerSocket serverSocket = new ServerSocket(8099);
try {
while (true) {
final Socket socket = serverSocket.accept();
newCachedThreadPool.execute(new Runnable() {
public void run() {
try {
InputStream stream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = stream.read(bytes);
String string = new String(bytes, 0, len);
System.out.println("接收到的数据:"+string);
} catch (Exception e) {
e.fillInStackTrace();
}
}
});
}
} catch (Exception e) {
e.fillInStackTrace();
} finally {
serverSocket.close();
}
}
}
10、TCP为什么是三次握手,为什么不是两次或者四次
首先,我们要知道TCP是全双工的,即客户端在给服务器端发送信息的同时,服务器端也可以给客户端发送信息。而半双工的意思是A可以给B发,B也可以给A发,但是A在给B发的时候B不能给A发,即不能同时。 单工为只能A给B发,B不能给A发; 或者是只能B给A发,不能A给B发。
我们假设A和B是通信的双方。我理解的握手实际上就是通信,发一次信息就是进行一次握手。
- 第一次握手: A给B打电话说,你可以听到我说话吗
- 第二次握手: B收到了A的信息,然后对A说: 我可以听得到你说话啊,你能听得到我说话吗
- 第三次握手: A收到了B的信息,然后说可以的,我要给你发信息啦
在三次握手之后,A和B都能确定这么一件事: 我说的话,你能听到; 你说的话,我也能听到。 这样,就可以开始正常通信了。
如果两次,那么B无法确定B的信息A是否能收到,所以如果B先说话,可能后面的A都收不到,会出现问题 。
如果四次,那么就造成了浪费,因为在三次结束之后,就已经可以保证A可以给B发信息,A可以收到B的信息; B可以给A发信息,B可以收到A的信息。