[TOC]
1.概述
网络编程三要素:IP / 端口(Port) / 协议(protocol) 补充知识: 3G/4G 通过信号塔,有的信号塔连接的是卫星通信;
2.IP概述
3.端口号概述
类比于您在什么年级/什么班/位置号
常用端口:
* mysql: 3306
* oracle: 1521
* web: 80
* tomcat: 8080
* QQ: 4000
* feiQ: 2425
4.协议概述 为计算机网络中进行数据交换而建立的规则、标准或约定的集合。
Socket套接字概述:网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
Socket在应用程序中创建,通过一种绑定机制与驱动程序建立关系,告诉自己所对应的IP和port
。可以类比为码头和集装箱以及货运船只;
使用的类:
public class DatagramSocket extends Object implements Closeable #这类代表一个发送和接收数据包的插座。
public final class DatagramPacket extends Object #这类表示一个数据报包。
UDP-Socket构建发送流程: 1.发送Send
2.接收Receive
3.接收方获取ip和端口号
String ip = packet.getAddress().getHostAddress();
int port = packet.getPort();
实际案例1:
// 发送端1:
package com.weiyigeek.net;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class Demo1_UDPSend {
public static void main(String[] args) throws Exception {
// 1.网络编程之UDP协议 - socket 传输优化(发送端)
Scanner sc = new Scanner(System.in); //创建键盘录入对象
DatagramSocket udpsocket = new DatagramSocket(); //创建一个UDP-socket相当于是一个码头
while(true) {
String str = sc.nextLine(); //接收我们录入的字符串,如果等于quit则退出循环 相当于是货物
//创建Packet相当于集装箱
DatagramPacket pt = new DatagramPacket(str.getBytes(), str.getBytes().length,InetAddress.getByName("127.0.0.1"), 8888);
udpsocket.send(pt); //从socket通道将数据发送出去,从码头发送货物;
if(str.equals("quit")) //双方都停止通信
break;
}
udpsocket.close();
}
}
//接收端 - 接收端一般先启动
package com.weiyigeek.net;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Demo1_UDPReceive {
public static void main(String[] args) throws Exception {
//2.Socket 通信接收端
DatagramSocket udpsocket = new DatagramSocket(8888); //创建socket相当于接收货物的码头
DatagramPacket udppacket = new DatagramPacket(new byte[1024], 1024); //创建接收的集装箱
while(true) {
udpsocket.receive(udppacket); //接收货物实际就是数据
byte[] arr = udppacket.getData(); // 获取数据
int len = udppacket.getLength(); //获取有效的字节个数
String ip = udppacket.getAddress().getHostAddress(); //获取IP地址
int port = udppacket.getPort(); //获取端口号
System.out.println(ip+":" + port + " = " + new String(arr,0,len));
if(new String(arr,0,len).equals("quit")) {
break;
}
}
udpsocket.close();
}
}
执行结果:
whoami
Iloveyou
WeiyiGeek
quit #非常注意最有一个quit 夹杂有其他的不可见符号
WeiyiGeek.
实际案例2:多线程实现一个窗口接和发数据
package com.weiyigeek.net;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class Demo2_ReceSend {
public static void main(String[] args) {
// 多线程实现一个窗口了接和收信息
new receive().start();
new send().start();
}
}
//接收端线程
class receive extends Thread {
@Override
public void run() {
try {
DatagramSocket ss = new DatagramSocket(8888); //监听端口
DatagramPacket pt = new DatagramPacket(new byte[1024], 1024);
while(true)
{
ss.receive(pt);
byte[] arr = pt.getData();
int len = pt.getLength();
String ip = pt.getAddress().getHostAddress();
int port = pt.getPort();
String msg = new String(arr,0,len);
System.out.println(ip + ":" + port + "\n" + msg);
if(msg.equals("quit")) {
System.out.println("接收端停止-Recevice Stop");
break;
}
}
ss.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}
//发送端线程
class send extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
try {
DatagramSocket ss = new DatagramSocket();
while(true){
String msg = sc.nextLine();
DatagramPacket dp = new DatagramPacket(msg.getBytes(), msg.getBytes().length, InetAddress.getByName("127.0.0.1"), 8888);
ss.send(dp);
if(msg.equals("quit")) {
System.out.println("发送端停止-Send Stop");
break;
}
}
ss.close();
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
执行结果:
quit
发送端停止-Send Stop
127.0.0.1:51090
quit
接收端停止-Recevice Stop
1.客户端Client
读取服务端输出流写出的数据
写出数据到服务端的输入流
2.服务端Server
写出数据到客户端的输入流
读取客户端输出流写出的数据
CS对应表: 客户端 C | 服务端 S —|— getInputStream | getOutputStream getOutoutStream | getInputStream
实际案例:
//########## Client ############
package com.weiyigeek.net;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Demo3_TcpSend {
public static void main(String[] args) throws Exception {
//1.tcp Socket 客户端
Socket client = new Socket("127.0.0.1",9999); //创建Socket指定ip地址和端口号
InputStream is = client.getInputStream(); // 获取服务端发送的输入流信息
OutputStream os = client.getOutputStream(); //客户端向服务端发送信息
//先接送服务端信息
byte[] arr = new byte[1024];
int len = is.read(arr);
System.out.println(new String(arr,0,len));
//在向服务端发送信息
os.write("我是客户端发来得消息!".getBytes());
//关闭客户端Socket
client.close();
}
}
//############ server ############
package com.weiyigeek.net;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Demo3_TcpReiceve {
public static void main(String[] args) throws Exception {
//2.Server服务端TCP socket 案例
ServerSocket ss = new ServerSocket(9999); //服务端监听端口
Socket socket = ss.accept(); //接受客服端得请求
InputStream is = socket.getInputStream(); //获取客户端得输入流
OutputStream os = socket.getOutputStream(); //客户端得输出流
//向客户端发送信息
os.write("这是从服务端发送得信息".getBytes());
//打印出接收客户端发送得信息
byte[] arr = new byte[1024];
int len = is.read(arr);
System.out.println(new String(arr,0,len));
socket.close();
}
}
WeiyiGeek.
实际案例: TCP服务端多线程及其优化
//客户端 - Client
package com.weiyigeek.net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
public class Demo3_TcpSend {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8888); //创建客户端连接到服务端的Tcp-socket = 港口
//优化1:需要读的时候是字符串,写的时候也是字符串
//优化2:服务器端应该是多线程的
//这时候我们将一个字节流转换成为了字符流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //获取服务端发送的信息,读取默认是以\r\n为结束符号
//这里不建议使用BufferedWriter 一行一行写
PrintStream ps = new PrintStream(socket.getOutputStream()); //PrintStream 中有写入换行的方法并且以\r\n为结束符号;
//读取服务端的消息并且输出消息
System.out.println(br.readLine());
ps.println("2.我是客户端的反馈信息!");
System.out.println(br.readLine());
ps.println("4.客户端的通信结束!");
//关闭客户端的socket
socket.close();
}
}
//服务端
package com.weiyigeek.net;
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 Demo3_TcpReceive {
private static ServerSocket ss;
public static void main(String[] args) throws Exception {
ss = new ServerSocket(8888);
while(true){
final Socket socket = ss.accept(); // 循环的接收客户端的信息
new Thread() {
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //接收的字节流转换成为字符流
PrintStream ps = new PrintStream(socket.getOutputStream()); //向客服端发送信息的对象
ps.println("1.我是服务端发出的信息!"); //注意这里不能采用print否则会一直卡输入界面,这是由于没有\r\n,客户端不知道什么时候结束就一直在读
System.out.println(br.readLine());
ps.println("3.服务端请求客户端关闭通信");
System.out.println(br.readLine());
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
}
WeiyiGeek.
练习1: 客户端向服务器写字符串(键盘录入),服务器(多线程)将字符串反转后写回,客户端再次读取到是反转后的字符串;
客户端:
package com.weiyigeek.net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Demo4_Client {
public static void main(String[] args) throws Exception, IOException {
//需求:客户端向服务器写字符串(键盘录入),服务器(多线程)将字符串反转后写回,客户端再次读取到是反转后的字符串
//1.服务端创建socket (码头)
Socket socket = new Socket("127.0.0.1",8888); //创建客端端连接的服务端的IP:prot
Scanner sc = new Scanner(System.in); //创建键盘录入对象
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //获取输入流
PrintStream ps = new PrintStream(socket.getOutputStream()); //获取输出流
System.out.print("请输入您要发送的字符串:");
ps.println(sc.nextLine() ); // 向服务端发送信息
System.out.println("服务端翻转后的字符串:" + br.readLine());
socket.close(); //关闭socket - 实际开发中非常重要 br / ps 也随之而关闭
}
}
服务端:
package com.weiyigeek.net;
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 Demo4_Server {
public static void main(String[] args) throws Exception, IOException {
//创建 服务端 socket
ServerSocket ss = new ServerSocket(8888);
//不断的接收服务端的请求
while(true){
final Socket sc = ss.accept(); //接收到客端发送的信息
//启动服务端多线程
new Thread() {
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream())); //获取输入流
PrintStream ps = new PrintStream(sc.getOutputStream()); //获取输出流
StringBuffer sendMsg = new StringBuffer(br.readLine()).reverse(); //接收客户端的字符串并且进行翻转
System.out.println("服务端翻转客户端发送的字符串:" + sendMsg);
ps.println(sendMsg); //向客服端发送翻转轴的字符串
sc.close(); //关键接收客端端的socket
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
}
WeiyiGeek.
练习2: 客户端向服务器上传文件
客户端:
package com.weiyigeek.net;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Demo5_Client {
public static void main(String[] args) throws Exception, IOException {
//(1) 客户端流程
//1.录入上传文件的路径,并且验证该路径是否存在
File upFile = getFile();
//2.发送文件名称在服务端
Socket client = new Socket("127.0.0.1",9999);
BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream())); // 获取输入流
PrintStream ps = new PrintStream(client.getOutputStream()); //获取输出流
System.out.println("正在向服务端发送将要上传的文件名称:" + upFile.getName());
ps.println(upFile.getName());
//6.接收结果,判断服务端是否存在该文件
String res = br.readLine();
System.out.println(res);
if(res.equals("Found")) {
System.out.println("服务端已经存在该文件请不要重新上传,即将关闭Client Socket!");
client.close();
return;
}else {
System.out.println("服务端不存在该文件正在上传!");
}
//7.用字节流可以拷贝任何文件,使用FileInputStream读取文件并且写入到网络之中
FileInputStream fis = new FileInputStream(upFile);
byte[] arr = new byte[8192];
int len;
while((len = fis.read(arr)) != -1) {
//PrintStream 优点:可以写字符流也可以写字节流
//BufferedWriter : 只可以写字符流
ps.write(arr, 0, len);
}
//关闭IO文件和socket
fis.close();
client.close();
}
private static File getFile() {
Scanner sc = new Scanner(System.in); //创建录入路径的对象
System.out.print("请输入您要上传的文件路径: ");
while(true){
String line = sc.nextLine();
File path = new File(line);
if(!path.exists()) {
System.out.println("文件不存在请重新输入: ");
}else if (path.isDirectory()){
System.out.println("您输入的路径是目录不是文件请重新输入: ");
}else {
return path;
}
}
}
}
服务端:
package com.weiyigeek.net;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Demo5_Server {
public static void main(String[] args) throws Exception {
//(2) 服务端流程
//3.创建多线程
ServerSocket ss = new ServerSocket(9999);
System.out.println("服务器已启动绑定端口 : 9999 --- run");
//4.接收客服端的数据并读取文件名称
while(true) {
final Socket res = ss.accept(); //接收客服端发送的信息
//多线程实例
new Thread() {
public void run() {
try {
//需要抽取出来字节流
InputStream is = res.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is)); // 获取输入流(字符流)
PrintStream ps = new PrintStream(res.getOutputStream()); //获取输出流
String fileName = br.readLine(); //读取发送的文件名
//5.判断服务端里面文件是否存在将结果发送客户端
File dir = new File("update");
dir.mkdir();
File file = new File(dir,fileName); //封装成为File对象
if(file.exists()) {
ps.println("Found"); //服务端存在!
res.close();
}else {
ps.println("NotFound"); //服务端不存在!
}
//8.从网络中读取传输的文件数据,并且写入到本地之中
FileOutputStream fos = new FileOutputStream(file);
byte[] arr = new byte[8192];
int len;
while((len = is.read(arr)) != -1) {
fos.write(arr,0,len);
}
System.out.println(file.getName() +"文件上传成功!");
//关闭文件IO和SocketIO
fos.close();
res.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
}
WeiyiGeek.