黑马程序员------网络编程复习笔记

---------------------- android培训java培训、期待与您交流! ----------------------

网络编程复习笔记

UDP和TCP

TCP协议提供了一种可靠的数据传输服务,它是一种面向连接的数据传输协议。在数据传输之前,通信节点之间必须建立起连接。为确保正确地接收数据,TCP协议要求在目标电脑成功收到数据时发回一个确认(即ACK)。如果在某个时限内未收到相应的ACK,将重新传送数据包。如果网络拥塞,这种重新传送将导致发送的数据包重复。但是,接收电脑可使用数据包的序号来确定它是否为重复数据包,并在必要时丢弃它。UDP协议是一种面向无连接的数据传输服务,它不能保证数据包以正确的顺序被接收。该协议不能保证数据准确无误地到达目的地。UDP在许多方面非常有效。当某个程序的目标是尽快地传输尽可能多的信息时(其中任意给定数据的重要性相对较低),可使用UDP协议。QQ、ICQ等聊天软件使用UDP协议发送消息。

UDP编程:

1.      DatagramSocket类

此类表示用来发送和接收数据报包的套接字。

主要的构造函数:

public DatagramSocket()
       构造数据报套接字并将其绑定到本地主机上任何可用的端口。
public DatagramSocket(int port)

创建数据报套接字并将其绑定到本地主机上的指定端口。

public DatagramSocket(int port,InetAddress laddr)

    创建数据报套接字,将其绑定到指定的本地地址。

当作为发送端时,可以不用参数,选择第一种构造方法,即无参数的构造方法。有多个IP地址时,应该使用第三个构造方法,指定发送端的IP。

常用的方法:

close() 关闭此数据报套接字。

send(DatagramPacket p)   从此套接字发送数据报包。

receive(DatagramPacket p)   从此套接字接收数据报包。

2.      DatagramPacket类

此类表示数据报包。数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。

主要的构造函数:

public DatagramPacket(byte[] buf, int length)

        构造 DatagramPacket,用来接收长度为 length 的数据包。

Public DatagramPacket(byte[] buf,int length,InetAddress address,int port)

       构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。length 参数必须小于等于 buf.length。 参数: buf - 包数据。 length - 包长度。 address - 目的地址。 port - 目的端口号。

         当DatagramPacket类作为发送端的时候,需要选择第二种构造方法,因为需要指定目的地的地址和端口号。而作为接收端的时候就不是那么的需要了。

常用的方法:

getAddress()  最好在接着调用getHostAddress()   返回 IP 地址字符串(以文本表现形式)。

getData()     返回数据缓冲区

getLength()   返回将要发送或接收到的数据的长度。

getPort()    返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。

setAddress(InetAddress iaddr)  设置要将此数据报发往的那台机器的 IP 地址。

setData(byte[] buf)  为此包设置数据缓冲区。

setLength(int length)  为此包设置长度

setPort(int iport)     设置要将此数据报发往的远程主机上的端口号。

3.      最简单的UDP程序:

字符串与字节数组之间的双向转换。

String str = “heima”;   byte [] buf = str. getBytes();

UDP接收程序必须先启动运行,才能接收UDP发送程序发送的数据。

用start命令来打开新命令行窗口的好处:新开启的一个命令行窗口,和原先的命令行具有相同的环境。

解决发送中文字符串的问题。

需要将含中文字符的字符串转换为字节数组了,再求其长度(发送的长度)。一个中文占2个字节,如果没有转换为字节数组,发送数据和包装数据的字节数组的长度不匹配。


一个简单的UDP聊天程序

package netUDPChat;

