(网络编程)Socket编程之TCP

网络模型
OSI参考模型
TCP/IP参考模型

网络通讯要素
1.IP地址---InetAddress
    找到对方,网络中设备的标识
    因为IP地址不易记忆,可以用主机名代替
    本地回环地址:127.0.0.1 主机名:localhost
默认IP地址

127.0.0.1 安装网卡就会有,可以ping此IP地址测试网卡

192.168.*.* 为常用的保留地址段 用在局域网中

//192.168.1.0 代表一个网络段
//192.168.1.255 代表这个网段的广播地址

2.端口号
    用于标识进程的逻辑地址,
    可以对不同进程进行标识,以便区分

    有效端口:0~65535,其中0~1024为系统使用或保留端口
       
    数据要发送到对方指定的应用程序上,
    为了表示这些应用程序,就给这些
    网络应用程序都用数据进行了标识。
    0--65535端口范围
    0--1024被系统保留
    WEB服务器    80 默认
    Tomcat服务器 8080 默认
    MySQL         3306 默认
    如果默认端口被其他应用程序占用,就会修改端口号

3.传输协议
    通信规则。也称为协议。
    国际组织定义了通用协议TCP
    另外一个常用的:UDP
    不同主机之间通信,也可以使用其他协议进行通信。
    比如:微软公司的

UDP
  将数据及源和目的三项封装成数据包,不要建立连接
  (不判断接收方是否存在,存在,则接收数据对方若不存在,则丢弃数据。是不可靠的协议)
  每个数据报的大小限制在64K内
  因无连接,是不可靠协议
  不需要建立连接,速度快
   
---速度第一,适用于数据可以适当的丢失(视频、聊天、桌面共享)
(对讲机---不知道对方是否在,也在进行数据传输)

TCP
  建立连接,形成传输数据的通道
  (需要先确定对方存在并与对方先握手,再传输数据。是可靠的协议)
  在连接中进行大数据量传输
  通过三次握手完成连接,是可靠协议
  必须建立连接,效率会稍低

握手:对方在?-->对方回复-->回复对方-----完成握手,开始建立TCP数据传输通道,传输数据。

---保证数据不丢失,适用于数据完整(下载文件)。消耗资源,速度稍慢,但可以大数据量传输。
(打电话---首先需要电话通了(对方在),才进行通讯)
   
Socket
网络编程就是Socket编程
(JSP、Servlet--JavaEE框架部分的一些组件技术)
Socket就是为网络服务提供的一种机制
通信的两端都有Socket
网络通信其实就是Socket间的通信
数据在两个Socket间通过IO传输


UDP传输
DatagramSocket与Datagrampacket
建立发送端,接收端
建立数据包
调用Socket的发送接收方法
关闭Socket
发送端与接收端是两个独立的运行程序

UDP的Socket服务的建立
java.net.*

DatagramSocket 
此类表示用来发送和接收数据报包的套接字
既能发送又能接收

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

DatagramPacket
此类表示数据报包
数据报包用来实现无连接包投递服务

构造方法
DatagramPacket(byte[] buf,int length)
构造DatagramPacket,用来接收长度为length的数据包

DatagramPacket(byte[] buf,int length,InetAddress,int port)
构造数据包,用来将长度为length的包发送到指定主机的指定端口号


TCP传输
Socket和ServerSocket
建立客户端和服务端
建立连接后,通过Socket中的IO流进行数据的传输
关闭Socket
同样,客户端与服务器端是两个独立的应用程序

类Socket
此类实现客户端套接字,套接字是两台机器间通信的端点。
套接字的实际工作由SocketImpl类的实例执行
应用程序通过更改创建套接字实现的套接字工厂
可以配置它自身,以创建适合本地防火墙的套接字。

构造函数
Socekt(InetAddress address,int port)
  创建一个流套接字并将其连接到指定IP地址的指定端口号
  创建了对象就连接到服务端

Socket(String host,int port)
  创建一个流套接字并将其连接到指定主机上的指定端口号

方法:
InputStream getInputStream()
  返回此套接字的输入流

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

 InetAddress getInetAddress()
  返回套接字连接的地址


类ServerSocket
构造函数:
ServerSocket(int port)
  创建绑定到特定端口的服务器套接字
 
方法:
 Socket accept()
   侦听并接收到此套接字的连接

 

 

//先打开服务端,在开客户端,否则客户端连接不到服务端。。。
//本示例:客户端向服务端发送一次数据,服务端接收到数据,并断开与客户端的连接
/*
TCP传输
1,tcp分客户端和服务端
2,客户端对应的对象是Socket
   服务端对应的对象是ServerSocet

客户端:
	查阅Socket对象,发现在
	该类对象建立时,就可以去连接指定主机
	因为tcp是面向连接的,所以在建立
	Socekt服务时,就要有服务端存在,并连接成功
	形成通路后,在该通道进行数据传输。

需求:给服务端发送一个文本数据
步骤:
1,创建Socket服务,并指定要连接的主机和端口

*/
import java.io.*;
import java.net.*;

