序列化
1、java.io.Serializable接口:
-
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
-
可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
- 如果实现Serializable接口,对象如何序列化,各个属性序列化的顺序是什么,都是默认的,程序员无法指定,也不用关心。
- 如果属性前面有static和transient修饰,不参与序列化。
- 2、java.io.Externalizable接口:
- 若某个要完全控制某一对象及其超类型的流格式和内容,则它要实现 Externalizable 接口的 writeExternal 和 readExternal 方法。
- 程序员在writeExternal方法中,自定定制哪些属性要序列化,顺序是什么样。
- 程序员在readExternal方法中,自定定制哪些属性要反序列化,顺序和writeExternal的方法中一致。
打印流:输出流
- (1)PrintStream:
-
经典代表:System.out
-
Sysetm.err
- new PrintStream(文件名)
- new PrintStream(文件名,编码)
- new PrintStream(另一个字节输出流)
- (2)PrintWriter
-
Web阶段学习时,从服务器端往客户端返回消息时用到response,response.getWriter()可以返回PrintWriter对象。
-
即 Web服务器往客户端(例如:浏览器)返回html网页时,用的是PrintWriter对象的输出方法。
public void test04() throws IOException{
//所有数据类型写出去,都是按照文本处理
PrintStream ps = new PrintStream("1.txt");
ps.println(12);
ps.println(true);
ps.println(new Goods("《从入门到放弃》", 99.99, 1000));
ps.close();
}
System.in:默认情况下是从键盘输入的数据中扫描
- Scanner:可以从你指定的文件、流中读取文本数据
public class TestScanner {
@Test
public void test05() throws FileNotFoundException{
Scanner input = new Scanner(new File("d:/1.txt"),"GBK");//InputStream
while(input.hasNextLine()){
String line = input.nextLine();
System.out.println(line);
}
input.close();
}
@Test
public void test04() throws FileNotFoundException{
Scanner input = new Scanner("1.txt");//InputStream
while(input.hasNextLine()){
String line = input.nextLine();
System.out.println(line);
}
input.close();
}
@Test
public void test03() throws FileNotFoundException{
Scanner input = new Scanner(new File("1.txt"));//InputStream
while(input.hasNextLine()){
String line = input.nextLine();
System.out.println(line);
}
input.close();
}
@Test
public void test02() throws FileNotFoundException{
Scanner input = new Scanner(new FileInputStream("1.txt"));//InputStream
while(input.hasNextLine()){
String line = input.nextLine();
System.out.println(line);
}
input.close();
}
@Test
public void test01(){
Scanner input = new Scanner(System.in);
System.out.print("请输入一个整数:");
int num = input.nextInt();
System.out.println("num = " + num);
input.close();
}
}
在Java层面是常量对象,但是可以同C等底层语言进行修改
- System.in:
- System.out
- System.err
public class TestSystem {
@Test
public void test02() throws FileNotFoundException{
System.setOut(new PrintStream("1.txt"));
System.out.println("aaaa");
System.out.println("bbb");
System.out.println("ccc");
System.out.println("ddd");
}
@Test
public void test01(){
PrintStream out = System.out;
System.out.println(out);
}
}
JDK1.7中新增了一种try…catch处理的方式,
- 称为try…with…resource,它是为资源关闭专门设计的try…catch的语法
- try(
-
需要关闭的资源对象
- ){
-
可能发生异常的逻辑代码
- }catch(异常类型 e){
-
异常处理代码
- }catch(异常类型 e){
-
异常处理代码
- }
- 凡是在try()中声明的资源对象,都会自动关闭,无论是否发生异常。
public class TestTryWithResource {
@Test
public void test03() {
//从d:/1.txt(GBK)文件中,读取内容,写到项目根目录下1.txt(UTF-8)文件中
try(
FileInputStream fis = new FileInputStream("d:/1.txt");
InputStreamReader isr = new InputStreamReader(fis,"GBK");
BufferedReader br = new BufferedReader(isr);
FileOutputStream fos = new FileOutputStream("1.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
){
String str;
while((str = br.readLine()) != null){
bw.write(str);
bw.newLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void test02() {
//从d:/1.txt(GBK)文件中,读取内容,写到项目根目录下1.txt(UTF-8)文件中
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream("d:/1.txt"),"GBK"));
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("1.txt"),"UTF-8"));
String str;
while((str = br.readLine()) != null){
bw.write(str);
bw.newLine();
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(bw!=null){
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(br!=null){
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void test01(){
//从d:/1.txt(GBK)文件中,读取内容,写到项目根目录下1.txt(UTF-8)文件中
FileInputStream fis = null;
InputStreamReader isr = null;
BufferedReader br = null;
FileOutputStream fos = null;
OutputStreamWriter osw = null;
BufferedWriter bw = null;
try {
fis = new FileInputStream("d:/1.txt");
isr = new InputStreamReader(fis,"GBK");
br = new BufferedReader(isr);
fos = new FileOutputStream("1.txt");
osw = new OutputStreamWriter(fos,"UTF-8");
bw = new BufferedWriter(osw);
String str;
while((str = br.readLine()) != null){
bw.write(str);
bw.newLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(bw!=null){
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(osw!=null){
osw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos!=null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(br!=null){
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(isr!=null){
isr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fis!=null){
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/* public static void main(String[] args) {
//从d:/1.txt(GBK)文件中,读取内容,写到项目根目录下1.txt(UTF-8)文件中
FileInputStream fis = new FileInputStream("d:/1.txt");
InputStreamReader isr = new InputStreamReader(fis,"GBK");
BufferedReader br = new BufferedReader(isr);
FileOutputStream fos = new FileOutputStream("1.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
String str;
while((str = br.readLine()) != null){
bw.write(str);
bw.newLine();
}
bw.close();
osw.close();
fos.close();
br.close();
isr.close();
fis.close();
}*/
}
io:
- 阻塞式IO
- nio: JDK1.4–>JDK1.7
- 非阻塞式的IO
- Path(接口):类似于File,用路径名表示一个目录或文件
- Paths:工具类,用来创建Path接口的对象
- Files工具类,操作文件或目录的工具类:
- (1)Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
- (2)delete(Path path) 功能类似于File类的delete()
-
不同的地方是会报异常,当文件不存在时
- deleteIfExists(Path path) 功能相同于File类的delete() 存在就删除,不存在就什么也不干
- (3)move(Path source, Path target, CopyOption… options)
- (4)List readAllLines(Path path, Charset cs)
public void test06() throws IOException{
Path file = Paths.get("testIO","java","2.txt");
List<String> allLines = Files.readAllLines(file, Charset.forName("UTF-8"));
for (String string : allLines) {
System.out.println(string);
}
}
@Test
public void test05() throws IOException{
Path src = Paths.get("2.txt");
Path dest = Paths.get("testIO","java","2.txt");
Files.move(src, dest, StandardCopyOption.REPLACE_EXISTING);
}
@Test
public void test04() throws IOException{
File file = new File("1.txt");
file.delete();
}
@Test
public void test03() throws IOException{
Path src = Paths.get("1.txt");
Files.delete(src);
}
@Test
public void test02() throws IOException{
Path src = Paths.get("1.txt");
Path dest = Paths.get("2.txt");
Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
}
@Test
public void test01(){
Path file = Paths.get("testIO", "java" ,"1.txt");
int count = file.getNameCount();
System.out.println(count);
Path name = file.getName(0);
System.out.println(name);
}
网络
网路通信的三个要素:
- (1)地址:
-
IP地址,定位到一台设备
- (2)端口号
-
定位到一个应用程序(进程)
- (3)网络协议
-
对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。
-
通俗的讲:保证如何准确的到达对方那里,能够正确的解析出数据出来。
- OSI(Open System Interconnection)开放系统互连参考模型。
- 它把计算机网络分成物理层、数据链路层、网络层、传输层、会话层、表示层、应用层等七层。
- 物理层:建立、维护、断开物理连接
- 数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。
- 网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。协议有:ICMP、IGMP、IP(IPV4 IPV6)、ARP、RARP。
- 传输层:定义传输数据的协议端口号,以及流控和差错校验。协议有:TCP、UDP。
- 会话层:建立、管理、终止会话。对应主机进程,指本地主机与远程主机正在进行的会话
- 表示层:数据的表示、安全、压缩。格式有:JPEG、ASCll、DECOIC、加密格式等
- 应用层:网络服务与最终用户的一个接口。协议有:HTTP、FTP、SMTP、DNS、TELNET、HTTPS、POP3等等。
- 最早确定的,也是最重要的是TCP/IP协议。把这些协议家族称为TCP/IP协议簇。
- (1)主机-网路层:硬件层面
- (2)网络层:例如IP寻址
- (3)传输层:协议有:TCP、UDP。
- (4)应用层:程序员面对的
- TCP:(Transmission Control Protocol,传输控制协议)面向连接的,可靠的,基于字节流的,适用于大数据量的传输的协议。
- UDP:(User Datagram Protocol,用户数据报协议)非面向连接,不可靠的,基于用户数据报(报文),只能支持最多64k以内的数据的发送。
IP地址: - IPV4:32位,4个整数,每一个整数是1个字节(无符号)0~255
-
例如:192.168.30.142
- IPV6:
-
128 位无符号数字,8个16进制值
-
例如:1080:0:0:0:8:800:200C:417A
- 特殊的IP:
-
127.0.0.1 本地回传IP
-
224.0.0.0至239.255.255.255 广播IP地址
-
....
- 域名:
- 用人比较方便记忆和识别的单词来代表IP地址
- 例如:www.atguigu.com
-
www.jd.com
-
www.taobao.com
-
www.baidu.com
-
....
- 域名–》域名解析器–>IP地址
- 域名:
- 一级域名:.com商业
-
.cn 中国
-
.org 组织,机构,非盈利型
-
.gov政府
-
.edu教育
-
....
- 域名需要注册
端口号:
- [0,65535]之间无符号 2个字节
- (1)公认端口(Well-Known Ports)范围从0到1023
-
http: 80 ftp:21 SMTP:25
- (2)注册端口(Registered Ports):端口号从1024到49151。
-
例如:Tomcat(8080),JBOSS(8080),Oracle(1521),MySQL(3306),SQL Server(1433),QQ(1080)。
- (3)动态/私有端口(Dynamic and/Private Ports):端口的范围从49152到65535,这些端口号一般不固定分配给某个服务。
java.net.InetAddress:用来包装IP地址对象的
- InetAddress有两个子类:Inet4Address 、Inet6Address
- (1)InetAddress.getLocalHost()
- (2)InetAddress.getByName(“www.baidu.com”)
- (3)InetAddress.getByAddress(addr)
- 如果后面的API中,要接收IP的形参类型是InetAddress的话,就可以刚才的方法来创建对象
不管传输层使用的是TCP/UDP协议,Java中用这样的API来表示,Socket(套接字)
- 分类:
- (1)流套接字:用于TCP,ServerSocket类和Socket类
- (2)数据报套接字:用于UDP,DatagramSocket类
- Socket(也可以叫“套接字”),是两台机器间通信的端点。可以和网卡驱动进行交流。负责把数据交给网卡驱动,或者从网卡驱动中提取数据。
TCP协议编程
/*
* TCP:
* 客户端,主动连接服务器
*
* Socket(InetAddress address, int port)
* Socket(String host, int port)
*
* 步骤:
* 1、连接服务器
* Socket socket = new Socket("192.168.30.142",9999);
*
* 2、发送或接受数据
*
* 3、断开连接
*/
public class TestClient {
public static void main(String[] args) throws UnknownHostException, IOException {
// 1、连接服务器
Socket socket = new Socket("192.168.30.142",9999);
//2、例如:接受数据
//字节流,输入流,InputStream
InputStream in = socket.getInputStream();
byte[] data = new byte[1024];
int len;
while((len = in.read(data)) != -1){
System.out.println(new String(data,0,len));
}
//3、断开
socket.close();
}
}
/*
* TCP:面向连接,可靠的,基于字节流的
* 服务器:等待被连接的过程
*
* ServerSocket:只负责接受和建立连接,不负责数据的传输
* Socket:负责数据的传输
*
* 步骤:
* 1、开启服务器
* 指定服务器监听的端口号
* 2、等待客户端并接受客户端的连接
*
* 3、接受/发送数据
* 发送方:输出流
* 接受方:输入流
*
* 4、断开连接
*
* 5、关闭服务器
*/
public class TestServer {
public static void main(String[] args) throws IOException {
//1、开启服务器:网卡驱动就监听9999端口号的数据
ServerSocket server = new ServerSocket(9999);
//2、等待客户端并接受客户端的连接
Socket socket = server.accept();//这句代码执行一次,就接受一个客户端连接
System.out.println("一个客户端连接成功!");
//3、例如:发送数据
//发送:欢迎你登录
//字节流,输出流 OutputStream
//(1)获取输出流
OutputStream out = socket.getOutputStream();
//(2)发送数据
out.write("欢迎你登录".getBytes());
//4、断开连接
socket.close();
//5、关闭服务器
server.close();
}
}
服务器端:
- (1)接收客户端的连接
- (2)接收客户端的词语
- (3)把词语“反转”返回给客户端
- (2)(3)多次,直到客户端发送"bye"为止
- 加一个条件,服务器端可以同时接收n个客户端连接
- 服务器端得加多线程
public class TestServer {
public static void main(String[] args) throws IOException {
//1、开启服务器
ServerSocket server = new ServerSocket(8989);
boolean flag = true;
while(flag){
//2、接收一个客户端的连接
Socket socket = server.accept();//每个客户端的socket是独立的
//为没一个客户端开启一个独立的线程维护它的通信
MessageHandler mh = new MessageHandler(socket);
mh.start();
}
//5、关闭服务器
server.close();
}
}
class MessageHandler extends Thread{
private Socket socket;
public MessageHandler(Socket socket) {
super();
this.socket = socket;
}
public void run(){
try {
//3、先获取输入流和输出流
InputStream in = socket.getInputStream();
/*
* 因为是接收一个词语,反转一个,返回一个
* 那么如果仅仅使用字节流,不好区分词语
* 需要用到字符流
* 那么就意味着需要把字节流转为字符流
*/
InputStreamReader isr = new InputStreamReader(in);//这里不涉及编码问题,仅仅为了转换流的类型
/*
* 字符流中几个字符是一个词语
* 那么我们这里选择“换行符”来作为词语的分割
* 意味着我们可以按行读取Scanner或BufferedReader
*/
BufferedReader br = new BufferedReader(isr);
OutputStream out = socket.getOutputStream();
/*
* 客户端收到字节,同样不方便处理几个字节是一个词语,仍然要把字节输出流转为字符流
* 而且字符之间也不好区分,那么也选择“换行符”进行区别词语
* 我们现在需要把OutputStream转为一个可以按行写的字符流或其他的处理流
*
* 可以按行写的:BufferedWriter(newLine())
* PrintStream(println())
*/
PrintStream ps = new PrintStream(out);
//从客户端接收词语
String word;
while((word = br.readLine()) != null){
if("bye".equals(word)){
break;
}
//如果不是bye,要反转,并且返回
StringBuilder sb = new StringBuilder(word);
sb.reverse();
//返回给客户端
ps.println(sb.toString());
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
//4、断开
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端:
- (1)从键盘输入词语
- (2)发送给服务器
- (3)接收服务器返回的结果
- (1)(2)(3)多次进行,直到键盘输入bye并发送给发服务器之后就结束
- 加一个条件,服务器端可以同时接收n个客户端连接
- 客户端代码不用修改
public class TestClient {
public static void main(String[] args) throws UnknownHostException, IOException {
//1、连接服务器
Socket socket = new Socket("192.168.30.142",8989);
/*
* * (1)从键盘输入词语
* (2)发送给服务器
* (3)接收服务器返回的结果
* (1)(2)(3)多次进行,直到键盘输入bye并发送给发服务器之后就结束
*/
Scanner input = new Scanner(System.in);
/*
* 同样考虑到发送词语,以及词语之间分割问题,那我们选择PrintStream和BufferedReader
*/
PrintStream ps = new PrintStream(socket.getOutputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
//从键盘输入词语
System.out.print("请输入词语:");
String word = input.next();
//发送给服务器端
ps.println(word);
if("bye".equals(word)){
break;
}
//接收服务器返回的结果
String result = br.readLine();
System.out.println("服务器返回的反转后的结果:" + result);
}
input.close();
socket.close();
}
}
从客户端发送文件到服务器端
- (1)接收客户端的连接
- (2)接收文件名.后缀名
- 思考:
- 存哪里 ①在当前项目中找一个位置存储,例如:upload文件夹
- 如何解决文件名重名的问题 文件名需要处理,加入时间戳或其他的唯一编码的UUID等值
- .后缀名需要保留,因为它代表文件的类型
- (3)接收文件内容
- (4)反馈结果
- 思考:
-
这里既要接收文件名.后缀名,又要接收文件内容。
-
这里既有 文本信息“文件名.后缀名”,又有其他类型的数据“文件内容”,只能选择字节流。
-
如何区别,文件名.后缀名 与 文件内容呢
-
想哪种字节输入流,可以处理字符串,和字节类型的数据。
-
FileInputStream
-
BufferedInputStream
-
DataInputStream
-
ObjectInputStream
-
这些里面:DataInputStream:readUTF() 和 read(byte[])
-
ObjectInputStream也可以,但是麻烦,我这里选择DataInputStream
public class TestServer {
public static void main(String[] args) throws IOException {
//1、开启服务器
ServerSocket server = new ServerSocket(9999);
//2、接收客户端的连接
Socket socket = server.accept();
//3、获取输入流
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
//接收文件名.后缀名
String fileName = dis.readUTF();
//处理文件名
/*
* 希望我要在服务器存储的文件名: 原来的文件名 + 时间戳
*/
long timestamp = System.currentTimeMillis();
//.的下标
int index = fileName.lastIndexOf(".");
//后缀名
String ext = fileName.substring(index);
// 原来的文件名
String name = fileName.substring(0, index);
//新文件名
String newFileName = "upload/" + name + timestamp + ext;
//创建文件输出流,把接收到的文件内容,写入新文件
FileOutputStream fos = new FileOutputStream(newFileName);
//接收文件内容
byte[] data = new byte[1024];
int len;
while((len = dis.read(data))!=-1){
fos.write(data, 0, len);
}
//还可以给客户端反馈:文件接收完毕
OutputStream out = socket.getOutputStream();
PrintStream ps = new PrintStream(out);
ps.println("文件接收完毕!");
//断开
fos.close();
socket.close();
server.close();
}
}
从客户端发送文件到服务器端
*
*
- 客户端:
- (1)从键盘输入文件的路径名,即选择要发送的文件
- (2)给服务器先把“文件名.后缀名"
- (3)发送文件内容
- (4)接收服务器的反馈结果
- 这里同样因为既要发送“文件名.后缀名",又要发送“文件内容”,选择字节流,选择DataOutputStream
public class TestClient {
public static void main(String[] args) throws UnknownHostException, IOException {
//1、连接服务器
Socket socket = new Socket("192.168.30.142",9999);
//2、从键盘输入文件的路径名,即选择要发送的文件
Scanner input = new Scanner(System.in);
System.out.print("请选择你要发送的文件(例如:D:/尚硅谷_190513班_柴林燕_JavaSE/开学典礼所发资料.rar):");
String fileName = input.nextLine();
File file = new File(fileName);
//3、给服务器发送“文件名.后缀名"
OutputStream out = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(out);
//发送“文件名.后缀名"
dos.writeUTF(file.getName());
//4、发送文件内容
//先从file文件读取内容
FileInputStream fis = new FileInputStream(file);
byte[] data = new byte[1024];
int len;
while((len = fis.read(data)) != -1){
//一边读,一边给服务器发送
dos.write(data,0,len);
}
socket.shutdownOutput();//表示发送完毕了
//5、接收反馈
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String result = br.readLine();
System.out.println("结果:" + result);
//6、关闭
socket.close();
fis.close();
}
}
群聊实现
public class TestServer {
private static ArrayList<Socket> online = new ArrayList<Socket>();
public static void main(String[] args) throws IOException {
//1、开启服务器
ServerSocket server = new ServerSocket(9999);
while(true){
//2、接收客户端的连接
Socket socket = server.accept();
//把这个客户端加入到online中
online.add(socket);
//每一个客户端独立的线程
MessageHandler mh = new MessageHandler(socket);
mh.start();
}
}
//私有的静态的内部类
//这里用内部类的原因,是为了用上面的online集合
private static class MessageHandler extends Thread{
private Socket socket;
private String ip;
public MessageHandler(Socket socket) {
super();
this.socket = socket;
this.ip = socket.getInetAddress().getHostAddress();
}
public void run(){
//这个客户端的一连接成功,线程一启动,就可以告诉其他人我上线了
sendToOthers(ip+"上线了");
/*
* (1)接收当前的客户端发送的消息
* (2)给其他在线的客户端转发
*/
//(1)接收当前的客户端发送的消息
try {
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String content;
while((content = br.readLine()) !=null){
if("bye".equals(content)){
//给自己发一句bye
OutputStream out = socket.getOutputStream();
PrintStream ps = new PrintStream(out);
ps.println("bye");
break;
}
//收到一句,转发一句
sendToOthers(ip+"说:" + content);
}
sendToOthers(ip+"下线了");
} catch (IOException e) {
sendToOthers(ip+"掉线了");
}
}
//因为转发的代码也很长,独立为一个方法
public void sendToOthers(String str){
//遍历所有online的客户端
Iterator<Socket> iterator = online.iterator();
while(iterator.hasNext()){
Socket on = iterator.next();
if(!on.equals(socket)){//只给其他客户端转发
try {
OutputStream out = on.getOutputStream();
PrintStream ps = new PrintStream(out);
ps.println(str);
} catch (IOException e) {
//说明on这个客户端要么下线了,要么掉线了
iterator.remove();
}
}
}
}
}
}
public class TestClient {
public static void main(String[] args) throws UnknownHostException, IOException {
//1、连接服务器
Socket socket = new Socket("192.168.30.142",9999);
//2、开启两个线程,一个收消息,一个发消息
SendThread st = new SendThread(socket);
ReceiveThread rt = new ReceiveThread(socket);
st.start();
rt.start();
//等发送线程停下来再往下走
try {
st.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//让接收数据的线程停下
rt.setFlag(false);
//等接收线程停下来,再往下走,断开连接
try {
rt.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
socket.close();
}
}
class SendThread extends Thread{
private Socket socket;
public SendThread(Socket socket) {
super();
this.socket = socket;
}
public void run(){
try {
//键盘输入
Scanner input = new Scanner(System.in);
OutputStream out = socket.getOutputStream();
PrintStream ps = new PrintStream(out);
while(true){
//从键盘输入
System.out.print("请输入要发送的消息:");
String content = input.nextLine();
System.out.println("content:" + content);
//给服务器发送
ps.println(content);
//如果bye,就结束发送
if("bye".equals(content)){
break;
}
}
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ReceiveThread extends Thread{
private Socket socket;
private boolean flag = true;
public ReceiveThread(Socket socket) {
super();
this.socket = socket;
}
public void run(){
try {
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
while(flag){
String line = br.readLine();
System.out.println(line);
if("bye".equals(line)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
UDP
public class TestReceive {
public static void main(String[] args) throws IOException {
//1、接收方也要socket
//接收方的端口号,指定,IP自动获取
DatagramSocket ds = new DatagramSocket(9999);
//2、准备一个数据报,接收数据
byte[] data = new byte[1024];
DatagramPacket dp = new DatagramPacket(data,data.length);
//3、接收数据
ds.receive(dp);
//4、把数据拆解出来
byte[] bs = dp.getData();//接收的数据
int len = dp.getLength();//接收的数据的实际长度
System.out.println(new String(bs,0,len));
//5、断开
ds.close();
}
}
public class TestSend {
public static void main(String[] args) throws IOException {
//1、发送方,建立一个Socket
//发送方的端口号和IP地址,自动获取
DatagramSocket ds = new DatagramSocket();
//2、准备把要发送的数据打包
String str = "马上下课了";
byte[] bytes = str.getBytes();
InetAddress ip = InetAddress.getByName("192.168.30.142");
DatagramPacket dp = new DatagramPacket(bytes,0,bytes.length, ip, 9999);
//3、发送,通过socket发送
ds.send(dp);
System.out.println("发送完毕");
//4、关闭
ds.close();
}
}