import java.awt.BorderLayout;
import java.awt.List;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class MainPanel extends JFrame {

	JTextField jtfMsg = new JTextField(22);
	JTextField jtfIP = new JTextField(22);
	List list = new List();
	DatagramSocket ds = null;
	
	public MainPanel(){
		setSize(500, 500);
		setTitle("聊天");
		this.setLocation(300, 100);
		
		JPanel jp = new JPanel();
		jp.setLayout(new BorderLayout());
		
		jp.add(jtfIP,BorderLayout.WEST);
		jp.add(jtfMsg,BorderLayout.EAST);
		this.add(list,BorderLayout.CENTER);
		this.add(jp,BorderLayout.SOUTH);
		//将窗口设置成不可改变大小的。
		this.setResizable(false);
		
		
		try {
			//注意这里的端口号必须和发送端的一致
			ds = new DatagramSocket(8000);
		} catch (SocketException e1) {
			e1.printStackTrace();
		}
		jtfMsg.addActionListener(new ActionListener(){

			@Override
			public void actionPerformed(ActionEvent e) {
				byte[] buf = jtfMsg.getText().getBytes();
				InetAddress ia;
				try {
					ia = InetAddress.getByName(jtfIP.getText());
					DatagramPacket dp = new DatagramPacket(buf,buf.length,ia,8000);
					ds.send(dp);
				} catch (Exception e1) {
					e1.printStackTrace();
				}
				//发送数据后将发送信息的文本框置空。
				jtfMsg.setText("");
			}
			
		});
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				ds.close();
				System.exit(0);
			}
		});
		
		
		new Thread(new Runnable(){
			@Override
			public void run() {
				byte [] buf = new byte[1024];
				DatagramPacket dp = new DatagramPacket(buf,buf.length);
				while(true){
					try {
						ds.receive(dp);
						String strInfo = new String(buf,0,dp.getLength()) + " from " +
						dp.getAddress().getHostAddress()+":" + dp.getPort();
						//让接收到得消息都显示在最上边。
						list.add(strInfo, 0);
					} catch (IOException e) {
						if(!ds.isClosed()){
							e.printStackTrace();
						}
					}
				}
			}
		}).start();
		
		setVisible(true);
	}
	
	
	public static void main(String[] args) {
		new MainPanel();
	}

}

UDP 服务器和客户端程序

package netSimpleUDP;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class MyReceiver {

	public static void main(String[] args) throws Exception{
		
		//作为接收端的时候 ,最好指定端口号
		DatagramSocket ds = new DatagramSocket(8888);
		
		byte []buf = new byte[1024];
		
		//创建接收端得数据报包,指定接受的长度
		DatagramPacket dp = new DatagramPacket(buf,buf.length);
		
		//接受数据报包
		ds.receive(dp);
		
		System.out.println(	new String(dp.getData(),0,dp.getLength())+
				dp.getAddress().getHostAddress()+ ":" + dp.getPort());
		//关闭数据报套接字
		ds.close();
	}

}

package netSimpleUDP;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class MySender {

	public static void main(String[] args) throws IOException {
		
		//创建发送端的数据报套接字
		DatagramSocket ds = new DatagramSocket();
		
		//需要发送的信息
		String strInfo = "Hello,jack!好样的!";
		
		/*
		 * 创建发送端的数据报包,包含包数据、包长度、目的地址、目的端口号
			需要将含中文字符的字符串转换为字节数组了,再求其长度.
			一个中文占2个字节,如果没有转换为字节数组,发送数据和包装数据的字节数组的长度不匹配。
		*/
		DatagramPacket dp = new DatagramPacket(strInfo.getBytes(),strInfo.getBytes().length,
				InetAddress.getByName("127.0.0.1"),8888);
		
		//发送数据报包
		ds.send(dp);
		
		//关闭数据报套接字
		ds.close();
	}
}

TCP编程:

TCP客户端程序与服务器端程序的交互过程:

(1)   服务器端创建一个ServerSocket,然后调用accept来等待客户端的连接

(2)   客户端创建一个Socket,并请求与服务器建立连接

(3)   服务器接受客户端的连接请求,并创建一个新的Socket与该客户建立专线连接。

(4)   建立了连接的两个Socket存在一个单独的线程(有服务器程序创建)上运行。

(5)   服务器开始等待新的连接请求。当新的连接请求开始时,重复(2)到步骤(5)的过程。

 

1.      ServerSocket类

