复习
1.实现线程的三种方式
继承线程类 Thread 在子类中重写 run 方法。在run 方法中定义线程的任务。
实现 Runnable 接口。该接口的子类可以认为是任务类。将该子类对象作为实参传递给Thread 对象即可。
实现Callable 接口,实现call 方法。该子类是任务类。将该类的对象作为实参给 FutureTask 对象。FutureTask 对象对象是用来调度管理任务的。再将 FutureTask 对象 给Thread 类对象。
2. 线程同步的三种方式
同步代码块,建议使用当前类的Class 对象 作为监视器对象使用。
同步方法,实例方法同步的是this,静态方法同步的是当前类的Class对象。
Lock 接口的子类对象。ReentrantLock 使用 try-finally 一定要解锁。
3. wait、notify、notifyAll
wait(): 导致当前线程在当前对象上等待,进入阻塞状态,并释放对当前对象的锁。
notify(): 唤醒在当前对象上等待的某一个线程,从阻塞状态进入就绪状态。等待cpu的下次的调度执行。如果被唤醒的线程被cpu调度执行了,那么该线程一定要重新获得wait() 时候释放的锁的控制权。
notifyAll(): 唤醒在当前对象上等待的所有线程,从阻塞状态进入就绪状态。等待cpu的下次的调度执行。
4 sleep(long time) 、wait().
相同点:都会导致当前线程被阻塞、都可以通过控制参数的时间来解除线程阻塞状态。
不同点:1调用方式不同;2所定义的类不同,一个是Thread类的静态方法,一个是Object类的实例方法。3 sleep 可以在同步代码块内部或者是同步代码块外部使用,wait 必须在同步中使用。4 sleep 只能通过时间来解除阻塞,wait 可以通过其他的线程调用notify 唤醒 5 如果当前线程持有监视器对象的锁,那么在sleep 的过程中,是不会释放锁资源的。wait,等待之后,会释放锁资源。
第一节 网络通信三要素
1. 相关的概念 了解
网络:使用物理线路或者是无线线路将若干台计算机连接在一起的整体。
网络的分类:局域网 LAN、城域网 MAN、广域网 WAN。
2. 三要素
1.如何在网络中唯一标识一台计算机? ip地址
2.同一台计算机上的多个程序如何共用网络而不冲突? 网络端口
3.不同的计算机通信怎么才能互相理解? 使用相同的协议
3 .IP地址
IP地址: ⽤来在⽹络中标记⼀台电脑的⼀串数字, ⽐如192.168.1.1(c类); 在同一⽹络上是惟⼀的(用来标记唯一的一台电脑)IP地址由4个字节32bits 构成,每一个字节表示的范围是[0-255]。使用 【.】点分符将四个字节分隔组成。例子:192.168.151.241 。IP地址一共有232次幂 种状态,IPV4版本已经不足以支持现有的网络中的计算机的数量。
每⼀个IP地址包括两部分: ⽹络地址和主机地址
公网地址可以通过互联网直接访问,私网地址只能作为公司内部的局域网使用。
IP地址是作为计算机中唯一标识使用的。通过IP地址确定和互联网中的哪个计算机交互。
127.0.0.1 可以代表本机。进行本机的网络测试。
localhost 也可以代表本机IP地址。
主机号0,255两个数不能使用(网络号、广播地址)255.255.255.255:广播的IP地址。代表子网广播地址,代表了子网中的所有的主机。
互联网中访问其他的计算机,都是通过IP地址确定的。访问公司的域名本质上也是通过IP地址访问的。公司的域名和公司的服务器的IP地址有一个一一对应的关系。通过域名就可以获得对应到 IP 地址。DNS:域名解析器。作用就是进行域名到IP地址转换的。
A类IP地址 :由1字节的⽹络地址和3字节主机地址组成, ⽹络地址的最⾼位必须是“0”,地址范围1.0.0.1-126.255.255.254可⽤的A类⽹络有126个, 每个⽹络能容纳1677214个主机
B类IP地址:由2个字节的⽹络地址和2个字节的主机地址组成, ⽹络地址的最⾼位必须是“10”,地址范围128.1.0.1-191.255.255.254 可⽤的B类⽹络有16384个, 每个⽹络能容纳65534主机
C类IP地址:由3字节的⽹络地址和1字节的主机地址组成, ⽹络地址的最⾼位必须是“110”范围192.0.1.1-223.255.255.254 C类⽹络可达2097152个, 每个⽹络能容纳254个主机
D类IP地址:第⼀个字节以“1110”开始, 它是⼀个保留的地址。它并不指向特定的⽹络, ⽬前这⼀类地址被⽤在多点⼴播(一对多) 中多点⼴播地址⽤来⼀次寻址⼀组计算机 地址范围224.0.0.1-239.255.255.254
E类IP地址:以“1111”开始, 为将来使⽤保留 E类地址保留, 仅作实验和开发⽤
私有IP:本地局域网上的IP,专门为组织机构内部使用
在这么多⽹络IP中, 国际规定有⼀部分IP地址是⽤于我们的局域⽹使⽤, 属于私⽹IP, 不在公⽹中使⽤的, 它们的范围是:
10.0.0.0~10.255.255.255
172.16.0.0~172.31.255.255
192.168.0.0~192.168.255.255
私有IP:局域网通信,内部访问,不能在外网公用。私有IP禁止出现在Internet中,来自于私有IP的流量全部都会阻止并丢掉
公有IP:全球访问
IP地址127. 0. 0. 1~127. 255. 255. 255⽤于回路测试
测试当前计算机的网络通信协议
如: 127.0.0.1可以代表本机IP地址, ⽤ http://127.0.0.1 就可以测试本机中配置的Web服务器
常用来ping 127.0.0.1来看本地ip/tcp正不正常,如能ping通即可正常使用
⼦⽹掩码:是我们测量两个IP是否属于同一个网段的工具
⼦⽹掩码不能单独存在, 它必须结合IP地址⼀起使⽤
⼦⽹掩码只有⼀个作⽤, 就是将某个IP地址划分成⽹络地址和主机地址两部分
⼦⽹掩码的设定必须遵循⼀定的规则:与IP地址相同, ⼦⽹掩码的长度也是32位,左边是⽹络位, ⽤⼆进制数字“1”表示;右边是主机位, ⽤⼆进制数字“0”表示
假设IP地址为“192.168.1.1”⼦⽹掩码为“255.255.255.0”。其中, “1”有24个, 代表与此相对应的IP地址左边24位是⽹络号;“0”有8个, 代表与此相对应的IP地址右边8位是主机号
4. 端口号
端口号: 用来标记区分进程
⼀台拥有IP地址的主机可以提供许多服务, ⽐如HTTP(万维⽹服务) 、 FTP(⽂件传输) 、 SMTP(电⼦邮件) 等, 这些服务完全可以通过1个IP地址来实现。 那么, 主机是怎样区分不同的⽹络服务呢?
显然不能只靠IP地址, 因为IP地址与⽹络服务的关系是⼀对多的关系。实际上是通过“IP地址+端⼝号”来区分不同的服务的。
端⼝号是一个数字,只有整数, 范围是从0到65535 (分为知名和动态两种)是一个逻辑端口,不是一个物理设备。每个通信的软件都唯一的使用了某一个端口进行通信。
知名端⼝是众所周知的端⼝号(用来做固定事情), 范围从0到1023 80端⼝分配给HTTP服务(网站) oracle 1521 mysql 3306 21端⼝分配给FTP服务(文件下载)。可以理解为, ⼀些常⽤的功能使⽤的号码是固定的 。
动态端⼝的范围是从1024到65535,之所以称为动态端⼝, 是因为它⼀般不固定分配某种服务, ⽽是动态分配。动态分配是指当⼀个系统进程或应⽤程序进程需要⽹络通信时, 它向主机申请⼀个端⼝, 主机从可⽤的端⼝号中分配⼀个供它使⽤
建议我们自定义的网络程序使用的端口的范围[1024~5000]。
端口号确定了和主机中的哪个软件交互。
5. 协议
协议:约定好的规范 。 例如:IP、TCP、UDP、http
根据TCP/IP协议簇功能的不同,将它分为了几种层次( TCP/IP协议簇层次划分)(重点记住)
(写代码按四层划分)
应用层:应用程序间沟通的层,不同的文件系统有不同的文件命名原则和不同的文本行表示方法等,不同的系统之间传输文件还有各种不兼容问题,这些都将由应用层来处理
传输层:在此层中,它提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据报协议(UDP)等,这一层负责传送数据,并且确定数据已被送达并接收。
网络层:负责提供基本的数据包传送功能,让每一块数据包都能够到达目的主机。网络层接收由更低层发来的数据包,并把该数据包发送到更高层,相反,IP层也把从TCP或UDP层接收来的数据包传送到更低层。
网络接口层(链路层):对实际的网络媒体的管理,定义如何使用实际网络来传送数据(处理机械的、电气的和过程的接口)
(理论上由七层组成)
物理层
数据链路层
网络层
传输层
会话层
表示层
应用层
TCP:
1.传输控制协议。Transfer control protocol
2.特点:安全的、可靠、面向连接、全双工的、字节流协议。
3.面向连接:通信的两端,先建立一个独立的信息的通道,然后使用该通道进行信息的交互。类似打电话。
4.全双工的:收发信息可以同时进行。
5.安全可靠的实现的机制:三次握手、四次挥手。
TCP协议:三次握手过程和四次挥手详解
三次握手建立连接:
第一次握手:客户端发送syn包(seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。
传输数据过程:
a.超时重传超时重传机制用来保证TCP传输的可靠性。每次发送数据包时,发送的数据报都有seq号,接收端收到数据后,会回复ack进行确认,表示某一seq 号数据已经收到。发送方在发送了某个seq包后,等待一段时间,如果没有收到对应的ack回复,就会认为报文丢失,会重传这个数据包。b.快速重传接受数据一方发现有数据包丢掉了。就会发送ack报文告诉发送端重传丢失的报文。如果发送端连续收到标号相同的ack包,则会触发客户端的快速重 传。比较超时重传和快速重传,可以发现超时重传是发送端在傻等超时,然后触发重传;而快速重传则是接收端主动告诉发送端数据没收到,然后触发发送端重传。c.流量控制这里主要说TCP滑动窗流量控制。TCP头里有一个字段叫Window,又叫Advertised-Window,这个字段是接收端告诉发送端自己 还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。 滑动窗可以是提高TCP传输效率的一种机制。d.拥塞控制滑动窗用来做流量控制。流量控制只关注发送端和接受端自身的状况,而没有考虑整个网络的通信情况。拥塞控制,则是基于整个网络来考虑的。考虑一下这 样的场景:某一时刻网络上的延时突然增加,那么,TCP对这个事做出的应对只有重传数据,但是,重传会导致网络的负担更重,于是会导致更大的延迟以及更多 的丢包,于是,这个情况就会进入恶性循环被不断地放大。试想一下,如果一个网络内有成千上万的TCP连接都这么行事,那么马上就会形成“网络风 暴”,TCP这个协议就会拖垮整个网络。为此,TCP引入了拥塞控制策略。拥塞策略算法主要包括:慢启动,拥塞避免,拥塞发生,快速恢复。
四次挥手断开连接:
第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不会再给你发数据了(当 然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但此时主动关闭方还可以接受数据。
第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。
UDP:
1.用户数据报协议 User datagram protocol
2.特点:不安全的,不可靠的,面向无连接的,基于数据报的协议。类似:写信。
3.优点:效率高
4.缺点:不安全、不可靠、包裹大小有限制。64K。
第二节 相关的类 java.net
1. InetAddress
说明:该类封装的是主机的 IP地址+主机名
/**
*InetAddress
*/
public class TestInetAddress {
public static void main(String[] args) throws Exception {
//得到本机的InetAddress对象
InetAddress host=InetAddress.getLocalHost();
System.out.println(host);
//获得名字
System.out.println(host.getHostName());
//获得IP地址
System.out.println(host.getHostAddress());
//获得别人的(没有获取到主机名)
InetAddress otherAddress=InetAddress.getByName("192.168.151.183");
System.out.println(otherAddress);
//得到互联网中主机的InetAddress对象
InetAddress[] address1=InetAddress.getAllByName("www.baidu.com");
for (InetAddress add:address1) {
System.out.println(add);
}
}
}
2. URL
URL是Uniform Resource Location的缩写,译为“统一资源定位符”。通俗地说,URL是Internet上用来描述信息资源的字符串。
/**
* java.net.URL (创建URL对象的时候里面的参数是——网址)
* https://news.163.com/19/1030/09/ESNO4CC0000189FH.html
* 一个URL由四部分组成:
* 协议:https
* 主机地址:news.163.com
* 端口号:协议默认 80
* 文件:/19/1030/09/ESNO4CC0000189FH.html
*/
public class TestUrl {
public static void main(String[] args) throws Exception{
test();
}
//使用url获得url的四个属性
static void test() throws Exception{
URL url=new URL("https://news.163.com/19/1030/09/ESNO4CC0000189FH.html");
System.out.println( url.getProtocol()); //协议 https
System.out.println(url.getHost()); // 主机地址 news.163.com
System.out.println(url.getPort()); //端口 1
System.out.println(url.getFile()); //文件/19/1030/09/ESNO4CC0000189FH.html
//得到网络传输文件的字节输入流
InputStream is=url.openStream();
//还可以这样,这个知道有这么回事儿就行
// URLConnection urlConnection=url.openConnection();
// InputStream is=url.openStream();
//isr负责从底层字节流中读取字节转换为字符,依赖utf-8
InputStreamReader isr=new InputStreamReader(is,"GBK");
//br负责从isr解码的字符数据中,一行一行的去读取。
BufferedReader br=new BufferedReader(isr);
String str=br.readLine();
while(str!=null){
System.out.println(str);
str=br.readLine();
}
br.close();
}
}
第三节 socket 编程 UDP
1 .使用UDP协议的DatagramSocket
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
*使用DatagramSocket进行数据的发送和接收--接收
*/
public class TestUdp {
public static final int RECEIVE_PORT=1222;
public static void main(String[] args) {
try {
receive();
} catch (Exception e) {
e.printStackTrace();
}
}
static void receive() throws Exception{
//创建用于收发数据的socket对象,必须指定接收的端口号
DatagramSocket ds=new DatagramSocket(RECEIVE_PORT);
byte[] buf=new byte[1024];
//创建dp对象存储收到的数据
//dp的作用就是将收到的字节数据,存到buf中
DatagramPacket dp=new DatagramPacket(buf,buf.length);//DatagramPacket的形参是(byte[],int length)
//接收数据 是一个阻塞式的方法 没有数据就一直等待
ds.receive(dp);
//将接收到的字节数组还原为原来的数据 解析dp
int length=dp.getLength();
String str=new String(buf,0,length);
System.out.println("来信"+str);
//回信息
str="收到来信,谢谢";
buf=str.getBytes();
//获得对方的InetAdress对象
InetAddress address=dp.getAddress();
//获得发送发的端口号
int port=dp.getPort();
dp=new DatagramPacket(buf,buf.length,address,port);
ds.send(dp);
ds.close();
}
}
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* 使用DatagramSocket进行数据的发送和接收
*/
public class TestUdp {
public static void main(String[] args) {
try {
sendMsg();
} catch (Exception e) {
e.printStackTrace();
}
}
static void sendMsg() throws Exception{
//创建发送包裹的快递员,可以指定端口号,也可以有系统分配
DatagramSocket ds=new DatagramSocket();
//提供书信的数据 信
String str= "你好!";
byte[] buf=str.getBytes();
//将字节数组封装到信封中DatagramPacket
DatagramPacket dp=new DatagramPacket(buf,buf.length, InetAddress.getByName("192.168.151.182"),1222);
//发送数据
ds.send(dp);
//接收回信
buf=new byte[1024];
dp=new DatagramPacket(buf,buf.length);
ds.receive(dp);
System.out.println("收到回信"+new String(buf,0,dp.getLength()));
//关闭
ds.close();
}
}
2. DatagramPacket收发对象
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
*发送对象Object o
*/
public class TestUdpSendObj {
public static void main(String[] args) throws Exception{
DatagramSocket ds=new DatagramSocket();
User user=new User("用户1","123456");
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(oos);
//得到user对象的字节数组
byte[] buf=baos.toByteArray();
//将字节数组封装到信封中,DataGramPAcket
DatagramPacket dp=new DatagramPacket(buf,buf.length, InetAddress.getByName("192.168.151.182"),1222);
//发送数据
ds.send(dp);
ds.close();
}
}
//创建用户类继承序列化接口
class User implements Serializable{
private String name;
private String pwd;
public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
*接收对象Object o
*/
public class TestRecvObj {
public static final int RECEIVE_PORT=1222;
public static void main(String[] args) throws Exception {
//创建用于收发数据的socket对象,必须指定接收的端口号
DatagramSocket ds=new DatagramSocket(RECEIVE_PORT);
byte[] buf=new byte[1024];
//创建dp对象存储接收的数据
//dp的作用就是将受到的字节数据存入到buf中
DatagramPacket dp=new DatagramPacket(buf,buf.length);
//接收数据 是一个阻塞式的方法 没有数据就一直等待
ds.receive(dp);
//使用字节数组还原user对象
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(buf));
Object o=ois.readObject();
System.out.println(o);
ds.close();
}
}
3.简易聊天室
//老板的代码
public class TestChat {
static Frame frame;
static TextArea area;
static Label ipLabel;
static Label msgLabel;
static TextField ipField;
static TextField msgField;
static DatagramSocket ds;
public static final int PORT = 1777;
public static void main(String[] args)throws Exception {
ds = new DatagramSocket(PORT);
initFrame();
receiveMsg();
}
//初始化窗口
private static void initFrame() {
frame = new Frame("尚学堂502聊天室");
frame.setSize(500, 500);
frame.setLocation(1360 - 500 >> 1, 700 - 500 >> 1);
frame.setResizable(false);
// 设置布局
frame.setLayout(new FlowLayout());
area = new TextArea(25, 68);
area.setEditable(false);
ipLabel = new Label("IP ADDRESS");
msgLabel = new Label("SEND CONTENT");
ipField = new TextField(50);
msgField = new TextField(50);
frame.add(area);
frame.add(ipLabel);
frame.add(ipField);
frame.add(msgLabel);
frame.add(msgField);
addEnventListener();
//默认不可见
frame.setVisible(true);
}
//给frame 和 msgFiled 添加事件
private static void addEnventListener() {
//frame
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
//msgField
msgField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//获得内容
String text = msgField.getText();
area.append("自己:" + text + "\r\n");
msgField.setText("");
//获得接收方的InetAddress对象
InetAddress address = null;
try {
address = InetAddress.getByName(ipField.getText());
} catch (UnknownHostException e1) {
e1.printStackTrace();
}
//
byte[] buf = text.getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length, address,PORT);
try {
ds.send(dp);
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
}
//接收的方法
static void receiveMsg()throws Exception{
// 主线程接收
//创建dp 对象存储收到的数据
byte[] buf = new byte[1024];
//dp 的作用就是将接收到的字节数据,存到buf 中。
DatagramPacket dp = new DatagramPacket(buf,buf.length);
while(true){
//接收数据 是一个阻塞式方法。没有数据就一直等待。
ds.receive(dp);
//将接收到的字节数组,还原为原来的数据 解析dp
//得到dp接收到的字节数据的长度
int length = dp.getLength();
String str = new String(buf,0,length);
//得到发送方的地址。
String sendHostAddr = dp.getAddress().getHostAddress();
//获得本机地址
String localHostAddr = InetAddress.getLocalHost().getHostAddress();
//屏蔽自己给自己发的消息
if(sendHostAddr.equals(localHostAddr))
continue;
//将接收到的信息,添加到area 中
area.append(sendHostAddr + " : "+str + "\r\n");
}
}
}
第四节. TCP 编程
Socket套接字(转)
所谓socket 通常也称作”套接字“,用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过”套接字”向网络发出请求或者应答网络请求。
Socket和ServerSocket类库位于java.net包中。ServerSocket用于服务器端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等的,并没有差别,不因为在服务器端或在客户端而产生不同级别。不管是Socket还是ServerSocket它们的工作都是通过SocketImpl类及其子类完成的。
重要的Socket API:
java.net.Socket继承于java.lang.Object,有八个构造器,其方法并不多,下面介绍使用最频繁的三个方法,其它方法大家可以见JDK-1.3文档。
. Accept方法用于产生”阻塞”,直到接受到一个连接,并且返回一个客户端的Socket对象实例。”阻塞”是一个术语,它使程序运行暂时”停留”在这个地方,直到一个会话产生,然后程序继续;通常”阻塞”是由循环产生的。
. getInputStream方法获得网络连接输入,同时返回一个InputStream对象实例。
. getOutputStream方法连接的另一端将得到输入,同时返回一个OutputStream对象实例。
注意:其中getInputStream和getOutputStream方法均会产生一个IOException,它必须被捕获,因为它们返回的流对象,通常都会被另一个流对象使用。
————————————————
原文链接:https://blog.youkuaiyun.com/siying8419/article/details/79735901
1.实现用户登陆
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
*客户端
*/
public class LoginClient {
public static void main(String[] args) {
Socket socket=null;
try {
socket=new Socket("192.168.151.182",LoginServer.SERVER_PORT);
String str="name=maomao&pwd=123456";
//使用输出流发送数据
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(str);
bw.newLine();
bw.flush();
//接收服务器端返回来的信息,是一个布尔值true or false
DataInputStream dis=new DataInputStream(socket.getInputStream());
boolean b=dis.readBoolean();
System.out.println(b);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
*服务器端
*/
public class LoginServer {
public static final int SERVER_PORT=1222;
public static void main(String[] args) {
try {
//指定端口提供创建连接服务
ServerSocket ss=new ServerSocket(SERVER_PORT);
System.out.println("等待客户端的连接。。。");
//阻塞方法等待客户端的连接
Socket socket=ss.accept();
System.out.println("有连接过来:"+socket);
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str=br.readLine();
System.out.println("客户端发送的内容"+str);
//解析str
String name=str.split("&")[0].split("=")[1];
String pwd=str.split("&")[1].split("=")[1];
System.out.println("name="+name);
System.out.println("pwd="+pwd);
boolean result=false;
if("maomao".equals(name)&&"123456".equals(pwd)){
result=true;
}
DataOutputStream dos=new DataOutputStream(socket.getOutputStream());
dos.writeBoolean(result);
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
2. 文件上传
import java.io.*;
import java.net.Socket;
/**
* 客户端上传文件
*/
public class UploadClient {
public static void main(String[] args) {
Socket socket=null;
try {
socket=new Socket("192.168.151.182", UpLoadSever.SEVER_PORT);
File file=new File("E:/1.txt");
//先将文件的名字和长度通知给服务器
String name=file.getName();
long len=file.length();
DataOutputStream dos=new DataOutputStream(socket.getOutputStream());
dos.writeUTF(name);
dos.writeLong(len);
dos.flush();
//使用字节流读取本地的文件数据,数据通过输出流发送给服务器
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos=new BufferedOutputStream(socket.getOutputStream());
byte[] buf=new byte[100];
int count=bis.read(buf);
while(count!=-1){
bos.write(buf,0,count);
bos.flush();
count=bis.read(buf);
}
//接收服务器反馈
DataInputStream dis=new DataInputStream(socket.getInputStream());
System.out.println(dis.readUTF());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//关闭套接字
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
*服务器下载文件
*/
public class UpLoadSever {
public static final int SEVER_PORT =1222;
public static void main(String[] args) {
ServerSocket ss=null;
try {
ss=new ServerSocket(SEVER_PORT);
Socket socket=ss.accept();
//接收客户端发送的名字和文件长度
DataInputStream dis=new DataInputStream(socket.getInputStream());
String str=dis.readUTF();
long len=dis.readLong();
//使用socekt输入流读取客户端发过来的文件数据
BufferedInputStream bis=new BufferedInputStream(socket.getInputStream());
//再用字节输出流写到本地
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("E:/sever/"+str));
byte[] buf=new byte[10];
int count=bis.read(buf);
while(true){
bos.write(buf,0,count);
bos.flush();
len -= count;
if(len==0){
break;
}
count=bis.read(buf);
}
//给客户反馈
DataOutputStream dos=new DataOutputStream(socket.getOutputStream());
dos.writeUTF("文件长传成功");
dos.flush();
socket.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3. 多线程网络编程
public class UploadServer {
public static final int SERVER_PORT = 1777;
//给服务器上传一个文件
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(SERVER_PORT);
try {
while(true){
//主线程来响应客户端的请求
Socket socket = ss.accept();
new UploadThread(socket).start();
}
} finally {
ss.close();
}
}
static class UploadThread extends Thread{
private Socket socket;
UploadThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
//接收客户端发送的名字
DataInputStream dis = new DataInputStream(socket.getInputStream());
String name = dis.readUTF();
long len = dis.readLong();
if(len > 1024*1024*12)
return;
//使用socket 的输入流读取客户端发送过来的文件的数据。
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
String hostAddress = socket.getInetAddress().getHostAddress();
//在用字节输出流写出到本地。
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("c:/server/"+hostAddress+name));
byte[] buf = new byte[100];
//读取的是客户端发送过来的文件的数据
int count = bis.read(buf);
// while(count != -1){
while(true){
bos.write(buf,0,count);
bos.flush();
len -= count;
if(len == 0)
break;
count = bis.read(buf);
}
//给客户端反馈
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF("文件上传成功!");
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
4.总结 网络编程
网络通信三要素:ip地址 决定了和网络中的哪个计算机通信; 端口号:决定了和计算机中的哪个软件通信; 通信协议:信息通信的规则。
TCP:长连接.
InetAddress 封装的是主机名+IP地址。
InetSocketAddress:封装的是IP地址+端口号。
DatagramSocket 使用底层的UDP协议。
Socket 使用底层 TCP协议。