JAVA [ TCP/IP ]

来源:互联网 发布:淘宝给予警告会降权吗 编辑:程序博客网 时间:2024/06/06 04:37

 应用层:应用程序间沟通的层,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。 
  传输层:在此层中,它提供了节点间的数据传送,应用程序之间的通信服务,主要功能是数据格式化、数据确认和丢失重传等。如传输控制协议(TCP)、用户数据报协议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。 
  连网络层:负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如网际协议(IP)


TCP/IP协议(Transmission Control Protocol/Internet Protocol)叫做传输控制/网际协议.

TCP/IP 的工作原理 -->本文采用TCP/IP协议传送文件为例,说明TCP/IP的工作原理,其中应用层传输文件采用文件传输协议(FTP)。 

TCP/IP协议的工作流程如下: 
●在源主机上,应用层将一串应用数据流传送给传输层。 
●传输层将应用层的数据流截成分组,并加上TCP报头形成TCP段,送交网络层。 
●在网络层给TCP段加上包括源、目的主机IP地址的IP报头,生成一个IP数据包,并将IP数据包送交链路层。 
●链路层在其MAC帧的数据部分装上IP数据包,再加上源、目的主机的MAC地址和帧头,并根据其目的MAC地址,将MAC帧发往目的主机或IP路由器。
●在目的主机,链路层将MAC帧的帧头去掉,并将IP数据包送交网络层。 
●网络层检查IP报头,如果报头中校验和与计算结果不一致,则丢弃该IP数据包;若校验和与计算结果一致,则去掉IP报头,将TCP段送交传输层。 
●传输层检查顺序号,判断是否是正确的TCP分组,然后检查TCP报头数据。若正确,则向源主机发确认信息;若不正确或丢包,则向源主机要求重发信息。 
●在目的主机,传输层去掉TCP报头,将排好顺序的分组组成应用数据流送给应用程序。这样目的主机接收到的来自源主机的字节流,就像是直接接收来自源主机的字节流一样。 

/**
 * 127.0.0.1没有于网卡绑定,当网卡坏了时,仍可测试使用本地网络程序
 * IP地址保证数据传送到计算机,但是不能确定是哪个网络程序,需要使用PORT,两个字节的整数
 * PORT 0~1023用于知名的网络服务和应用,端口号在0~65535之间
 * 
 * 高级协议 UDP TCP
 *  TCP:传输控制协议,面向连接的通信协议,打电话
 *  UDP:用户数据报协议,一个主机向另一个主机发送,它不管另个主机是否启动,另一个主机接收到信息后不会回应,像是传呼台,一般发送几遍
 *  
 *  数据帧格式
 *  协议类型 | 源IP | 目标IP | 源端口 | 目标端口 | 帧序号 | 帧数据
 *  1hello
 *  2   world
 * Socket是网络驱动层提供给应用程序编程的接口和一种机制
 * 类似港口码头,应用程序    把       货物       放到港口码头,完成了货物的运货
 * 应用程序 只需等待       货物      到达码头,将        货物取走
 *  
 *  
 *  1 产生Socket
 *                      应用程序-----------------------------
 *                      | ^|
 *                      2| ||
 *                      | ||
 *            调用bind将   | |  4应用程序从Socket中取数据  |
 *            Socket的信息| --------------------------- Socket
 *            通知给驱动程序| -------------------------->
 *                      | |     3 驱动程序根据从网卡传送来的
 *                      | |数据包中指定目标端口号,将处理后的数据传送到
 *                      | |         相应的socket中
 *                          | |
 *                      驱动程序
 *  
 *  ServerSocket 用于TCP通信的服务器
 *  DatagramDocket类用于UDP通信
 *  Scoket类用于TCP通信的服务器和客户端
 *  
 */
public class InetAddressExample {
public static void main(String[] args){
try{
//该主机每一个接口所以对应的NetworkInterface类实例
Enumeration<NetworkInterface> interfaceList = NetworkInterface.getNetworkInterfaces();
if(interfaceList == null){
System.out.println("--NO interfaces found--");
}else{
while(interfaceList.hasMoreElements()){
NetworkInterface iface = interfaceList.nextElement();
//返回一个本地名称
System.out.println("Interface " + iface.getName() + ":");
//获取与接口关联的地址
Enumeration<InetAddress> addrList = iface.getInetAddresses();
if(!addrList.hasMoreElements()){
System.out.println("\t(No addresses for this interface)");
}
while(addrList.hasMoreElements()){
InetAddress address = addrList.nextElement();
System.out.println("\tAddress"
+ ((address instanceof Inet4Address? "(v4)"
: (address instanceof Inet6Address? "(v6)" : "?"))));
System.out.println("\t\t:" + address.getHostName());
System.out.println("\t\t:" + address.getHostAddress());
}
}
}
}catch(SocketException se){
System.out.println("Error getting network interfaces:" + se.getMessage());
}
}
}