主要的构造方法:

public ServerSocket()

创建非绑定服务器套接字。

public ServerSocket(int port)

创建绑定到特定端口的服务器套接字。端口0 在所有空闲端口上创建套接字。

accept()方法     侦听并接受到此套接字的连接。

close()方法        关闭此套接字。

 

2.      Socket类

主要的构造方法:

public Socket()

public Socket(InetAddress address,int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。

public InputStream getInputStream()返回此套接字的输入流。

public OutputStream getOutputStream()返回此套接字的输出流。

* 会使用telnet命令 

        * 当我们编写了服务器端程序的时候,我们没有必要马上编写客户端程序

        * 我们可以先用telnet命令测试下服务器端得程序是否正确。格式如下:

        * telnet IP地址   端口号

        *

* TCP服务器模型的编写要点

        * 1.TCP服务器程序要想接收多个客户端的连接,需要循环调用ServerSocket的accept方法

        * 2.服务器程序与每个客户端的会话过程不能互相影响,需要在独立的线程中运行

        * 3.一个线程服务对象与一个客户端Socket对象相关联,共同来完成与一个客户端的会话。

* 如何检测和解决端口冲突问题

使用netstat命令查看当前正在被使用的TCP端口号。  netstat –na

TCP上对象的传递

 

package ObjectTCP;

import java.io.Serializable;

/*
 * 用TCP传递的对象必须实现Serializable接口
 */

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

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getDepartment() {
		return department;
	}

	public void setDepartment(String department) {
		this.department = department;
	}
	
	
}

package ObjectTCP;

import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ObjectServer {

	/**
	 * 传递对象的服务器程序:
	 * 服务器端接收客户端传过来的对象。
	 */
	public static void main(String[] args) throws Exception{
			//创建ServerSocket对象
			ServerSocket ss = new ServerSocket(8002);
			
			//等待连接
			Socket s = ss.accept();
			
			InputStream in = s.getInputStream();
			
			//将字节输入流包装到对象输入流中
			ObjectInputStream ois = new ObjectInputStream(in);
			
			//读取对象
			Student stu = (Student) ois.readObject();
			
			//打印读取到得对象的信息
			System.out.println("id = " + stu.getId() + ",name= " + stu.getName() +
					",age= " + stu.getAge() + ",department= " + stu.getDepartment());
			
			//关闭对象输入流
			ois.close();
			
			//关闭Socket套接字
			s.close();
			
			//关闭服务器
			ss.close();
	}

}

package ObjectTCP;

import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;

public class ObjectClient {

	/**
	 * TCP传递对象的客户端程序:
	 * 客户端程序将对象传递给服务器端
	 */
	public static void main(String[] args) throws Exception{
		//建立连接
		Socket s = new Socket("127.0.0.1",8002);
		
		//获得字节输出流对象
		OutputStream out = s.getOutputStream();
		
		//创建Student对象
		Student stu = new Student(01,"jack",21,"Math");
		
		//用对象输出流包装字节输出流对象
		ObjectOutputStream oos = new ObjectOutputStream(out);
		
		//将Student对象写入到对象服务器端
		oos.writeObject(stu);
		
		//关闭对象输出流
		oos.close();
		
		//关闭Socket套接字
		s.close();
	}

}


 

URL(Uniform Resourse Locator) 统一资源定位符

 

URL的组成:协议名、主机名、端口号、资源名

如:http://locahost:8080/index.html

相对URL

Java.net包中提供了对URL进行编码和解码的类URLEncoder  和URLDecoder


TCP服务器端和客户端程序

