您的位置:首页 > 理论基础 > 计算机网络

黑马程序员-Socket网络编程

2013-11-12 00:05 393 查看
----------------------
ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

网络通信的三要素:

(1):IP地址

(2):通信端口

(3):通信协议

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

分A,B,C类IP地址

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

示例:255.255.255.0

A类IP地址:1个网络号+3个主机号

B类IP地址:2个网络号+2个主机号

C类IP地址: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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: