黑马程序员-Socket网络编程

来源:互联网 发布:java发送自定义短信 编辑:程序博客网 时间:2024/05/22 08:27

---------------------- 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) 地址

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

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

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

staticInetAddress 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>();@Overridepublic 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 stubSocket 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