黑马程序员-Socket网络编程

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

 

网络通信的三要素:

1):IP地址

2):通信端口

3):通信协议

Ip地址:ip地址实际上是有4个字节共32位的二进制来表示的,但是这样不便于记忆,那么把这32位分割成4分,每一字节8位,再转成十进制表示。2^8=256-1,范围在0~255255是广播地址

A,B,CIP地址

 

子网掩码的作用:为了标识网络号

示例:255.255.255.0

AIP地址:1个网络号+3个主机号

BIP地址:2个网络号+2个主机号

CIP地址:3个网络号+1个主机号

InetAddress

此类表示互联网协议 (IP) 地址

static InetAddress getLocalHost():获取本机的IP地址对象,返回一个InetAddress对象

String getHostAddress():获取 IP 地址字符串,返回String类型

String getHostName():获取此 IP 地址的主机名,返回String类型

static InetAddress getByName(String host):在给定主机名的情况下确定主机的 IP 地址。主机名可以是机器名(如 "java.sun.com"),也可以是其 IP 地址的文本表示形式(如"192.168.10.10"

注意:主机名有可能重复,但IP地址是唯一的(在同一网内),所以此方法推荐使用IP地址

boolean isReachable(int timeout): 测试是否可以达到该地址(能否在指定时间内连接上该地址),timeout 为调用中止前的时间(以毫秒为单位)

static InetAddress[] getAllByName(String host):在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址所组成的数组。

用法:getAllByName(“www.baidu.com”),一个域名可以对应多个主机,百度域名可能有多个(主机)服务器,所以这个方法是得到百度的所有服务器的IP地址对象

网络通讯也叫Socket通讯

不同的协议用不同的socket

UDP协议: DatagramSocket(发送与接收), DatagramPacket(数据包)

         UDP没有客户端与服务端之分

发送端与接收端都用DatagramSocket

TCP协议:Socket(客户端), ServerSocket(服务端)

         TCP分客户端,服务端,两者有各自的Socket

UDP协议与TCP协议的区别

UDP

将数据及源和目的封装成数据包中,不需要建立连接

每个数据报的大小在限制在64k

因无连接,是不可靠协议,会出现丢包(带宽或CPU能力不足)

不需要建立连接,速度快(效率比tcp高)

TCP

建立连接,形成传输数据的通道。

在连接中进行大数据量传输,数据没有大小限制

通过三次握手完成连接,是可靠协议

必须先建立连接,效率会稍低

 

创建UDP发送端

 

思路与步骤:

1:定义Socket服务(DatagramSocket

         DatagramSocket(int port) //如果没有指定端口,那么由CPU指定

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

2:定义一个数据包,封装需要发送的数据

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

         构造数据报包,用来将长度为length 的包发送到指定主机上的指定端口号。

3:通过Socket服务发送信息

         send(DatagramPacket p)

    从此套接字发送数据报包。

4:关闭资源

         close()

核心代码:

DatagramSocket ds = new DatagramSocket(8888);

byte[] buf = "我的信息".getBytes();
DatagramPacket dp =
 new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.10.10"),10000);
		ds.send(dp);
		ds.close();

 

创建一个UDP接收端

 

思路与步骤

1:创建一个Socket服务

         DatagramSocket(int port)

         创建数据报套接字并将其绑定到要监听(要接收数据的)的指定端口。

2:定义一个数据包,用于存储到接收到的数据

         DatagramPacket(byte[] buf, intlength)

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

3:通过Socket服务的receive()将接收到的数据存放到数据包中

         receive(DatagramPacket p)

    从此套接字接收数据报包。

4:通过数据包特有的方法获取接收到的数据

5:关闭资源

 

核心代码:

DatagramSocket ds = new DatagramSocket(10000);

		byte[] buf = new byte[1024];
		DatagramPacket dp = new DatagramPacket(buf,buf.length);

		ds.receive(dp);

		String ip = dp.getAddress().getHostAddress();
		String data = new String(dp.getData(),0,dp.getLength());
		int port = dp.getPort();
		System.out.println(ip+"::"+data+"::"+port);

		ds.close();


 

创建一个TCP的客户端

思路与步骤:

1:创建客户端Socket服务,指定要连接的主机地址和服务端口

2:为了发送数据,应该获取Socket流中的输出流

3:往输出流写数据(与UDP这一步骤有区别,不需要写发送数据的语句,只需把数据写入流即可)

4:关闭Socket流对象

 

核心代码

Socket s = new Socket("192.168.1.254",10003);
		//创建客户端Socket服务,指定要连接的主机地址和服务端口
		OutputStream out = s.getOutputStream();
		//为了发送数据,应该获取Socket流中的输出流
		out.write("我是服务端".getBytes());
		//往输出流写数据
		s.close();
		//关闭流


创建一个TCP服务端

思路与步骤:

1:建立一个服务端socket的服务,并监听一个端口

2:通过accept方法获取连接过来的客户端的对象

3:获取客户端发过来的数据,要使用客户端对象的读取流来读取

4:断开连着的客户端,免得占用资源

 

核心代码

ServerSocket ss = new ServerSocket(10003);
//通过accept方法获取连接过来的客户端的对象
Socket s = ss.accept();//accept()返回的是一个Socket类型的对象
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip);
//获取客户端发过来的数据,要使用客户端对象的读取流来读取
InputStream in = s.getInputStream();//得到客户端对象后,获取读取流
		
byte[] buf = new byte[1024];
int len = in.read(buf);		
System.out.println(new String(buf,0,len));
		
s.close();//断开连着的客户端,免得占用资源


 

 建立TCP客户端与服务端互访:

客户端发送数据到服务端,服务端接收到数据后,返回自定义数据

客户端代码:

class  TransClient
{
	public static void main(String[] args) throws Exception
	{
		Socket s = new Socket("192.168.1.254",10005);
		//定义读取键盘数据的流对象。
		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));
		//定义目的,将数据写入到socket输出流。发给服务端。
		BufferedWriter bufOut = 
			new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
		//定义一个socket读取流,读取服务端返回的大写信息。
		BufferedReader bufIn = 
			new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line = null;
		
		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;		
			bufOut.write(line);
			bufOut.newLine();
			bufOut.flush();
			String str =bufIn.readLine();
			System.out.println("server:"+str);
		}
		bufr.close();
		s.close();
	}
}


 

 