/**
 * 
 *  TCP网络程序的工作原理
 *  UDP么有主从之分,两个可以是完全应用实例
 *  
 *  为Client创建的Socket
 *  两个Socket之间建立专线连接服务器接受请求并创建Socket
 *  |^
 *  |客户端发出连接|
 *  Client Socket [simple phone]  -------------------------------> ServerSockt  [114]
 *  
 *  ServerSocket类
 *  public ServerSocket()
 *  public ServerSocket(int port) 
 *  public ServerSocket(int port, int backlog)  //保持连接请求的客户数量
 *  public ServerSocket(int port, int backlog, InetAddress bindAddr)
 *  close()
 *  accpet() //返回建立专线连接的对象
 *  
 *  
 *  Socket类
 *  public Socket()  //轮循
 *  public Socket(String host, int port)
 *  public Socket(InetAddress address, int port)
 *  public Socket(String host, int port, InetAddress address, int localport)  
 *  //客户机有多快网卡,通过哪块网卡连接服务器
 *  
 *  网络字节流形式,数据交换
 *  getInputStream和getOutputStream
 *  
 *  简单的TCP服务器程序
 *  TCP服务器程序先运行,客户端才能连接上
 *  使用Windows提供的telnet程序测试
 *  使用BufferedReader包装类,从网络输入流中一次读取一行文本
 *  如何打开telent程序的本地回显文本
 * 
 * @author ShenJie
 *
 */

public class TCPDemo1 {
public static void main(String[]args) throws Exception{
ServerSocket ss = new ServerSocket(8001);
Socket s = ss.accept();
InputStream ips = s.getInputStream();
OutputStream ops = s.getOutputStream();

ops.write("welcome to SHENJIE".getBytes());

//byte[] buf = new byte[1024];
//int len = ips.read(buf);
//System.out.println(new String(buf, 0 , len));


BufferedReader br = new BufferedReader(new InputStreamReader(ips));
System.out.println(br.readLine());
br.close();

//关闭顺序
ips.close();
ops.close();
s.close();
ss.close();

//用telnet实验
//telnet localhost 8001
//telnet > ?
//telnet > set ?

}
}

/**
 *  
 *  
 *  完善的TCP服务器程序模型
 *  同时与多个客户端会话,
 *  需要循环调用accept方法,会话不鞥你影响,需要独立运行
 *  客户端每次向服务器发送一行字符文本,服务器将这行字符文本中的所有字符反向排列送回客户端
 *  当客户端发送“quit”时,服务器结束与客户端的会话
 *  
 *  TCP客户端
 * 
 * @author ShenJie
 *
 */

