《网络编程》
目录
一、网络协议
网络协议指的是计算机网络中互相通信的对等实体之间交换信息时所必须遵守的规则的集合。有很多种,具体选择哪一种协议则要看情况而定。Internet上的计算机使用的是TCP/IP协议。
TCP/IP协议
TCP/IP是由一组具有专业用途的多个子协议组合而成的,这些子协议包括TCP、IP、UDP、ARP、ICMP等。TCP/IP凭借其实现成本低、在多平台间通信安全可靠以及可路由性等优势迅速发展,并成为Internet中的标准协议。在上世纪90年代,TCP/IP已经成为局域网中的首选协议,在最新的操作系统(如Windows、Windows Server等)中已经将TCP/IP作为其默认安装的通信协议。
IP地址
在Internet上有千百万台主机,为了区分这些主机,人们给每台主机都分配了一个专门的地址,称为IP地址(32 位数字,四个用点号分隔的数字)。
查看本机ip:命令提示符——>输入指令ipconfig——>回车执行
DNS(Domain Name System)域名系统
IP地址都是数字,太难记,DNS 将 IP 地址映射至字符串,映射由域名服务器系统维护(如www.baidu.com)。
端口
端口是英文port的意译,可以认为是设备与外界通讯交流的出口。端口可分为虚拟端口和物理端口,其中物理端口又称为接口,是可见端口(如USB接口);虚拟端口指计算机内部或交换机路由器内的端口,不可见。
常用协议 | 默认端口 |
---|---|
超文本传输协议http | 80 |
文件传输协议ftp | 21 |
Telnet协议 | 23 |
简单邮件传输协议smtp | 25 |
二、网络通信
Socket是什么?
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
TCP与UDP
TCP:是一种基于流的网络通讯方法,相对UDP传输速度慢,但通信更可靠;
UPD:传输速度快,但容易丢包(即发出的信息接收方不一定收到,发出信息的顺序与接收的顺序不一定相同)。
TCP三次握手通信建立原理
- 客户端——>服务器:是否可连接?
- 服务器——>客户端:可连接,是否连接成功?
- 客户端——>服务器:连接成功!
通信过程
- 客户端的输入流是服务器的输出流;
- 客户端的输出流是服务器的输入流。
三、Socket编程
Java套接字
ServerSocket(服务器套接字)
- ServerSocket(int port)
- ServerSocket(int port, int backlog)
- ServerSocket(int port, int backlog, InetAddress bindAddr)
Socket(客户端套接字)
- Socket(InetAddress address, int port)
- Socket(String host, int port)
案例(C/S架构)
(一)服务器界面初始化
- 界面UI
- 代码实现
...
public class Server extends JFrame implements ActionListener {
JLabel jlb1, jlb2;
JTextField jtf1, jtf2, jtf3;
JButton jbtn1, jbtn2;
JScrollPane jsp;
JTextArea jta;
public Server() {
setTitle("服务器");
setSize(400, 600);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
setLocationRelativeTo(null);
setLayout(null);
//1.ip
jlb1 = new JLabel("ip");
jlb1.setFont(new Font("微软雅黑", Font.BOLD, 16));
jlb1.setBounds(20, 20, 20, 30);
jtf1 = new JTextField();
jtf1.setEditable(false);
try {
//设置本机ip
jtf1.setText(InetAddress.getLocalHost().getHostAddress());
} catch (UnknownHostException e) {
e.printStackTrace();
}
jtf1.setBounds(50, 20, 150, 30);
//2.端口
jlb2 = new JLabel("port");
jlb2.setFont(new Font("微软雅黑", Font.BOLD, 16));
jlb2.setBounds(230, 20, 40, 30);
jtf2 = new JTextField();
jtf2.setBounds(280, 20, 100, 30);
//3.启动按钮
jbtn1 = new JButton("启动服务器");
jbtn1.addActionListener(this);
jbtn1.setBounds(20, 60, 360, 30);
//4.聊天界面
jta = new JTextArea();
jta.setEditable(false);
jsp = new JScrollPane(jta);
jsp.setBounds(20, 100, 360, 400);
//5.发送框
jtf3 = new JTextField();
jtf3.setEditable(false);
jtf3.setBounds(20, 510, 290, 30);
//6.发送按钮
jbtn2 = new JButton("发送");
jbtn2.addActionListener(this);
jbtn2.setEnabled(false);
jbtn2.setBounds(320, 510, 60, 30);
add(jlb1);
add(jtf1);
add(jlb2);
add(jtf2);
add(jbtn1);
add(jsp);
add(jtf3);
add(jbtn2);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
}
public static void main(String[] args) {
new Server();
}
}
(二)客户端界面初始化
- 界面UI
- 代码实现
...
public class Client extends JFrame implements ActionListener {
JLabel jlb1, jlb2;
JTextField jtf1, jtf2, jtf3;
JButton jbtn1, jbtn2;
JScrollPane jsp;
JTextArea jta;
public Client() {
setTitle("客户端");
setSize(400, 600);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
setLocationRelativeTo(null);
setLayout(null);
//1.ip
jlb1 = new JLabel("ip");
jlb1.setFont(new Font("微软雅黑", Font.BOLD, 16));
jlb1.setBounds(20, 20, 20, 30);
jtf1 = new JTextField();
jtf1.setBounds(50, 20, 150, 30);
//2.端口
jlb2 = new JLabel("port");
jlb2.setFont(new Font("微软雅黑", Font.BOLD, 16));
jlb2.setBounds(230, 20, 40, 30);
jtf2 = new JTextField();
jtf2.setBounds(280, 20, 100, 30);
//3.启动按钮
jbtn1 = new JButton("连接服务器");
jbtn1.addActionListener(this);
jbtn1.setBounds(20, 60, 360, 30);
//4.聊天界面
jta = new JTextArea();
jta.setEditable(false);
jsp = new JScrollPane(jta);
jsp.setBounds(20, 100, 360, 400);
//5.发送框
jtf3 = new JTextField();
jtf3.setEditable(false);
jtf3.setBounds(20, 510, 290, 30);
//6.发送按钮
jbtn2 = new JButton("发送");
jbtn2.addActionListener(this);
jbtn2.setEnabled(false);
jbtn2.setBounds(320, 510, 60, 30);
add(jlb1);
add(jtf1);
add(jlb2);
add(jtf2);
add(jbtn1);
add(jsp);
add(jtf3);
add(jbtn2);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
}
public static void main(String[] args) {
new Client();
}
}
(三)服务器事件
- Server端全局变量添加
...
public class Server extends JFrame implements ActionListener {
...
//套接字及对象输入输出流
private ServerSocket ss;
private Socket socket;
private ObjectInputStream ois;
private ObjectOutputStream oos;
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
...
}
- 事件处理
...
public class Server extends JFrame implements ActionListener {
...
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("启动服务器")) {
try {
//1.获取端口
String port = jtf2.getText();
//2.启动服务器套接字
ss = new ServerSocket(Integer.parseInt(port));
//3.开启接受模式(此处进入等待状态,直至客户端连接)
client = ss.accept();
oos = new ObjectOutputStream(client.getOutputStream());
//三步握手第二步
oos.writeObject("连接成功!");
ois = new ObjectInputStream(client.getInputStream());
//4.界面变化
jbtn1.setText("服务器运行中...");
jbtn1.setEnabled(false);
jtf3.setEditable(true);
jbtn2.setEnabled(true);
//5.开启会话子线程,不断接受来自客户端的消息
new Thread(() -> {
while (true) {
try {
Object o = ois.readObject();
jta.append(sdf.format(new Date()) + "\n【客户端】:" + o + "\n\n");
} catch (IOException | ClassNotFoundException ioException) {
//发生异常就停止子线程
break;
}
}
}).start();
} catch (IOException unknownHostException) {
unknownHostException.printStackTrace();
}
} else if (e.getActionCommand().equals("发送")) {
//1.判断内容是否为空
String text = jtf3.getText();
if (text.matches("\\s+")) {
JOptionPane.showMessageDialog(null, "发送内容不可为空!");
return;
}
//2.当前界面消息刷新
jta.append(sdf.format(new Date()) + "\n" + text + "\n\n");
//3.发送消息给客户端
try {
oos.writeObject(text);
} catch (IOException ioException) {
ioException.printStackTrace();
}
//4.清空发送框
jtf3.setText("");
}
}
...
}
(四)客户端事件
- Client端全局变量添加
...
public class Client extends JFrame implements ActionListener {
...
//客户端套接字及对象输入输出流
private Socket client;
private ObjectInputStream ois;
private ObjectOutputStream oos;
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
...
}
- 事件处理
...
public class Client extends JFrame implements ActionListener {
...
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("连接服务器")) {
try {
//1.获取ip及端口
String ip = jtf1.getText();
String port = jtf2.getText();
//2.启动客户端套接字(三步握手第一步)
client = new Socket(ip, Integer.parseInt(port));
ois = new ObjectInputStream(client.getInputStream());
oos = new ObjectOutputStream(client.getOutputStream());
//三步握手第三步
oos.writeObject("连接成功!");
//3.界面变化
jbtn1.setText("连接服务器成功!");
jbtn1.setEnabled(false);
jtf3.setEditable(true);
jbtn2.setEnabled(true);
//4.开启会话子线程,不断接受来自服务器的消息
new Thread(() -> {
while (true) {
try {
Object o = ois.readObject();
jta.append(sdf.format(new Date()) + "\n【服务器】:" + o + "\n\n");
} catch (IOException | ClassNotFoundException ioException) {
//发生异常就停止子线程
break;
}
}
}).start();
} catch (IOException unknownHostException) {
unknownHostException.printStackTrace();
}
} else if (e.getActionCommand().equals("发送")) {
//1.判断内容是否为空
String text = jtf3.getText();
if (text.matches("\\s+")) {
JOptionPane.showMessageDialog(null, "发送内容不可为空!");
return;
}
//2.当前界面消息刷新
jta.append(sdf.format(new Date()) + "\n" + text + "\n\n");
//3.发送消息给服务器
try {
oos.writeObject(text);
} catch (IOException ioException) {
ioException.printStackTrace();
}
//4.清空发送框
jtf3.setText("");
}
}
...
}
(五)启动测试
- 开启服务器端口10086并启动;
- 开启客户端输入ip及端口进行连接;
执行结果
(六)消息测试
- 服务器——>客户端:你好
- 客户端——>服务器:我好
执行结果
总结
重点
- 网络协议相关概念;
- TCP协议及三步握手原理;
- Socket编程。
难点
- TCP协议及三步握手原理;
- Socket编程。