## Java网络编程
### java网络编程基础知识
### 1、协议(TCP/IP)
TCP/IP(Transmission Control Protocol/Internet Protocol)的简写,中文译名为传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议是Internet最基本的协议、Internet国际互联网络的基础,简单地说,就是由网络层的IP协议和传输层的TCP协议组成的。
### TCP/IP协议的由来
在阿帕网(ARPR)产生运作之初,通过接口信号处理机实现互联的电脑并不多,大部分电脑相互之间不兼容,在一台电脑上完成的工作,很难拿到另一台电脑上去用,想让硬件和软件都不一样的电脑联网,也有很多困难。当时美国的状况是,陆军用的电脑是DEC系列产品,海军用的电脑是Honeywell中标机器,空军用的是IBM公司中标的电脑,每一个军种的电脑在各自的系里都运行良好,但却有一个大弊病:不能共享资源。
### 互联网之父--瑟夫(Vinton G.Cerf)
1997年,为了褒奖对因特网发展作出突出贡献的科学家,并对TCP/IP协议作出充分肯定,美国授予为因特网发明和定义TCP/IP协议的文顿·瑟夫和卡恩“国家技术金奖”。这无疑使人们认识到TCP/IP协议的重要性。
![](https://box.kancloud.cn/2016-02-25_56ceb3e408e4b.jpg)
### tcp/ip基础--ip地址与包的路由传递
ip地址
概述:每个internet上的主机和路由器都有一个ip地址,它包括网络号和主机号,所有ip地址都是32位的,ip地址按照国际标准的划分为a,b,c,d,e五种类型。
### 端口(port)--概念
在网络技术中,端口(Port)有好几种意思。集线器、交换机、路由器的端口指的是连接其他网络设备的接口,如RJ-45端口、Serial端口等。这里所指的端口不是指物理意义上的端口,而是特指TCP/IP协议中的端口,是逻辑意义上的端口。
如果把IP地址比作一间房子,端口就是出入这间房子的门。真正的房子只有几个门,但是一个IP地址的端口可以有65536(即:256*256)个之多!端口是通过端口号来标记的,端口号只有整数,范围是从0到65535(256*256-1)。
### 端口(port)--分类
有65536个端口0号是保留端口
1-1024是固定端口
又叫有名端口,即被某些程序固定使用,一般程序员不使用。
22:SSH远程登录协议 23:telnet使用 21:ftp使用
25:smtp服务使用 80:iis使用 7:echo服务
1025-65535是动态端口
这些端口,程序员可以使用
### 端口(port)--注意事项
1、在计算机(尤其是做服务器)要尽可能的少开端口;
2、一个端口只能被一个程序监听;
3、如果使用netstat -an可以查看本机有哪些端口在监听
4、可以使用netstat -anb来查看监听端口的pid,在结合任务管理器关闭不需要的端口。
### url--概念
统一资源定位符(URL,Uniform Resource Locator的缩写)也被称为网页地址,是因特网上标准的资源的地址。它最初是由蒂姆·伯纳斯-李发明用来作为万维网的地址的。现在它已经被万维网联盟编制为因特网标准RFC1738了。
Internet上的每一个网页都具有一个唯一的名称标识,通常称之为URL地址,这种地址可以是本地磁盘,也可以是局域网上的某一台计算机,更多的是Internet上的站点,简单地说,URL就是Web地址,俗称“网址”,是用于完整地描述Internet上网页和其他资源的地址的一种标识方法。
### url--组成
http://www.sina.com:8080/index.html
1、协议;2、ip地址(32位);3、端口号(16位)0-65535;4、资源名称。
### 单工、半双工和全双工
如果甲可以向乙发送数据,但是乙不能向甲发送数据,这样的通信就是单工通信(Simplex Communication)。
单工数据传输只支持数据在一个方向上传输,就和传呼机一样。
半双工数据传输允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信,就和对讲机一样;
全双工数据通信允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力,就和电话一样。
![](https://box.kancloud.cn/2016-02-25_56ceb3e418941.jpg)
####
#### 案例一:半双工
##### Server1.java
~~~
/**
* 半双工
* 服务器端,9999端口监听
* 可以接收从客户端发来的信息
*/
package com.net.server;
import java.io.IOException;
import java.net.*;
import java.io.*;
public class Server1 {
ServerSocket ss = null;
Socket s = null;
// 构造方法
public Server1() {
try {
// 在9999端口进行监听
ss = new ServerSocket(9999);
// 等待某个客户端来连接,该方法会返回一个Socket连接
s = ss.accept();
// 读取s中传递的数据
InputStreamReader isr = new InputStreamReader(s.getInputStream());
BufferedReader br = new BufferedReader(isr);
String info = br.readLine();
System.out.println("服务器接收到了" + info);
PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
pw.println("Hello,Client!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (s != null) {
s.close();
}
if (ss != null) {
ss.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Server1 s1 = new Server1();
}
}
~~~
##### Client1.java
~~~
/**
* 客户端
*/
package com.net.client;
import java.io.IOException;
import java.net.*;
import java.io.*;
public class Client1 {
Socket s = null;
//构造方法
public Client1(){
try {
//Socket()方法可以连接某个服务器,127.0.0.1表示服务器的IP,9999是该服务器的端口号
s = new Socket("127.0.0.1", 9999);
//连接成功,就可以发送数据给服务器
//通过PrintWriter向s写数据,true表示即时刷新
PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
pw.println("Hello,Server!");
InputStreamReader isr = new InputStreamReader(s.getInputStream());
BufferedReader br = new BufferedReader(isr);
String resp = br.readLine();
System.out.println("客户端接收到了" + resp);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(s != null){
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Client1 c1 = new Client1();
}
}
~~~
#### 案例二:半双工聊天
##### Server2.java
~~~
/**
* 半双工
* 服务器端,9999端口监听
* 可以接收从客户端发来的信息
*/
package com.net.server;
import java.net.*;
import java.io.*;
public class Server2 {
ServerSocket ss = null;
Socket s = null;
BufferedReader br1 = null;
BufferedReader br2 = null;
PrintWriter pw = null;
// 构造方法
public Server2() {
try {
// 在9999端口进行监听
ss = new ServerSocket(9999);
// 等待某个客户端连接,返回 Socket
s = ss.accept();
while (true) {
// 从客户端接收消息
br1 = new BufferedReader(new InputStreamReader(
s.getInputStream()));
String str = br1.readLine();
System.out.println("从客户端收到的消息是:" + str);
// 如果收到消息为"byebye",关闭服务器
if (str.equals("byebye")) {
System.out.println("服务器关闭连接");
break;
}
// 从控制台输入
System.out.print("从控制台输入:");
br2 = new BufferedReader(new InputStreamReader(System.in));
// 将消息发送给客户端
pw = new PrintWriter(s.getOutputStream(), true);
pw.println(br2.readLine());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 关闭资源
if (pw != null) {
pw.close();
}
if (br2 != null) {
br2.close();
}
if (br1 != null) {
br1.close();
}
if (s != null) {
s.close();
}
if (ss != null) {
ss.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Server2 s2 = new Server2();
}
}
~~~
##### Client2.java
~~~
/**
* 客户端
*/
package com.net.client;
import java.net.*;
import java.io.*;
public class Client2 {
Socket s = null;
PrintWriter pw = null;
BufferedReader br1 = null;
BufferedReader br2 = null;
public Client2() {
try {
// Socket()方法可以连接某个服务器,127.0.0.1表示服务器的IP,9999是该服务器的端口号
s = new Socket("127.0.0.1", 9999);
while (true) {
// 连接成功,就可以发送数据给服务器
// 从控制台读入数据
System.out.print("从控制台输入:");
br1 = new BufferedReader(new InputStreamReader(System.in));
String str = br1.readLine();
// 通过PrintWriter向s写数据,true表示即时刷新
pw = new PrintWriter(s.getOutputStream(), true);
pw.println(str);
if (str.equals("byebye")) {
System.out.println("客户端关闭连接");
break;
}
// 从服务端接收消息
br2 = new BufferedReader(new InputStreamReader(
s.getInputStream()));
System.out.println("从服务器接收的消息是:" + br2.readLine());
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 关闭资源
if (pw != null) {
pw.close();
}
if (br2 != null) {
br2.close();
}
if (br1 != null) {
br1.close();
}
if (s != null) {
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Client2 c2 = new Client2();
}
}
~~~
#### 案例三:全双工聊天
##### Server3.java
~~~
/**
* 全双工
* 监听端口6166
*/
package com.net.server;
import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.RenderingHints.Key;
import javax.swing.*;
import java.awt.event.*;
public class Server3 extends JFrame implements ActionListener, KeyListener {
// 网络变量
ServerSocket ss = null;
Socket s = null;
// IO流
BufferedReader br = null;
PrintWriter pw = null;
// 定义组件
JTextArea jta = null;
JTextField jtf = null;
JButton jb = null;
JPanel jp1 = null;
JScrollPane jsp = null;
// 构造方法
public Server3() {
// 创建组件
jta = new JTextArea();
jsp = new JScrollPane(jta);
jtf = new JTextField(20);
jtf.addKeyListener(this);
jb = new JButton("发送");
jb.addActionListener(this);
jp1 = new JPanel();
// 添加到JPanel
jp1.add(jtf);
jp1.add(jb);
// 添加到JFrame
add(jsp, "Center");
add(jp1, "South");
// 设置窗体
setTitle("服务器端");
setSize(400, 300);
setLocation(0, 100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
try {
// 服务器监听
ss = new ServerSocket(6166);
// 等待客户端连接
s = ss.accept();
// 创建读入缓冲字符流
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
// 创建打印流
pw = new PrintWriter(s.getOutputStream(), true);
while (true) {
// 读取从客户端发来的信息
// 当没有消息过来时,会一直处于阻塞状态,这条语句也就不会执行
String info = br.readLine();
jta.append("客户端:" + info + "\r\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server3 s3 = new Server3();
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == jb) {
// 点击确定,发送信息
// 把服务器在jtf写的内容发送给客户端,并在jta显示
String info = jtf.getText();
pw.println(info);
jta.append("服务器:" + info + "\r\n");
jtf.setText("");
}
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
// 按下Enter,发送信息
// 把服务器在jtf写的内容发送给客户端,并在jta显示
String info = jtf.getText();
pw.println(info);
jta.append("服务器:" + info + "\r\n");
jtf.setText("");
}
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
~~~
##### Client3.java
~~~
/**
* 客户端
*/
package com.net.client;
import java.net.*;
import java.io.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Client3 extends JFrame implements ActionListener, KeyListener {
// 网络变量
Socket s = null;
// IO流
BufferedReader br = null;
PrintWriter pw = null;
// 定义组件
JTextArea jta = null;
JTextField jtf = null;
JButton jb = null;
JPanel jp1 = null;
JScrollPane jsp = null;
// 构造方法
public Client3() {
// 创建组件
jta = new JTextArea();
jsp = new JScrollPane(jta);
jtf = new JTextField(20);
jtf.addKeyListener(this);
jb = new JButton("发送");
jb.addActionListener(this);
jp1 = new JPanel();
// 添加到JPanel
jp1.add(jtf);
jp1.add(jb);
// 添加到JFrame
add(jsp, "Center");
add(jp1, "South");
// 设置窗体
setTitle("客户端");
setSize(400, 300);
setLocation(500, 100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
try {
// Socket()方法可以连接某个服务器,127.0.0.1表示服务器的IP,6166是该服务器的端口号
s = new Socket("127.0.0.1", 6166);
// 创建读入缓冲字符流
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
// 创建打印流
pw = new PrintWriter(s.getOutputStream(), true);
while (true) {
// 读取从服务器发来的消息
// 当没有消息过来时,会一直处于阻塞状态,这条语句也就不会执行
String info = br.readLine();
jta.append("服务器:" + info + "\r\n");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 关闭资源
if (pw != null) {
pw.close();
}
if (br != null) {
br.close();
}
if (s != null) {
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Client3 c3 = new Client3();
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == jb) {
// 如果点击确定按钮
// 发送给服务端,并更新jtf,并在jta显示
String info = jtf.getText();
pw.println(info);
jta.append("客户端:" + info + "\r\n");
jtf.setText("");
}
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
// 按下Enter,发送信息
// 把服务器在jtf写的内容发送给客户端,并在jta显示
String info = jtf.getText();
pw.println(info);
jta.append("服务器:" + info + "\r\n");
jtf.setText("");
}
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
~~~
#### 案例四:在网络间传递对象
##### User.java
~~~
/**
* 对象流所用对象
* 必须序列化
*/
package com.objectstream;
import java.io.*;
public class User implements Serializable {
private String name;
private String pass;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
}
~~~
##### Server1.java
~~~
/**
* 对象流
* 服务器
* 监听端口1234
*/
package com.objectstream;
import java.net.*;
import java.io.*;
public class Server1 {
ServerSocket ss = null;
Socket s = null;
ObjectInputStream ois = null;
// 构造方法
public Server1() {
try {
ss = new ServerSocket(1234);
s = ss.accept();
System.out.println("在1234端口监听");
// 以对象流方式读取(假定客户端发送的是User的一个对象)
ois = new ObjectInputStream(s.getInputStream());
User u = (User) ois.readObject();
// 输出
System.out.println("从客户端接收到了" + u.getName() + " " + u.getPass());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (ois != null) {
ois.close();
}
if (s != null) {
s.close();
}
if (ss != null) {
ss.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Server1 s1 = new Server1();
}
}
~~~
##### Client1.java
~~~
/**
* 客户端
*/
package com.objectstream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client1 {
Socket s = null;
ObjectOutputStream oos = null;
// 构造方法
public Client1() {
try {
s = new Socket("127.0.0.1", 1234);
// 通过ObjectOutputStream给服务器传递对象
oos = new ObjectOutputStream(s.getOutputStream());
User u = new User();
u.setName("张三");
u.setPass("123456");
oos.writeObject(u);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (oos != null) {
oos.close();
}
if (s != null) {
s.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Client1 c1 = new Client1();
}
}
~~~
特别说明:
在java网络编程要通过IO流实现对象的传递,需要对服务端和客户端定义相同的对象,以达到实现对象的传递。同时需要类进行Serializable序列化,否则报错。
----------参考《韩顺平.循序渐进学.java.从入门到精通》
----------参考《JDK_API_1_6_zh_CN》
Java学习笔记--导航[http://blog.csdn.net/q547550831/article/details/49819641](http://blog.csdn.net/q547550831/article/details/49819641)