1.socket
在进行网络编程前,我们需要了解socket。我们知道IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用层。
TCP/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP协议是应用层协议,主要解决如何包装数据。
那么socket是啥呢?
- 首先呢,socket就是网络通信的工具,任何一门语言都有socket,他不是任何一个语言的专有名词,而是大家通过自己的程序与其他电脑进行网络通信的时候都用它。
- 实际上socket是对TCP/IP协议的封装,它的出现只是使得程序员更方便地使用TCP/IP协议栈而已。socket本身并不是协议,它是应用层与TCP/IP协议族通信的中间软件抽象层,是一组调用接口(TCP/IP网络的API函数)。
socket非常类似于电话插座。以一个国家级电话网为例。电话的通话双方相当于相互通信的2个进程,区号是它的网络地址;区内一个单位的交换机相当于一台主机,主机分配给每个用户的局内号码相当于socket号。任何用户在通话之前,首先要占有一部电话机,相当于申请一个socket;同时要知道对方的号码,相当于对方有一个固定的socket。然后向对方拨号呼叫,相当于发出连接请求。对方假如在场并空闲,拿起电话话筒,双方就可以正式通话,相当于连接成功。双方通话的过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,一方挂起电话机相当于关闭socket,撤消连接。
socket在网路中的位置:
2.Socket的TCP和UDP通信
socket有两种建立通信的方式,一种是基于TCP的可靠传输,一种是基于UDP的不可靠传输
TCP
- 可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;文件传输程序。
- 面向连接的协议,在socket之间进行数据传输之前必须要建立连接,所以在TCP中需要连接时间
- TCP传输数据无大小限制,一旦建立连接,双方socket就可以按统一的格式传输大的数据(无限制)。
- TCP是一个可靠的协议,它确保接收方完全正确地获取发送方所发送的全部数据。
UDP
- 不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文(数据包),尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
- 每个数据包中都给出了完整的地址信息,因此无需建立发送方和接收方的连接。
- UDP传输数据时是有大小限制的,每个被传输的数据包必须限定在64KB之内
- UDP是一个不可靠的协议,发送方所发送的数据包并不一定以相同的顺序到达接收方。
socket中TCP和UDP对比:
3.Java中Socket方法
方法 | 说明 |
---|---|
ServerSocket(int port) | 创建ServerSocket |
bind(SocketAddress bindpoint) | 将套接字绑定到本地地址 |
accept() | 阻塞方法,也就是说调用accept方法后程序会停下来等待连接请求 |
close() | 关闭此套接字 |
connect(SocketAddress endpoint) | 将此套接字连接到服务器 |
InetAddress getInetAddress() | 返回套接字的连接地址 |
InetAddress getLocalAddress() | 获取套接字绑定的本地地址 |
InputStream getInputStream() | 返回此套接字的输入流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
SocketAddress getLocalSocketAddress() | 返回此套接字绑定的端点地址,如果尚未绑定则返回 null |
SocketAddress getRemoteSocketAddress() | 返回此套接字的连接的端点地址,如果尚未连接则返回 null |
int getLoacalPort() | 返回此套接字绑定的本地端口 |
int getPort() | 返回此套接字连接的远程端口 |
4.基于Java的socket网络编程
4.1代码结构
4.2基于TCP实现
服务端
- 创建一个服务器端socket套接字(套接字会在制定的端口上监听)
- 当有使用ServerSocket中的accept()获取客户端socket对象
- 使用多线程实现聊天:
- 1.MyClientThread线程负责接收客户端发送给服务器端的消息
- 2.MyServerThread线程负责向客户端发送消息
package socket.chat; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; /** * socket实现聊天 * 1.创建一个服务器端socket套接字(套接字会在制定的端口上监听); * 2.当有使用ServerSocket中的accept()获取客户端socket对象 * 3.使用多线程实现聊天:1.MyClientThread线程负责接收客户端发送给服务器端的消息; * 2.MyServerThread线程负责向客户端发送消息 */ class MyServerSocket{ public static void main(String[] args) { ServerSocket serverSocket = null;//服务器端socket Socket clientSocket = null;//客户端socket try { //创建一个服务器端socket服务 serverSocket = new ServerSocket(8888); while (true) {//使用while死循环模拟客户端一直启动 clientSocket = serverSocket.accept();//获取连接服务端的客户端socket //该线程用于接收客户端发送的消息,并将该消息打印到控制台 MyClientThread myClientThread = new MyClientThread(clientSocket); //该线程用于向客户端发送的消息 MyServerThread myServerThread = new MyServerThread(clientSocket); //启动线程 myClientThread.start(); myServerThread.start(); } } catch (IOException io) { io.printStackTrace(); } finally { if (clientSocket != null) { try { clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } }//main--end }//MyServerSocket--end /** * 接收客户端的内容 * 1.根据客户端Socket获取指向客户端Socket对象的输入流对象(输入源) * 2.通过输入流对象将客户端输入的信息读取到内存中 * 3.通过输出流对象(System.out.print)将内存中的数据打印到控制台 */ class MyClientThread extends Thread { private DataInputStream dataInputStream = null; public MyClientThread(Socket socket) { try { //获取客户端的输入流对象 this.dataInputStream = new DataInputStream(socket.getInputStream()); } catch (IOException io) { io.printStackTrace(); } }//MyClientThread--end @Override public void run() { String tellClient = null; try { while (true) { tellClient = this.dataInputStream.readUTF();//将客户端发送的信息写入到内存中 System.out.println("客户端说:"+tellClient);//将读取的客户端信息打印到控制台 } } catch (IOException io) { io.printStackTrace(); } }//run--end }//MyClientThread--end /** * 向发送客户端的消息 * 1.根据客户端Socket获取指向客户端Socket对象的输出流对象(输出目的地) * 2.获取控制台输入流对象(输入源) * 3.通过输入流对象将控制台输入的信息读取到内存中 * 4.通过输出流对象将内存中的数据返回给服务器端 */ class MyServerThread extends Thread { private DataOutputStream dataOutputStream = null;//用于输出服务器返回给客户端的信息 private Scanner in = null;//用于将服务器端在控制台输入的信息读取到内存中 public MyServerThread(Socket socket) { try { //根据客户端获取输出流对象 this.dataOutputStream = new DataOutputStream(socket.getOutputStream()); //获取控制台输入流对象 in = new Scanner(System.in); } catch (IOException e) { e.printStackTrace(); } }//MyServerThread--end @Override public void run() { String tellServer = null; while (true) { try { //将控制台中的信息读入到内存中 tellServer = in.nextLine(); //服务器端向客户端发送消息 this.dataOutputStream.writeUTF(tellServer); } catch (IOException e) { e.printStackTrace(); } } }//run }//server
客户端
- 根据IP和port获取和服务端连接的Socket对象
- 通过服务端Socket对象获取指向服务端Socket对象的输入流/输出流,获取服务器端发送的信息或者向服务器发送消息
package socket.chat; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.util.Scanner; /** * 客户端socket * 1.根据IP和port获取和服务端连接的Socket对象 * 2.通过服务端Socket对象获取指向服务端Socket对象的输入流/输出流,获取服务器端发送的信息或者向服务器发送消息 */ class MyClientSocket { public static void main(String[] args) { Socket serverSocket = null; try { serverSocket = new Socket("127.0.0.1", 8888); MyClientToSerThread myClientToSerThread = new MyClientToSerThread(serverSocket); MyAcceptServerThread myAcceptServerThread = new MyAcceptServerThread(serverSocket); myClientToSerThread.start(); myAcceptServerThread.start(); } catch (IOException io) { io.printStackTrace(); } }//main--end }//MyClientSocket--end /** * 向服务端发送消息 */ class MyClientToSerThread extends Thread { private DataOutputStream dataOutputStream = null; private Scanner in = null; public MyClientToSerThread(Socket socket) { try { this.dataOutputStream = new DataOutputStream(socket.getOutputStream()); this.in = new Scanner(System.in); } catch (IOException io) { io.printStackTrace(); } }//MyClientToSerThread--end @Override public void run() { String tell = null; while (true) { try { tell = in.nextLine(); dataOutputStream.writeUTF(tell); } catch (IOException e) { e.printStackTrace(); } } }//run--end }//MyClientToSerThread--end /** *接收客户端的信息 **/ class MyAcceptServerThread extends Thread { private DataInputStream dataInputStream = null; public MyAcceptServerThread(Socket socket) { try { this.dataInputStream = new DataInputStream(socket.getInputStream()); } catch (IOException e) { e.printStackTrace(); } }//MyAcceptServerThread--end @Override public void run() { String tell = null; while (true) { try { tell = this.dataInputStream.readUTF(); System.out.println("服务端:"+tell); } catch (IOException e) { e.printStackTrace(); } } }//run--end }//MyAcceptServerThread--end
4.3运行结果
5.抓包分析
使用wireshark抓取本地TCP包,选择回环网卡
TCP三次握手建立过程
服务端向客户端发信息