class TcpClient
{
	public static void main(String[] args) throws Exception
	{
		//创建客户端的socekt服务,指定目的主机和端口
		Socket s = new Socket("127.0.0.1",6789);

		//为了发送数据,应该获取socket流中的输出流
		OutputStream out = s.getOutputStream();

		out.write("我来了".getBytes());

		s.close();
	}
}


/*
定义端点接收数据并打印到控制台上
服务端
1,建立服务端的socket服务,ServerSocket()并监听一个端口
2,获取连接过来的客户端对象,通过ServerSocket的accept方法
   如果没有连接就会等,所以这个方法是阻塞式的
3,客户端如果发过来数据,那么服务端要使用对应的客户端对象
	并获取到该客户端对象的读取刘来读取发过来的数据
4,关闭服务(可选--服务端常开)
*/

class  TcpServer
{
	public static void main(String[] args) throws Exception
	{
		//建立服务端的Socket服务,并监听一个端口
		ServerSocket ss = new ServerSocket(6789);
		
		//通过accept方法获取连接过来的客户端对象
		Socket s = ss.accept();

		//获取客户端发送过来的数据,使用客户端对象的读取流读取数据
		String ip = s.getInetAddress().getHostAddress();

		System.out.println(ip+"...connected");

		InputStream in = s.getInputStream();
	
		byte[] buf = new byte[1024];

		int len = in.read(buf);

		System.out.println(new String(buf,0,len));


		s.close();//关闭客户端
		//ss.close();//服务端一般都保持常开,不关闭
	}
}

 

/*
演示tcp传输的客户端和服务端的互动

需求:客户端给服务端发送数据,服务端收到数据后,给客户端反馈数据

*/

import java.io.*;
import java.net.*;

/*
客户端
1,建立socekt服务,指定要连接的主机和端口
2,获取socekt流中的输出流,将数据写到该流中
  通过网络发送给服务端
3,获取socket流中的输入流,将服务端反馈的数据获取到并打印
4,关闭客户端资源
*/

//客户端
class TcpClient2 
{
	public static void main(String[] args) throws Exception
	{
		//建立Socket连接
		Socket s = new Socket("127.0.0.1",10000);

		//向服务端发送数据
		OutputStream out = s.getOutputStream();

		out.write("服务端我来了".getBytes());
		
		//获取服务端的反馈数据
		InputStream in = s.getInputStream();

		byte[] buf = new byte[1024];

		int len = in.read(buf);//阻塞式方法,Scoket没有收到数据就等待

		System.out.println(new String(buf,0,len));

		//关闭连接
		s.close();
	}
}

//服务端
class TcpServer2 
{
	public static void main(String[] args) throws Exception
	{
		//建立服务端
		ServerSocket ss = new ServerSocket(10000);

		//侦听并接收数据
		Socket s = ss.accept();

		String ip = s.getInetAddress().getHostAddress();

		System.out.println("ip:"+ip+"...connected");

		InputStream in = s.getInputStream();

		byte[] buf = new byte[1024];

		int len = in.read(buf);//阻塞式方法,Scoket没有收到数据就等待

		System.out.println(new String(buf,0,len));

		//向客户端发送数据
		OutputStream out = s.getOutputStream();

		out.write("收到!".getBytes());

		//断开连接
		s.close();

		//ss.close();
	}
}

 

/*
需求:建立一个文本转换服务器
客户端通过键盘给服务端发送文本数据,
服务端将文本转成大写并返回给客户端
而且客户端可以不断的进行文本转换,
并输入over之后结束转换
*/
/*字符打印流
PrintWriter---非常常用。web开发时使用它将数据一行一行打到客户端
构造函数可以接收的参数类型:
1,file对象   File
2,字符串路径 String
3,字节输出流 OutputStream
4,字符输出流 Writer
*/
/*
注意:

1, PrintWriter 非常常用,应该掌握
	Printwriter 在构造时,可传入字节流,也可以传入字符流,很通用。还能传入字符串和文件流对象
	在构造时,如果传入参数true,则使用println会换行并自动刷新缓冲区
	即,可以替代缓冲区写出流的newLine()和flush()

2,该例中操作的源和目的都是文本数据,即字符数据
	为了提高效率,所以采用缓冲区字符读取和写入流
	这里要注意的是,对数据的操作使用缓冲区必须进行刷新动作
	另外,write(line)写入一行是不会写入“回车符”的
	而read(line)则要以读到“回车符”为标记,才能判断一行结束
	所以,在write的时候,就必须newLine
	接收方读的时候才会依据换行符读取成功!

3,关闭客户端时,会返回-1到socket输出流,当服务端读取到-1之后,
	应该产生相应动作来处理。
	服务端可以退出,也可以不退出!
	该怎么做呢??

*/


import java.io.*;
import java.net.*;

