黑马程序员 java基础之网络编程TCP
TCP网络传输。
客户端和服务端
分别对应着两个对象。
Scoket(客户端)和ServerSocket(服务端)。
Socket(String address, int port)
创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
这个客户端在一建立的情况下就去连接服务端。
通过指定地址和指定的端口找到服务端。并与之建立连接。
步骤:
1.创建Socket服务,并指定要连接的主机和端口。
Socket s = new Socket("localhost",10002);
如果上述这步成功了的话。也就是通路建立了。
当通路一建立,就有一个Socket流,也就是网络流。
两端之间建立了一个网络流。Socket中既有输入流也有输出流,
一旦建立连接,在Socket中就可以取到输入流和输出流对数据进行操作。
流不用去建立,只要通路一建立,流就存在了。
getOutputStream()
返回此套接字的输出流。
getInputStream()
返回此套接字的输入流。
服务端
服务端没有自己的流对象。
服务端和客户端连接的时候,就使用了客户端的流对象和客户端进行操作。
public classTCPSocket {
/**
*@param args
*/
public static void main(String[] args)throws Exception
{
Socket s = newSocket("localhost",10002);
//为了发送数据,应该获取socket流中的输出流
OutputStream os =s.getOutputStream();
BufferedWriter bw = newBufferedWriter(new OutputStreamWriter(os));
bw.write("tcp,gemenwolaile");
bw.flush();
//而在这里不同去关流
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(10002);
//通过accept方法获取连接过来的客户端对象。
Socket s = ss.accept();
System.out.println(s.getInetAddress().getHostAddress());
//获取客户端发送过来的数据,那么要使用客户端对象的读取流方法来读取数据。
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(newString(buf,0,len));
s.close();//关闭客户端。
}
}
必须先启动服务端。
演示tcp传输的客户端和服务端的互访。
需求:客户端给服务端发送数据,服务端收到后,给客户端反馈信息。
/*
1.建立socket服务。指定要连接的主机和端口
2.获取socket流中的输出流,将数据写到该流中。通过网络发送给服务端。
3.获取socket流中的输入流,将服务器反馈的数据获取到,并打印。
4.关闭客户端资源。
*/
class TcpClient2
{
public static void main(String[] args)throws Exception
{
Socket s = new Socket("localhost",10004);
OutputStream os = s.getOutputStream();
os.write("nihao".getBytes());
InputStream is = s.getInputStream();
byte [] arr = new byte[1024];
//十分要主要,这里的read读的是Socket流。也是一个阻塞式方法。只有服务端往回发送数据的时候,才会解除阻塞。
int len = is.read(arr);
System.out.println(newString(arr,0,len));
s.close();
}
}
class TcpServer2
{
public static void main(String[] args)throws Exception
{
ServerSocket ss = newServerSocket(10004);
Socket s = ss.accept();
InputStream is = s.getInputStream();
byte [] arr = new byte[1024];
int len = is.read(arr);
System.out.println(newString(arr,0,len));
OutputStream os =s.getOutputStream();
os.write("完成".getBytes());
s.close();
}
}
package day5;
importjava.net.*;
importjava.io.*;
public classTCPSocket2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
class Client3
{
public static void main(String[] args)throws Exception
{
Socket s = newSocket("localhost",10009);
BufferedWriter bw = new BufferedWriter(newOutputStreamWriter(s.getOutputStream()));
BufferedReader br = newBufferedReader(new InputStreamReader(System.in));
String ss = null;
BufferedReader brr = newBufferedReader(new InputStreamReader(s.getInputStream()));
while((ss = br.readLine())!=null)
{
bw.write(ss);
bw.newLine();
bw.flush();
System.out.println(brr.readLine());
}
br.close();
s.close();
}
}
class Server3
{
public static void main(String[] args)throws Exception
{
ServerSocket ss = newServerSocket(10009);
Socket s = ss.accept();
InputStream is = s.getInputStream();
BufferedReader br = newBufferedReader(new InputStreamReader(is));
OutputStream os =s.getOutputStream();
BufferedWriter bw = new BufferedWriter(newOutputStreamWriter(os));
String sss = null;
while((sss = br.readLine())!=null)
{
//上边的readLine只有在看到回车符后才能结束,也就是解除阻塞。
bw.write(sss.toUpperCase());
bw.newLine();
bw.flush();
}
//有一个疑问,为什么当客户端结束的时候,服务端也结束了,
//这是因为,当客户端调用s.close()时,会在流里写一个-1,
//然后服务器端就跳出循环,然后通过ss关闭服务器。
ss.close();
}
}
要注意,对于读取流中的阻塞式方法来说,一定要将发送发的缓冲区刷新。
而且必须将行结束符写到这个流中。
对于服务器端的阻塞式方法,如readLine(),必须在客户端发送一个标志,让服务器端知道其结束了,否则服务器一直处于读取数据的状态。
在客户端使用
s.shutdownOutput();//关闭客户端的输出流,相当于给流中加入一个结束标记
下边,我们使用更高级的协议去完成一个自定义浏览器和tomcat服务器的处理请求。
可以直接使用应用层的协议去封装。
URL url = newURL("http://localhost:8080/GP2/index.jsp");
//首先将url地址封装到一个对象为URL中。
URLConnectionconn = url.openConnection();
//这个URLConnection就封装了有关底层协议的一些信息。
//也就通过一些应用层的协议进行拆包,并将这些属性封装到这个对象中去.
//然后通过openConnection()在内部帮助我们去完成Socket等一系列连接动作。
//所以不同写Socket(是在传输层上的协议),而且是带着协议去封装的。
//当连接成功,当然可以将其服务端传递过来的数据拿到。也就是得到输入流对象。
从获取值
InputStream is =conn.getInputStream();
//从而从这个流对象中的得到值
byte [] arr =new byte[1024];
int len = 0;
while((len = is.read(arr))!=-1)
{
System.out.println(newString(arr,0,len));
}
这时候传递过来的数据就没有了响应头。而直接打出html中的信息。
这是因为。当底层的网络协议把数据封装好之后(包括响应头),通过应用层去拆包之后,将数据和响应头分离开来,得到的数据就只剩数据了。
而这些响应头中的信息,可以通过URLConnection对象中的一系列方法进行简析.
比如说:System.out.println(conn.getHeaderField("Server"));
可以得到有关服务器的信息。
当浏览器写入一个网址之后,去访问某一台主机的时候.它到底做什么了事情?
在上网的时候,先去本地找hosts中的各自域名的映射关系.
当本地有,就返回本地的,如localhost,如果本地没有,就去各自运营商提供的域名服务器DNS中去寻找映射关系,最后使用ip地址找到对应的服务器,使用端口找到对应的应用程序.如果直接输入ip地址,则不走域名简析.