一、网络编程三个要素
1.IP地址:网络中设备(计算机)的唯一标识
Ipv4: 占4个字节,点分十进制表示法. 举例: 192.168.91.126
Ipv6: 占16个字节,冒分十六进制表示法,举例:fe80::4d1:d7cb:3ba7:490b%10
2.端口号:计算机中应用程序的唯一标识, 范围[0~65535]
3.通信协议:
UDP: 面向无连接、不可靠的协议、一个数据包大小在64KB以内
TCP: 面向有连接、可靠的协议,数据大小没有限制
二、UDP通信代码编写
1.发送端
1.发送端
//1)创建DatagramSocket对象
DatagramSocket socket = new DatagramSocket(); //会绑定一个默认的端口号,系统分配的
//2.创建一个DatagramPacket数据包,数据包装中需要封装数据、数据的长度,对方的ip,对方的贷款好
DatagramPacket packet = new DatagramPacket(
字节数组,
数组的长度,
对方的ip
对方的端口号
);
//3.发送数据包
socket.send(packet);
//4.关闭socket
socket.close();
2.接收端
//1)创建DatagramSocket对象
DatagramSocket socket = new DatagramSocket(接收端的端口号); //会绑定一个默认的端口号,系统分配的
//2.创建一个DatagramPacket空数据包
DatagramPacket packet = new DatagramPacket(
字节数组,
数组的长度
);
//3.接收数据包
socket.receive(packet);
//4.解析数据包(拆包)
byte[] data = packet.getData();
int length = packet.getLength();
//如果发送的数据是一个字符串,可以把data数组中有效的数据转换为字符串
String str = new String(data,0,length);
//5.关闭socket
socket.close();
三、TCP通信
1.客户端(Socket)
//1)创建Socket对象,指定服务端的ip地址和端口号
Socket socket = new Socket(服务端的ip地址, 服务端口号);
//2)通过socket可以获取一个输出流,用于往服务端发送数据
OutputStream os = socket.getOutputStream();
os.write(字节数据);
//3)读取服务端返回的数据
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1924];
int len;
while(len = is.read(bytes)){
String str = new String(data,0,len);
System.out.println(str);
}
//4)释放资源
socket.close();
2.服务端(ServerSocket)
//1)创建一个ServerSocket对象,注册一个服务端的端口
ServerSocket server = new ServerSocket(服务端口号);
//2)监听客户端
Socket socket = server.accept(); //接收客户端
//3)获取输入流,读取客户端发过来的消息
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1924];
int len;
while(len = is.read(bytes)!=-1){
String str = new String(data,0,len);
System.out.println(str);
}
//4)获取输出流,往客户端回写数据
OutputStream os = socket.getOutputStream();
os.write(字节数据);
//5)释放资源
socket.close();```
3.在上面的代码上做优化(实现多发多收)
客户端代码:
```java
// 1、创建一个Socket对象,请求与服务器的连接(TCP通信)
Socket socket = new Socket("127.0.0.1",10011);
// 2、从socket通信管道中得到一个字节输出流,负责写数据出去
OutputStream os = socket.getOutputStream();
//为了让流能够直接输出字符串,把OutputStream转换为Writer
OutputStreamWriter osw = new OutputStreamWriter(os);
//为了能够写一行,把OutputStreamWriter封装为BufferedWriter
BufferedWriter bw = new BufferedWriter(osw);
// 3、发送消息出去
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入你要发的数据:");
String msg = sc.next();
if(msg.equals("exit")){
System.out.println("客户端已经退出");
break;
}
bw.write(msg);
bw.newLine(); //换行
bw.flush(); //把数据尽快写到服务端(刷新)
}
// 4、释放资源
socket.close();
服务端代码:
System.out.println("服务端启动。。。");
// 1、注册端口
ServerSocket server = new ServerSocket(10011);
// 2、开始等待接收客户端的socket连接请求
Socket socket = server.accept();
// 3、从socket通信管道中得到一个字节输入流读取数据
InputStream in = socket.getInputStream();
//为了直接能够直接读取字符数据,把InputStream转换为Reader
InputStreamReader isr = new InputStreamReader(in);
//为了能够直接读取一行,在用BufferedReader进行封装
BufferedReader br = new BufferedReader(isr);
// 4、读取消息
//一次读取一行(循环读取)
String line;
while ((line = br.readLine())!=null){
System.out.println(socket.getRemoteSocketAddress()+
"发来了消息:"+line);
}
//释放资源
socket.close();
- 在上面的代码上加上多线程改进
服务端代码:
//第一步:写一个线程任务
public class ServerRunnable implements Runnable{
private Socket socket;
//通过ServerRunnable的的有参构造,把把客户端端传进来
public ServerRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//读取客户端Socket发送过来的数据
InputStream in = socket.getInputStream();
//把InputStream转换为Reader
InputStreamReader isr = new InputStreamReader(in);
//把InputStreamReader再次封装为BufferedRader,就可以一次读取一行了
BufferedReader br= new BufferedReader(isr);
String line;
while ((line = br.readLine())!=null){
System.out.println(socket.getRemoteSocketAddress()+"发过来的消息:"+line);
}
} catch (IOException e) {
System.out.println(socket.getRemoteSocketAddress()+"...异常下线了");
}
}
}
//第二步:服务端代码
public class ServerDemo2 {
public static void main(String[] args) throws Exception {
System.out.println("服务端启动。。。");
// 1、注册端口
ServerSocket server = new ServerSocket(10011);
while (true) {
// 2、开始等待接收客户端的socket连接请求
Socket socket = server.accept();
System.out.println(socket.getRemoteSocketAddress()+"...上线了");
//来了一个客户端socket,我就为客户端单独开一条线程来执行任务
ServerRunnable sr = new ServerRunnable(socket);
new Thread(sr).start();
}
}
}
5.在上面的案例基础上,再次使用线程池改进
public class ServerDemo2 {
//定义一个线程池
private static ThreadPoolExecutor pool = new ThreadPoolExecutor(
5, //核心线程数
10, //最大线程数
3, //临时线程空闲的时间
TimeUnit.SECONDS, //时间的单位
new ArrayBlockingQueue<>(10), //任务队列
Executors.defaultThreadFactory(), //线程工程(用来造线程的)
new ThreadPoolExecutor.AbortPolicy() //拒绝策略(有多余的任务拒绝,并且抛出异常)
);
public static void main(String[] args) throws Exception {
System.out.println("服务端启动。。。");
// 1、注册端口
ServerSocket server = new ServerSocket(10011);
while (true) {
// 2、开始等待接收客户端的socket连接请求
Socket socket = server.accept();
System.out.println(socket.getRemoteSocketAddress()+"...上线了");
//来了一个客户端socket,我就为客户端单独开一条线程来执行任务
ServerRunnable sr = new ServerRunnable(socket);
pool.submit(sr);
}
}
}```
6.在上个案例的基础上,进行消息转发,把消息转发给其他的客户端
修改了线程任务的代码:
```java
public class ServerRunnable implements Runnable{
private Socket socket;
public ServerRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//读取客户端Socket发送过来的数据
InputStream in = socket.getInputStream();
//把InputStream转换为Reader
InputStreamReader isr = new InputStreamReader(in);
//把InputStreamReader再次封装为BufferedRader,就可以一次读取一行了
BufferedReader br= new BufferedReader(isr);
String line;
while ((line = br.readLine())!=null){
System.out.println(socket.getRemoteSocketAddress()+"发过来的消息:"+line);
//把消息转发发给其他的客户端
sendMsgToAllSocket(line);
}
} catch (IOException e) {
System.out.println(socket.getRemoteSocketAddress()+"...异常下线了");
}
}
private void sendMsgToAllSocket(String line) throws IOException {
//获取所有的客户端对象(Socket对象)
ArrayList<Socket> list = ServerDemo2.list;
for (Socket socket1 : list) {
if(this.socket!=socket1){
//给其他的客户端回写数据
OutputStream in = socket1.getOutputStream();
//把OutputStream转换为Writer
OutputStreamWriter osw = new OutputStreamWriter(in);
//为了写一行,再把OutputStreamWriter封装为BufferedWriter
BufferedWriter bw = new BufferedWriter(osw);
bw.write(line);
bw.newLine(); //换行
bw.flush(); //将数据及时刷新到客户端
}
}
}
}
修改客户端代码:单独开一条线程用来读取服务端转发的数据
//为了让客户端在发送数据的同时,也能接收服务端转发过来消息。
//需要写一个子线程专门读取服务返回的数据
new Thread(new Runnable() {
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
InputStreamReader isr =new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine())!=null){
System.out.println(socket.getRemoteSocketAddress()+"发来消息:...."+line);
}
// 4、释放资源
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();