c——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
网络编程
什么是网络编程?
用Java语言实现计算机间数据的信息传递和资源共享
网络编程的三要素
- IP地址:网络中计算机的唯一标识
a:点分十进制 xxx.xxx.xxx.xxx
b:IP地址的组成 网络号段+主机号段
c:InetAddress类 是个没有构造方法的类 用于获取ip等 - 端口
是应用程序的标识。范围:0-65535。其中0-1024不用。 - 协议
UDP:数据打包,每一次不超过64k,不建立连接,效率高,不可靠
TCP:建立数据通道,无限制,效率稍低,可靠
Socket机制
- 通信两端都应该有Socket对象
- 所有的通信都是通过Socket间的IO进行操作的. 网络通信其实就是Socket通信
Socket套接字:网络上具有唯一标识的IP地址和端口号组合在一起构成唯一能识别的标识符套接字
UDP协议发送和接收数据
发送:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* Created by mo on 15/11/21.
* UDP发送数据
* 创建Socket对象
* 创建数据包
* 发送数据包
* 释放资源
*/
public class UDPsend {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket = new DatagramSocket();
byte[] arr = "Hello,UDP,我来了".getBytes();
// InetAddress address = InetAddress.getByName("192.168.0.100");//接收端ip
// int port = 10765;//接收端端口
// DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length,address,port);
//精简版
DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length,InetAddress.getByName("192.168.0.100"),10765);
datagramSocket.send(datagramPacket);
datagramSocket.close();
}
}
接收:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* Created by mo on 15/11/21.
* UDP接收数据
*
* 创建接收端Socket对象
* 创建数据包(接收数据)
* 调用Socket对象的接收方法
* 解包并显示在控制台
* 释放资源
*/
public class UDPreceive {
public static void main(String[] args)throws IOException{
//接收数据要指定端口
DatagramSocket datagramSocket = new DatagramSocket(10765);
//创建数据包接收
byte[] arr = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length);
//接收数据
datagramSocket.receive(datagramPacket);
//解析数据内容
// int len = datagramPacket.getLength(); //获取数据实际长度
// byte[] received = datagramPacket.getData();//获取数据缓冲区
String s = new String(datagramPacket.getData(),0,datagramPacket.getLength());
//获取对方ip
String ip = datagramPacket.getAddress().getHostAddress();
System.out.println(ip+":"+s);
datagramSocket.close();
}
}
案例1:键盘录入数据 直到录入886 客户端结束输入数据
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* Created by mo on 15/11/21.
* 需求:键盘录入数据 直到录入886 客户端结束输入数据
*/
public class Send {
public static void main(String[] args)throws IOException {
//这是发送端
System.out.println("请输入要发送的数据");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line ;
//创建UDP用的DatagramSocket对象
DatagramSocket datagramSocket = new DatagramSocket();
//读取键盘录入的数据
while ((line = br.readLine())!=null){
if (line.equals("886")){
break;//如果是886则退出循环
}
//不是886,则创建数据包并把数据和接受者的端口+ip封装
byte[] arr = line.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length, InetAddress.getByName("192.168.0.100"),10765);
//发送数据包
datagramSocket.send(datagramPacket);
}
datagramSocket.close();
}
}
/**
* Created by mo on 15/11/21.
* 这是接收端
*/
public class Received {
public static void main(String[] args) throws IOException{
//创建接收端DatagramSocket对象,传入端口号
DatagramSocket datagramSocket = new DatagramSocket(10765);
//循环接收数据
while (true){
byte[] arr = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length);
datagramSocket.receive(datagramPacket);
String s = new String(datagramPacket.getData(),0,datagramPacket.getLength());
String ip = datagramPacket.getAddress().getHostAddress();
System.out.println(ip+":"+s);
}
//接收端应该一直开着 等待接收数据 所以不close
}
}
案例2:多线程实现接收发送在同一窗口
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
* Created by mo on 15/11/21.
* 多线程实现接收发送在同一窗口
*/
public class SendAndReceived {
public static void main(String[] args) throws SocketException {
//创建接收和发送的Socket对象
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receiveSocket = new DatagramSocket(10765);
//创建和启动线程
Thread send = new Thread(new SendThread(sendSocket));
Thread receive = new Thread(new ReceiveThread(receiveSocket));
send.start();
receive.start();
}
}
/**
* 这是发送线程类
*/
class SendThread implements Runnable{
//构造方法接收socket对象
private DatagramSocket datagramSocket;
public SendThread(DatagramSocket datagramSocket) {
this.datagramSocket = datagramSocket;
}
@Override
public void run() {
//键盘录入数据
System.out.println("请输入要发送的数据");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line ;
try {
while ((line = br.readLine())!=null){
//如果输入的是886则退出循环
if (line.equals("886")){
break;
}
//将输入的数据转为字节数组,然后封装到数据包并发送
byte[] arr = line.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length, InetAddress.getByName("192.168.0.100"),10765);
datagramSocket.send(datagramPacket);
}
} catch (IOException e) {
e.printStackTrace();
}
datagramSocket.close();
}
}
/**
* 这是接收线程类
*/
class ReceiveThread implements Runnable{
private DatagramSocket datagramSocket;
public ReceiveThread(DatagramSocket datagramSocket) {
this.datagramSocket = datagramSocket;
}
@Override
public void run() {
while (true){
byte[] arr = new byte[1024];
//创建接收用的数据包
DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length);
//接收数据包
try {
datagramSocket.receive(datagramPacket);
} catch (IOException e) {
e.printStackTrace();
}
//解析数据包
String s = new String(datagramPacket.getData(),0,datagramPacket.getLength());
//获取发送者的ip
String ip = datagramPacket.getAddress().getHostAddress();
System.out.println(ip+":"+s);
}
}
}
TCP协议发送和接收数据
发送:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* Created by mo on 15/11/21.
*
* TCP发送数据
* 创建发送端的Socket
* 如果创建成功 就说明连接已建立
* 获取输出流写数据
* 释放资源
*/
public class ClientDemo {
public static void main(String[] args) throws IOException{
// Socket socket = new Socket(InetAddress.getByName("192.168.0.100",10888)); //太复杂了,简化一下
Socket socket = new Socket("192.168.0.100",10888);
OutputStream os = socket.getOutputStream();
os.write("hello,TCP,我来了".getBytes());
//客户端接收服务器端的反馈
InputStream is = socket.getInputStream();
byte[] arr = new byte[1024];
int len = is.read(arr);
String s = new String(arr,0,len);
System.out.println(s);
//关闭Socket
socket.close();
}
}
接收:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Created by mo on 15/11/21.
*
* TCP接收数据
* 创建接收端的ServerSocket
* 监听客户端,返回一个对应的Socket对象
* 获取输入流,读取数据并显示
* 释放资源
*/
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10888);
//监听,并接收到此套接字的连接,此方法在连接传入前一直阻塞
Socket s = ss.accept();
//获取数据
InputStream is = s.getInputStream();
byte[] arr = new byte[1024];
int len = is.read(arr);//阻塞式方法
String str = new String(arr, 0, len);
System.out.println(s.getInetAddress().getHostAddress() + ":" + str);
//服务器向客户端发送反馈,上面的read是阻塞式,所以不用担心上面还没执行完就执行下面
OutputStream os = s.getOutputStream();
os.write("服务器已收到信息".getBytes());
//关闭客户端的Socket
s.close();
// ss.close();//服务器要保持开启 不能关
}
}
案例1:客户端键盘录入,服务器输出到控制台
客户端
import java.io.*;
import java.net.Socket;
/**
* Created by mo on 15/11/21.
*/
public class Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("192.168.0.100", 10588);
//创建线程用于接收服务端发来的反馈
Thread rec = new Thread(new Receive(socket));
rec.start();
//键盘录入
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
//把通道内的流给包装一下.因为Socket走的是字节流OutputStream
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String line;
while ((line = bf.readLine()) != null) {
if (line.equals("886")){
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
//这里也可以采用字节流,但要保证发送端接收端都用的字节流(具体代码为以下注释部分)
// BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
// OutputStream bw = socket.getOutputStream();
//
// String line;
//
// while ((line = bf.readLine()) != null) {
// if (line.equals("886")){
// break;
// }
// bw.write(line.getBytes());
//
// }
//关闭Socket
rec.interrupt();
socket.close();
}
}
/**
* 这是用于接收服务器端发来的反馈的线程类
*/
class Receive implements Runnable{
//构造方法接收Socket对象
private Socket socket ;
public Receive(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
while (true){
//判断Socket连接是否被关闭
if (socket.isClosed()){
break;
}
//连接还存在就开始等待接收反馈
InputStream is;
byte[] arr = new byte[1024];
int len;
try {
is = socket.getInputStream();
len = is.read(arr);//阻塞式方法
System.out.println(new String(arr,0,len));
} catch (IOException e) {
System.out.println("已退出聊天室");//输入886退出后捕获并处理异常
}
}
}
}
服务端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Created by mo on 15/11/21.
* 客户端键盘录入,服务器输出到控制台
*/
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10588);
//在循环里监听并建立Socket连接
while (true){
Socket s = ss.accept();
//把通道内的流给包装一下.因为Socket走的是字节流
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line ;
//IO流获取数据并给予反馈
while ((line = br.readLine()) != null){
System.out.println(s.getInetAddress().getHostAddress()+":"+line);
OutputStream os = s.getOutputStream();
os.write("服务器已接收到信息".getBytes());
}
s.close();
}
//这里也可以采用字节流,但要保证发送端接收端都用的字节流(具体代码为以下注释部分)
// while (true){
// Socket s = ss.accept();
// InputStream br=s.getInputStream();
//
// int line ;
// byte[] arr = new byte[1024];
// while ((line = br.read(arr)) != -1){
// System.out.println(s.getInetAddress().getHostAddress()+":"+new String(arr,0,line));
// OutputStream os = s.getOutputStream();
// os.write("服务器已接收到信息".getBytes());
// }
//
// s.close();
// }
}
}
案例2:多个客户端上传文件到服务器(多线程解决)
服务器端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Created by mo on 15/11/21.
*
* 只用while(true)包起来的缺点:都以一个名称储存 前几个客户端传的被覆盖 只剩最后一个
*
* 所以要用多线程
*/
public class Server {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket serverSocket = new ServerSocket(25255);
//因为不知道有多少个客户端发送数据,所以这里用while循环监听并为每一个建立连接的客户端启动一个新线程
//接收文件的代码放在线程里处理
while (true) {
Socket s = serverSocket.accept();
Thread thread = new Thread(new serverThread(s));
thread.start();
}
}
}
/**
* 这是服务端接收文件的线程类
*/
class serverThread implements Runnable {
private Socket s;
public serverThread(Socket s) {
this.s = s;
}
@Override
public void run() {
byte[] arr = new byte[4096];
int len;
try {
BufferedInputStream br = new BufferedInputStream(s.getInputStream());
//这里 多个线程 会覆盖一个文件 所以要改进
// BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("account.txt"));
//改进方法:因为目前无法获取到上传的文件名,所以只能用当前时间毫秒值来命名
String newName = System.currentTimeMillis()+".txt";
BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream(newName));
while ((len = br.read(arr)) != -1) {
bw.write(arr, 0, len);
//重点!这里非常有必要flush()一下,不然会导致,最后一次没有接收,丢失最后一部分
bw.flush();
}
//保存完毕后给予客户端反馈
OutputStream sd = s.getOutputStream();
sd.write("文件上传完毕".getBytes());
bw.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端(每个客户端都一样,这里只放一个客户端的代码)
import java.io.*;
import java.net.Socket;
/**
* Created by mo on 15/11/21.
*
* 多个客户端 1个服务器端
*
* 客户端不用修改 只修改服务器端
*/
public class Client1 {
public static void main(String[] args)throws IOException {
//创建Socket对象 传入目标ip+端口
Socket socket = new Socket("192.168.0.100",25255);
//封装要上传的文件
BufferedInputStream br = new BufferedInputStream(new FileInputStream("/Users/mo/account.txt"));
//获取通道的写入流
BufferedOutputStream bw = new BufferedOutputStream(socket.getOutputStream());
int len;
byte[] arr = new byte[4096];
while ((len=br.read(arr)) != -1){
bw.write(arr,0,len);
//重点!这里非常有必要flush()一下,不然会导致,最后一次没有发送出去,导致传过去的文件丢失一部分
bw.flush();
}
//上传完了就给个结束标记,告诉服务器已经传完了,别等了
socket.shutdownOutput();
//接收服务器端发来的反馈
InputStream rec = socket.getInputStream();
int end ;
byte[] arr1 = new byte[1024];
end = rec.read(arr1);
System.out.println(new String(arr1,0,end));
br.close();
socket.close();
}
}