2、网络模型
a,IP地址:网络中设备的标识。java描述的类InetAddress。
1):不易记忆,可用主机名。2):本地回环地址:127.0.0.1;主机名:localhost。
c,传输协议:通讯的规则。
常见协议:TCP,UDP。UDP:将数据及源和目的封装到数据包中,不需要建立连接。每个数据包大小限制在64k内。eg:步话机。1):因无连接,是不可靠协议。容易丢失数据包,丢了就丢了,无法挽回。2):不需要建立连接,数度快。TCP:建立连接,形成传输数据的通道。在连接中进行大数据量的传输。eg:电话。1):通过三次握手完成连接,是可靠协议。2):必须建立连接,效率会稍低。
class LocalHostDemo
{
public static void main(String[] args) throws UnknownHostException
{
//获取本机的ip地址。
InetAddress i1 = InetAddress.getByName("hnlm-THINK");
sop(i1.toString());
sop(i1.getHostAddress());
sop(i1.getHostName());
//获取本机回环地址。
InetAddress i2 = InetAddress.getByName("localhost");
sop(i2.toString());
sop(i2.getHostAddress());
sop(i2.getHostName());
//获取googleip地址。
InetAddress[] i3 = InetAddress.getAllByName("www.google.jp");
for(InetAddress i : i3)
{
sop(i.toString());
sop(i.getHostAddress());
sop(i.getHostName());
}
}
public static void sop(Object o)
{
System.out.println(o);
}
}
4,Socket:就是为网络服务提供的一种机制,网络编程就是Socket编程。
1):通信的两端都有Socket。2):网络通信其实就是Socket间的通信。3):数据在两个Socket间通过IO传输。
Socket编程思路: UDP传输。1,定义了一个udp的 发送端,通过UDP传输方式,将一段文字数据发送出去。a,建立udpsocket服务。DatagramSocket类b,提供数据,并将数据封装到数据包中。DatagramPacketc,通过socket服务的发送功能,将数据包发送出去。d,关闭资源。--------------------------------------------------------public class UdpSend { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { //1,建立UDP的socket服务。指定发送端口,这个端口可以不用指定,系统可以自动分配。 DatagramSocket ds = new DatagramSocket(10000); //2,明确要发送的具体数据。 String s = "Udp i am come in!!"; byte[] buff = s.getBytes(); //3,将数据封装成数据包。 DatagramPacket dp = new DatagramPacket(buff,buff.length, InetAddress.getByName("hnlm-THINK"),10001); //4,用soket服务的send方法将数据发送出去。 ds.send(dp); //关闭资源。 ds.close(); } }
--------------------------------------------------------2:定义一个 udp的 接受端 ,用于接受udp协议传输的数据并处理。a,定义udpsocket服务,通常会监听一个端口。DatagramSocket类其实就是给这个接受网络应用程序定义数字标识。方便于明确哪些数据过来,该应用程序可以处理。b,定义一个数据包,因为要存储接收到的字节数据,并且数据包对象中有更多的功能可以提取字节数据中的不同数据信息。c,通过socket服务中的receive方法将收到的数据存入已定义好的数据包中。数据包:DatagramPacket。方法:getPort( ):无论发送数据包设置的目的源地址是什么,得到的端口号都是发送端Socket设置的端口号。方法:.getAddress( ).getHostAddress( )中:当发送数据包设置的目的源为——回环地址时,此方法得到的是回环地址。当发送数据包设置的目的源为——其他地址时,此方法得到的是发送数据机器的IP地址。d,通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上。e,关闭资源。----------------------------------------------------------------------------------------------------------public class UdpRece { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { //1,创建udp的socket服务。端口号必须指定,必须和上面的端口号一样。 DatagramSocket ds = new DatagramSocket(10001); //2,定义数据包,用于存储接收到的数据。先定义字节数组,数据包会把数据存储到字节数组中。 byte[] buff = new byte[1024]; DatagramPacket dp = new DatagramPacket(buff,buff.length); //3,通过socket服务的接收方法,将收到的数据存储到数据包中。该方法是阻塞式的方法。 ds.receive(dp); //4,通过数据包的方法,获取数据包中的具体数据内容。 String ip = dp.getAddress().getHostAddress(); int port = dp.getPort(); //5,将字节数组中的有效部分转化成字符串。 String text = new String(dp.getData(), 0, dp.getLength()); System.out.println("ip:" + ip + "\r\nport:" + port + "\r\ncontent:" + text); //6,关闭资源。 ds.close(); } }
-----------------------------------------------------------------------------------------------------Socket编程思路: TCP 传输。
1)Socket和ServerSocket。2)建立客户端和服务器端。3)建立连接后,通过Socket中的IO流进行数据传送。4)关闭socket。同样,客户端跟服务端是两个独立的应用程序。TCP传输演示:1)Tcp分客户端跟服务端。2)客户端对应的对象是:Socket。服务端对应的对象是:ServerSocket。5 ) 练习:
1)BufferedWriter类中write()方法跟PrintStream类中println()方法的区别。wirte()方法是一个阻塞式的方法,当readLine一行后,write会将回车符一行结束标记也写进line中,所以要用BufferWriter类中的newLine()方法来保证readLine方法的原意。而println它可以打印一行后自动换行。2)Socket类其对象调用close()方法后会将soket流末尾自动置为-1 。
4)服务端:
创建步骤:
需求:定义ServerSocket端点,接受数据并打印在控制台上。
a,建立服务端的socket端点,ServerSocket(),并监听一个端口。b,获取链接过来的客户端对象,通过ServerSocket类的accept()方法完成。没有连接就会等,所以这个方法是阻塞式的。c,客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端的读取流来读取发过来的数据。并打印在控制台。d,关闭服务端。(可选操作)
--------------------------------------------------------------------------------------------------
ublic class SingleTcpServer { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { //建立服务端的socket服务,用来监听客服端端口。 ServerSocket ss = new ServerSocket(10005); //获取客户端对象。 Socket s = ss.accept(); //获取客户端的ip地址。 String ip = s.getInetAddress().getHostAddress(); //打印其地址,表示连接成功。 System.out.println(ip + "..connected!!!"); //可以通过获取到的socket对象中的socket流和具体的客户端进行通讯。 //读取客户端的数据,使用客户端的socket对象的输入流。 InputStream is = s.getInputStream(); byte[] buff = new byte[1024]; int len = is.read(buff); String text = new String(buff,0,len); System.out.println(text); //关闭资源。 s.close(); ss.close(); }
------------------------------------------------------------------------------------------------
3)客户端:
a,通过查阅Socket对象,发现在该对象建立时,就可以去连接指定主机。因为,tcp是面向连接的,所以在建立Socket服务时,就要有服务端的存在,并连接成功。形成通路后,在该通道进行数据的传输。b,创建步骤:需求:给服务端发送一个文本数据。
1)创建客户端的Socket服务,并指定要连接的目的主机跟端口。2)为了发送数据,应该获取Socket流中的输出流。3)关闭Socket流。OutputStream输出流会伴随Socket流的关闭而关闭。
----------------------------------------------------------------
public class SingleTcpClient {
/**
* @param args
* @throws IOException
* @throws UnknownHostException
*/
public static void main(String[] args) throws UnknownHostException, IOException {
//1,定义tcp的socket服务,用来发送数据,端口号和主机名必须确定。
Socket s = new Socket("192.168.1.100",10005);
//2,获取socket流中的输出流对象。
OutputStream os = s.getOutputStream();
//3,输出流中写入数据。
os.write("tcp,i am come in!!!".getBytes());
//4,关闭资源。
s.close();
}
}
----------------------------------------------------------------
总结:
对于网络编程而言,重要的是理解其步骤,按照步骤的需要,一步步搭建根基!
客户端和服务端需要交互,那么就要构建相对应的流,供其输入和输出!
对于阻塞式方法,一定要注意,提供停止标签!
对于PrintWriter ,记得用println 而不是write;不要忘了加上true,自动刷新!
----------------------------------------------------------------
/*
*需求:多客户端向服务器上传图片,并且保证不会因为文件名称而重复。
*/
class PicClient
{
public static void main(String[] args) throws Exception
{
if (args.length != 1)
{
sop("请选择一个格式为jpg的图片文件进行上传!!!");
return ;
}
File file = new File(args[0]);
if (!(file.exists() && file.isFile()))
{
sop("上传的图片不存在或不是文件!!");
return ;
}
if (!file.getName().endsWith(".jpg"))
{
sop("该图片格式不对!!");
return ;
}
if (file.length() > 1024*1024*5)
{
sop("上传图片过大!!");
return ;
}
Socket s = new Socket("192.168.1.100",10008);
FileInputStream fis =
new FileInputStream(file);
PrintStream out = new PrintStream(s.getOutputStream());
BufferedReader in =
new BufferedReader(new InputStreamReader(s.getInputStream()));
byte[] buf = new byte[1024];
int len = 0;
while ((len = fis.read(buf)) != -1)
{
out.write(buf,0,len);
}
s.shutdownOutput();
String line = in.readLine();
sop(line);
out.close();
fis.close();
s.close();
}
public static void sop(Object o)
{
System.out.println(o);
}
}
/*
服务端
这个服务端有个局限性。当A客户端连接上以后。被服务端获取到。服务端执行具体流程。
这时B客户端连接,只有等待。
因为服务端还没有处理完A客户端的请求,还有循环回来执行下次accept方法。所以
暂时获取不到B客户端对象。
那么为了可以让多个客户端同时并发访问服务端。
那么服务端最好就是将每个客户端封装到一个单独的线程中,这样,就可以同时处理多个客户端请求。
如何定义线程呢?
只要明确了每一个客户端要在服务端执行的代码即可。将该代码存入run方法中。
*/
class PicThread implements Runnable
{
private Socket s;
PicThread(Socket s)
{
this.s = s;
}
public void run()
{
String ip = s.getInetAddress().getHostAddress();
try
{
int count = 1;
File file = new File(ip + "(" + count + ")" + "1.png");
FileOutputStream fos = new FileOutputStream(file);
while (!file.exists())
{
fos = new FileOutputStream(new File(ip + "(" + (count++) + ")" + "1.png"));
}
BufferedInputStream in = new BufferedInputStream(s.getInputStream());
PrintStream out = new PrintStream(s.getOutputStream());
sop(ip + "...connected!");
byte[] buf = new byte[1024];
int len = 0;
while((len = in.read(buf)) != -1)
{
fos.write(buf,0,len);
}
out.println("上传成功!!!");
out.close();
in.close();
fos.close();
s.close();
}
catch (Exception e)
{
throw new RuntimeException(ip + "上传失败!!");
}
}
public static void sop(Object o)
{
System.out.println(o);
}
}
class PicServer
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10008);
while (true)
{
Socket s = ss.accept();
new Thread(new PicThread(s)).start();
}
// ss.close();
}
public static void sop(Object o)
{
System.out.println(o);
}
}
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------