package tcp;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {

	/**
	 * 实现功能:
	 * 编写TCP服务器程序能够与多个客户端会话,客户端每次向服务器发送一行文本
	 * 服务器就将这行文本中的所有字符反向排列后回送给客户端,
	 * 当客户端发送"quit"时,服务器结束与客户端的连接。
	 */
	
	public static void main(String[] args) {
		try {
			ServerSocket ss = null;
			
			/* 通过一个配置参数来指定TCP服务程序所使用的端口号
			if(args.length <1){
				
				ss = new ServerSocket(8001);
			}else{
				ss = new ServerSocket(Integer.parseInt(args[0]));
			}*/
			
			/*
			 * 将用户所指定的端口号保存到一个文件中,
			 * 当服务器程序下次启动时,直接从文件中读取端口号,
			 * 当要修改端口号时,直接从文件里修改就可以了。
			 */
			File portFile = new File("port.txt");
			BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(portFile)));
			int port = Integer.parseInt(br.readLine());
			ss = new ServerSocket(port);
		
			//服务器一直启动
			while(true){
				//创建Socket  及启动与之相应的线程
				Socket s = ss.accept();
				new Thread(new ServerThread(s)).start();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
			
	}	
}			
	

package tcp;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
/*
 * 线程类,一个客户端对应着一个线程
 */

public class ServerThread implements Runnable{
	private Socket s = null;
	public ServerThread(Socket s){
		this.s = s;
	}		
		@Override
	public void run() {
		try{	
				//获得输入字节流
				InputStream  in = s.getInputStream();
				
				//获得输出字节流
				OutputStream out = s.getOutputStream();
				
				//包装类,实现字节转换为字符操作
				BufferedReader br = new BufferedReader(new InputStreamReader(in));
				
				/*实现行输出,换行与与操作系统有关,windows为"\r\n",linux为"\n"
				 * 而PrintStream的println的换行符与操作系统无关,都为"\n"
				 * PrintWriter 注意他的缓存问题:即写入的内容不立即输出
				 * 解决的方法:构造方法设置为自动刷新缓存,和println配合使用
				 */
				PrintWriter pw = new PrintWriter(out,true);
				
				/*
				 * 此处注意一个小问题就是
				 * 当我们用telnet启动一个客户端时,输入abd[backspace]c     命令行显示abc
				 * 当时转换后为dba  这是因为   转换后为c[backspace]dba    命令行中显示dba
				 */
				while(true){
					//读取客户端输入的一行数据
					String strLine = br.readLine();
					
					//当客户端输入quit时客户端与服务器端失去连接
					if(strLine.equalsIgnoreCase("quit")){
						break;
					}
					
					String strEcho = new StringBuffer(strLine).reverse().toString();
					
					//将发转的信息写入到输出流中
					pw.println(strLine + "--->" + strEcho);
				}
				
				//各种关闭
				br.close();
				pw.close();
				s.close();
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	
}
		
		
		
		


package tcp;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

public class TCPClient {

	/**
	 * 客户端:
	 */
	public static void main(String[] args) throws Exception{
		//通过配置参数来指定连接服务程序的IP地址和Port
		if (args.length < 2){
			System.out.println("User:Please use :  java TCPClient  TCPServerIP TCPServerPort");
			return;
		}
		Socket s = new Socket(args[0],Integer.parseInt(args[1]));
		InputStream in = s.getInputStream();
		OutputStream out = s.getOutputStream();
		
		//获得从输入流中读取一行的包装类
		BufferedReader brNet = new BufferedReader(new InputStreamReader(in));
		
		//获得从键盘中读取一行的包装类
		BufferedReader brKeyBoard = new BufferedReader(new InputStreamReader(System.in));
		
		//往输出流中写入一行
		PrintWriter pw = new PrintWriter(out,true);
		
		while(true){
			//先从键盘读取一行
			String str = brKeyBoard.readLine();
			
			//将读取的一行字符写入到输出流中
			pw.println(str);
			
			//如果从键盘输入的字符为quit,那么结束客户端程序
			if(str.equalsIgnoreCase("quit")){
				break;
			}
			
			//读取输入流中的数据,将从服务器端返回的信息打印
			System.out.println(brNet.readLine());
		}
		
		//各种关闭
		brNet.close();
		brKeyBoard.close();
		pw.close();
		s.close();
	}

}


---------------------- android培训java培训、期待与您交流! ---------------------- 详细请查看: http://edu.csdn.net/heima
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值