服务端代码:

class  TransServer
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(10005);
		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"....connected");
		//读取socket流中的数据。
		BufferedReader bufIn =
			new BufferedReader(new InputStreamReader(s.getInputStream()));
		//目的。socket输出流。将大写数据写入到socket输出流,并发送给客户端。
		BufferedWriter bufOut = 
			new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
		String line = null;
		while((line=bufIn.readLine())!=null)
		{
			System.out.println(line);
			bufOut.write(line.toUpperCase());
			bufOut.newLine();
			bufOut.flush();
		}
		s.close();
		ss.close();
	}
}


 

 

在建立TCP客户端与服务端互访的时候,有如下细节需要注意

(1)      客户端在写入数据之后,需要刷新,才能被发送

(2)      服务端在读数据的时候,使用的是缓冲读取流,readLine()判断数据结束的标记是回车符\r\n,所以要在客户端发送数据后再添加一个换行:newLine();

(3)      同理,服务端发送返回数据的时候,也要刷新和换行:newLine();

 

介绍一个新的带自动刷新的输出流对象:PrintWrite

并且PrintWrite的特有输出方法println()能够自动加上换行符

那么上述代码核心部分

 

BufferedWriterbufOut =

                            new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

 

修改为:PrintWriter out = newPrintWriter(s.getOutputStream(),true);

 

bufOut.write(line.toUpperCase());

                            bufOut.newLine();

                            bufOut.flush();

 

修改为:out.println(line.toUpperCase());

 

客户端从服务端下载文件

服务端支持多台主机同时连接下载,并统计被下载多少次,同一IP地址下载多次算一次

服务端代码示例:

public class MyServer {
	public static void main(String[] args) throws Exception {
		@SuppressWarnings("resource")
		ServerSocket server = new ServerSocket(10000);
		ClientThread ct = null;
		while (true) {
			Socket client = server.accept();
//accept()是阻塞式方法,如果有连接,才会往下执行
			ct = new ClientThread(client);//注意多线程的写法
			Thread t = new Thread(ct);
			t.start();
		}
	}
}

class ClientThread extends Thread {
	private Socket client;
	public static int num = 0;

	public ClientThread(Socket client) {
		this.client = client;
	}

	HashSet<InetAddress> hashset = new HashSet<InetAddress>();

	@Override
	public void run() {
		FileInputStream in = null;
		try {
			OutputStream out = client.getOutputStream();
			in = new FileInputStream(new File("d:\\test\\IMG_0309.JPG"));

			byte[] buf = new byte[1024];
			int len = 0;
			while ((len = in.read(buf)) != -1) {
				out.write(buf, 0, len);
				out.flush();
			}
			if (!(hashset.contains(client.getInetAddress()))) {
				num++;
			}
			System.out.println("下载了" + num + "次");
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				in.close();
				client.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}


 

 

客户端代码:

public class DownloadPic {
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		Socket socket = new Socket("192.168.10.10", 10000);
		InputStream in = socket.getInputStream();
		FileOutputStream fos = new FileOutputStream(new File(
				"d:\\test\\我下载的pic.jpg"));
		byte[] buf = new byte[1024];
		int length = 0;
		while ((length = in.read(buf)) != -1) {
			fos.write(buf, 0, length);
			fos.flush();
		}
		fos.close();
		socket.close();
	}
}

 

客户端往服务端上传文件

要注意的地方:要注意结束标记,服务端读取的结束标记,否则会一直阻塞

可以用Socket的方法,shutDownOutput()关闭客户端的输出流,执行此方法是通知服务端,文件读完了,可以结束服务端读取的循环

问题:为什么客户端从服务端下载文件的时候,不需要shutDownOutput()也可以正常执行?

答:

下载:服务端使用自定义输入流读取需要被下载的文件,然后交给socket输出流,当输入流读到流末尾的时候会自动结束读取循环,socket输出流没有写入结束符号。但是服务端会继续往下执行,会关闭socket对象,如果socket被关闭,客户端会收到通知,会自动结束客户端的socket

 

上传:因为客户端用自定义的输入流中读取需要被上传的文件,交给socket的输出流,自定义的输入流读完数据会结束读取,但是不会写入结束符号,会结束while循环,继续往下执行,会关闭socket,服务端从socket读取数据的时候,也不会读到结束符号,所以服务端会一直等待,这时候必须要客户端发送一个结束的标记,提示服务端结束读取。

 

 

URLConnection(重点)属于应用层

URL url = new URL(www.baidu.com:8080/web/demo.html);

URLConnection conn= url.openConnection();

//此时openConnection()方法会返回一个URLConnection类型的对象

该对象的作用是把Socket封装在了内部,起的是Socket的连接作用

 

 

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

详细请查看:http://edu.csdn.net

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值