class  TcpClient3
{
	public static void main(String[] args) throws Exception
	{
		//建立Socket连接
		Socket s = new Socket("127.0.0.1",8888);

		//定义读取键盘数据的流对象
		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));
		
		//定义目的,将数据写入到socket输出流,发送给服务端
		//BufferedWriter bufOut = 
			//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		
		//定义一个socket读取流,读取服务端返回的信息
		BufferedReader bufIn =
			new BufferedReader(new InputStreamReader(s.getInputStream()));

		String line = null;

		while((line=bufr.readLine())!=null)
		{			
			if(line.equals("over"))
				break;
			out.println(line);//换行并带自动刷新
			
//			bufOut.write(line);//写一行是不会写入回车符的
//			bufOut.newLine();//所以这里一定要换行
//			bufOut.flush();
			
			String serverMessage = bufIn.readLine();//读一行,要读到行结束标记才算
			System.out.println("服务端:"+serverMessage);
		}
		bufr.close();
		s.close();//关闭该流,会返回-1到该socket读取流中,导致服务端读取到之后,服务端也关闭
	}
}
class  TcpServer3 
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(8888);
		Socket s = ss.accept();

		String ip = s.getInetAddress().getHostAddress();
		System.out.println("客户端ip:"+ip+"...is connect");

		//读取socket读取流中的数据
		BufferedReader bufIn = 
			new BufferedReader(new InputStreamReader(s.getInputStream()));

		//目的为socket输出流。将转换后的大写数据写入到socket输出流,并发送给客户端
		//BufferedWriter bufOut = 
			//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

		PrintWriter out = new PrintWriter(s.getOutputStream(),true);//true对println是有效自动刷新
		
		String line = null;
		while((line=bufIn.readLine())!=null)
		{
			if(line.equals("close server"))
				break;
			System.out.println("客户端message:"+line);

			out.println(line.toUpperCase());//true对println是有效自动刷新
			
//			bufOut.write(line.toUpperCase());
//			bufOut.newLine();
//			bufOut.flush();
		}
		s.close();
		ss.close();
	}
}

 

 

/*
IO 结合 Socket Tcp文件传输

需要注意的问题:
1,异常处理
2,判断文件是否存在
3,缓冲区写需要flush
4,PrintWriter的应用
5,readLine读数据到缓冲区不会读入结束标记
   另一个readLine再读缓冲区因为其中没有结束标记,所以会一直读,死循环
6,文件结束标记
	1,自定义一个结束标记。客户端与服务端协商一个读取缓冲区数据结束标记
		缺点在于:有可能读取到的文件中的数据的一行有这个标记
	2,利用时间来标记。。时间是唯一的
	3,socket提供的方法 
		void shutdownInput()  关闭输入流--在流中加入结束标记
		void shutdownOutput() 关闭输出流--在流中加入结束标记
7,发送文件之前,应该先发读取的文件名到服务端,以便服务端创建好文件
8,服务端应判断文件名是否重名,如果重名则会覆盖以前的文件,所以需要判断文件名
*/

import java.io.*;
import java.net.*;
class TextClient 
{
	public static void main(String[] args) throws Exception
	{
		//建立socket连接
		Socket s = new Socket("127.1.1.0",7777);


		//带缓冲区的字符读取流关联文件
		BufferedReader bufr = 
			new BufferedReader(new FileReader("E:/day19/SetSystemDemo.java"));
		
		//字符打印流对象包装socket字节输出流
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		
		//获取当前时间并传给服务端,以此为上传结束标记(时间是唯一的)
		long time = System.currentTimeMillis();
		
		//out.println(time+"");//long型转为字符串

		DataOutputStream dos = new DataOutputStream(s.getOutputStream());
		dos.writeLong(time);

		String line = null;
		while((line=bufr.readLine())!=null)//读取文件,文件有结束标记!当判断到结束标记是循环就结束了!
		{
			out.println(line);//结束标记是不会写入到socket字节输出流中的
		}
		
		//out.println("over");
		//s.shutdownOutput();//写入流结束标记
		
		out.println(time+"");


		//带缓冲区的字符读取流包装socket字节输入流
		BufferedReader bufIn =
			new BufferedReader(new InputStreamReader(s.getInputStream()));
		
		//服务端只返回”上传成功“一句,不用循环
		String str = bufIn.readLine();//阻塞式方法。没有数据就等待
		System.out.println(str);

		bufr.close();
		s.close();
	}
}

class TextServer 
{
	public static void main(String[] args) throws Exception
	{
		//建立服务端socket连接
		ServerSocket ss = new ServerSocket(7777);

		//侦听并接收数据
		Socket s = ss.accept();
		
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"......连接成功");

		DataInputStream dis = new DataInputStream(s.getInputStream());
		String time = dis.readLong()+"";

		//包装socket字节输入流
		BufferedReader bufIn =
			new BufferedReader(new InputStreamReader(s.getInputStream()));

		//创建接收文件
		PrintWriter pw = new PrintWriter(new FileWriter("e:/tcp.java"),true);

		String line = null;
		while((line=bufIn.readLine())!=null)//这里读socket输入流,当中不会有文件结束标记,所以循环停不下来
		{	
			//if(line.equals("over"))		//底层一直在调用read()读取缓冲区数据!这里需要加入结束标记!
			if(line.equals(time))
				break;
			pw.println(line);
		}

		//BufferedWriter bufOut = 
			//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
		//bufOut.write("上传成功");
		//bufOut.flush();//必须刷新。

		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		out.println("上传成功!");//自动刷新
		
		s.close();
		ss.close();
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值