public class TCPDemo2 {
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(8001);
boolean bRunning = true;
//会话过程
while(bRunning){
Socket s = ss.accept();
//每个会话启动一个线程
new Thread(new Servicer(s)).start();
}
ss.close();
}
}
class Servicer implements Runnable{
Socket s;
public Servicer(Socket s){
this.s = s;
}
@Override
public void run() {
InputStream ips;
try {
ips = s.getInputStream();
OutputStream ops = s.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(ips));
//true自动刷新缓冲区
PrintWriter pw = new PrintWriter(new OutputStreamWriter(ops),true);
//PrintStream 都是产生\n
while(true){
String strLine = br.readLine();
if(strLine.equalsIgnoreCase("quit")){
break;
}
System.out.println(strLine + strLine.length());
//abd{backspace}c
//c{backspace}dba
//abc --- > abd

String strEcho  = new StringBuffer(strLine).reverse().toString();
pw.println(strLine + " ----> " + strEcho);
}
br.close();
pw.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

/**
 * 
 *  如何检测和解决端口冲突问题
 *  netstat
 *  通过TCP程序在网络上传送对象
 * @author ShenJie
 */

public class TCPDemo3 {
public static void main(String[] args) throws Exception, IOException{
Socket s = new Socket("localhost", 9999);
InputStream ips = s.getInputStream();
OutputStream ops = s.getOutputStream();

BufferedReader brNet = new BufferedReader(new InputStreamReader(ips));
//true自动刷新缓冲区
PrintWriter pw = new PrintWriter(ops,true);

BufferedReader brKeyBoard = new BufferedReader(new InputStreamReader(System.in));
while(true){
String strWord = brKeyBoard.readLine();
pw.println(strWord);
if(strWord.equalsIgnoreCase("quit")){
break;
}
System.out.println(brNet.readLine());
}
pw.close();
brNet.close();
ops.close();
ips.close();
s.close();
}
}

/**
 * ObjectInputStream 和 ObjectOutputStream从底层输入流中读取对象类型,以及写入
 * 
 * 应用通信协议和网络通信协议的关系
 * 
 * 电话系统tcp 语言ftp
 * 
 * @author ShenJie
 *
 */

public class TCPDemo4 {
public static void main(String[] args) throws Exception{
ServerSocket ss =new ServerSocket(9999);
Socket s = ss.accept();
OutputStream ops = s.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(ops);
Student stu = new Student(19,"wangwu", 22 ,"hunan");
oos.writeObject(stu);
oos.close();
ops.close();
s.close();
ss.close();
}
}
class ObjectClient{
public ObjectClient() throws Exception{
Socket s = new Socket("localhost", 9999);
InputStream ips = s.getInputStream();
ObjectInputStream ois = new ObjectInputStream(ips);

Student stu = (Student)ois.readObject();
System.out.print("id is =" + stu.id);
System.out.print("name is =" + stu.name);
System.out.print("age is =" + stu.age);
System.out.print("department is =" + stu.department);

ois.close();
ips.close();
s.close();
}
}
class Student implements Serializable{
int id;
String name;
int age;
String department;

public Student(int id, String name, int age, String department){
this.id =id; this.name =name; this.age= age; this.department=department;
}
}

****************************************

int recvMsgSize;
byte[] reveiveBuf = new byte[BUFSIZE];

//接收并复制数据,直到客户端关闭
while((recvMsgSize = in.read(reveiveBuf)) != -1){
out.write(reveiveBuf,0,recvMsgSize);
}

***************************************************

/**

*  简易 TCPEchoServerPool

*/

public class TCPEchoServerPool {
private static final int BUFSIZE =32;
public static void main(String[] args) throws IOException{
int servPort = 9999;
int threadPoolSize = 20;
final ServerSocket servSock = new ServerSocket(servPort);

for(int i=0;i<threadPoolSize;i++){
Thread thread = new Thread(){
public void run(){
while(true){
try{
Socket clntSock = servSock.accept();
//handleEchoClient
}catch(IOException e){

}
}
}
};
thread.start();
}
}
}



/**
 * 港口码头
 * public DatagramSocket() 系统自动分配端口号:例如给别人打电话,自己的号码不固定
 * public DatagramSocket(int port)  :例如要朋友给自己打电话
 * public DatagramSocket(int port, InetAddress Iaddr)   多个ip地址上运行,明确指定发送接收的IP地址
 * 
 * 接收和发送数据的集装箱
 * public DatagramPacket(byte[] buf,int length)   接收数据
 * public DatagramPacket(byte[] buf,int length, InetAddress address,int port)  发送数据,需要有接收方的地址信息 
 * 
 * getInetAddress 和 getPort方法可以获得发送方的地址信息
 * getData 和 getLength方法  == packet中字节数组缓冲区,packet中实际收到的数据长度
 * 
 * 数据包定位1024个字节,发送不能多于这个
 * 
 * InetAddress是表示计算机ip地址的一个类,一般计算机地址用"192.168.0.1"和"www.it315.org"表示
 * getByName方法返回一个InetAddress实例对象
 * getHostAddress返回 类似"192.168.0.1"的地址
 * 
 * 字符串与字节数组之间双向转换
 * UDP接收程序先启动运行,才能接收UDP发送程序发送的数据
 * CMD用start命令打开新命令行窗口的好处,继承环境
 * 解决发送中文字符串的问题
 * 
 * @author ShenJie
 *
 */

public class UDPDemo1 {
public static void main(String[] args){
}
}
//数据一发完就结束了
class UdpSend{
public static void main(String[] args){
DatagramSocket ds = null;
try {
ds = new DatagramSocket();
String strInfo = "Hello, this is Jesse!";
//String strInfo = "用中文的字符!";
try {
ds.send(new DatagramPacket(strInfo.getBytes(),strInfo.length(),3000));
//中文时
//ds.send(new DatagramPacket(strInfo.getBytes(),strInfo.getBytes().length(),3000));

} catch (IOException e) {
e.printStackTrace();
}
ds.close();
} catch (SocketException e) {
e.printStackTrace();
}
}
}
//接收程序开着,一直阻塞
class UdpRecv{
public static void main(String[] args){
DatagramSocket ds = null;
try {
ds = new DatagramSocket(3000);
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, 1024);
ds.receive(dp);
//接收实际收到的数据个数
dp.getAddress();//获得对方的发送ip地址
System.out.println(new String(dp.getData(),0,dp.getLength())
+ "from " + dp.getAddress().getHostAddress() + ":" + dp.getPort());
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

/**
 * 多线程和GUI
 * 1. 编写图形用户界面
 * 2. 编写网络消息发送功能
 * 3. 编写网络消息接收功能
 *
 *  只有udp才能发送和接收广播地址
 *  广播地址需要计算
 *  可以放在INternet上使用,每台计算机需要有合法的IP地址,本来就能合法访问
 *  
 *  *******************************************************
 *  使用私有IP并通过网关代理上网的计算机不能
 *  
 *    192.168.0.2
 *        192.168.0.3  [内部网络没有合法地址]
 *        192.168.0.4
 *        192.168.0.5
 *                  ----------------------------交换机----------------------------
 *                  电话,isdn,cable,光纤专线连接
 *                  获得合法有效的IP地址 166.111.111.10
 *                  可以让Internet计算机访问到他
 *                  如果打开IP网络转发功能,内部网络其他计算机网关地址设置为这台机器的内部网络地址
 *                      |
 *                  |
 *                  |
 *                  |
 *                  internet-------------------------> 221.201.121.57
 *                  
 *  192.168.0.2 发送到  221.201.121.57, 先将数据发送到网关上
 *  数据格式为
 *     1.     192.168.0.2      221.201.121.57  3000   3000   hello
 *     2.     166.111.111.10   221.201.121.57  1027   3000   hello
 *     3.     221.201.121.57   166.111.111.10  3000   1027   world
 *     4.     221.201.121.57   192.168.0.2     3000   3000   world
 *     
 *     
 *  转发映射记录表
 *       192.168.0.2/3000    166.111.111.10/1027
 *       192.168.0.3/3000    166.111.111.10/1028
 *       
 *  *******************************************************
 */

public class UDPDemo2 extends JFrame{
List lst = new List();
JTextField tfIP = new JTextField(15);
JTextField tfData = new JTextField(20);
DatagramSocket ds = null;

public UDPDemo2(){
try {
ds =new DatagramSocket(3000);
} catch (SocketException e) {
e.printStackTrace();
}
this.add(lst, "Center");
Panel p =new Panel();
add(p, "South");

p.setLayout(new BorderLayout());
p.add(tfIP,"West");
p.add(tfData,"East");

//线程类接收网络上发送的消息
new Thread(new Runnable(){
@Override
public void run() {
byte[]  buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, 1024);
while(true){
try {
ds.receive(dp);
} catch (IOException e) {
/**
* 当关闭的时候不发生异常
*/

if(!ds.isClosed())
e.printStackTrace();
}
//lst一般显示在最后一行,希望显示在第一行
//lst.add("");

lst.add(new String(/*dp.getData()*/buf, 0 , dp.getLength())
+ " from " + dp.getAddress().getHostAddress() + ":" + dp.getPort(),0);
}
}

}).start();

tfData.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
byte[] buf = tfData.getText().getBytes();
DatagramPacket dp;
try {
dp = new DatagramPacket(buf, buf.length,
InetAddress.getByName(tfIP.getText()), 3000);
try {
ds.send(dp);
} catch (IOException ex) {
ex.printStackTrace();
}
} catch (UnknownHostException ex) {
ex.printStackTrace();
}
tfData.setText("");
}
});
addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
ds.close();
dispose();
System.exit(0);
}
});
setTitle("UDP\u5355\u673a\u7f51\u7edc\u804a\u5929\u5de5\u5177");
}
public static void main(String[] args){
UDPDemo2 ud = new UDPDemo2();
ud.setSize(380,500);
ud.setVisible(true);
}
}

原创粉丝点击