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三次握手建立过程
服务端向客户端发信息