TCP客户端;
import java.net.*;
import java.io.*;
public class TCPClient
{
public static void main(String[] args) throws Exception
{
Socket s = new Socket("127.0.0.1", 6666); //8行
//第一个参数是要连接到的主机IP,即表示要连接到那台机器上,127.0.0.1表示链接到本机, 第二个参数表示要连接到哪个网络程序上
//new Socket("127.0.0.1", 6666) 一旦构造对象成功,就意味着这时已经产生了一个试图和IP为"127.0.0.1" 端口为6666的网络程序进行连接的请求
//如果这时IP为"127.0.0.1" 的机器上正好有一个网络程序在监听6666端口,这时连接就会建立成功
//所谓连接成功是指这两个网络程序建立了一个通信管道,双方都可以通过这个管道写数据和读数据,并且一方写入的数据实际就是另一方要读取的数据,一方要读取的数据实际就是另一放要写入的数据, 即这个管道实际是双向的,任何一方都可以通过getInputStream() 和 getOutputStream()获取输入流和输出流两个流,并且一方的输入流实际就是另一方的输出流,这是同一个流,一方的输出流实际就是另一方的输入流,这也是同一个流 即一个通信管道两个流,双方各自都可以得到两个流,这两个流分别是输入流和输出流
//记住:一旦new出了Socket对象,该对象就会自动向服务器端发送连接请求,如果连接不成功则程序立即终止
//因此我们在TCP编程的客户端是找不到请求连接的代码,但是在服务器端却存在监听客户端连接请求的代码, 代码类似于:ss.accept()
OutputStream os = s.getOutputStream(); //16行 一旦连接成功,相当于建立了一根管道,这根管道在客户端相当于输出流管道,在服务器端相当于输入流管道!
//程序如果能执行到16行说明已经连接成功,因为8行执行完后就会自动立即发送一个连接请求,如果服务器端没有打开,或者服务器端虽然已经打开但是却没有监听客户端的连接请求,则程序会立即终止,是不会执行到16行的, 如果执行到了16行那说明连接成功了
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF("同志们好");
dos.flush();
dos.close();
s.close();
}
}
TCP服务端:
import java.net.*;
import java.io.*;
public class TCPServer
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(6666);//8行 6666是端口号,表示该服务器程序在监听6666端口是否有客户端程序的连接
//new出的ServerSocket对象ss并不会自动监听客户端有没有向6666端口发送请求,要想监听6666端口是否有客户端的请求,则必须的调用ss对象的accept方法
//如果本程序只有8行这一行的代码的话,本程序运行时将无任何输出结果并会立即终止
while (true)
{
Socket s = ss.accept(); //accept()功能:等待客户端的连接,没有连接,则继续监听,程序停滞不前,如果接收到客户端的一个连接,则自动将该连接封装为一个Socket对象,程序将不再阻塞,而是继续放下执行
//这里的s实际是链接到客户端的s,服务器端的s.getInputStream() 和客户端的s.getOutputStream()实际是同一根管道
//accept()是阻塞式方法,如果接收不到客户端的连接,则程序将停止不前
System.out.println("一个连接已经建立!!!");
DataInputStream dis = new DataInputStream(s.getInputStream()); //通过Socket对象可以获得InputStream和OutputStream两个管道
System.out.println(dis.readUTF()); //readUTF()方法也是阻塞式方法,如果接收不到客户端数据,则程序将停止不前
dis.close(); //DataInputStream 和 InputStream 流中都没有flush方法 DataOutputStream 和 OutputStream 流中都有flush方法
s.close();
}
}
}
UDP客户端:
import java.net.*;
import java.io.*;
public class TestUDPClient
{
public static void main(String args[]) throws Exception
{
//定义码头ds
DatagramSocket ds = new DatagramSocket();
//13行到23行完成的功能是: 定义可以发送数据的集装箱dp,dp中保存着n的二进制代码
long n = 10000L; //13行
ByteArrayOutputStream baos = new ByteArrayOutputStream(); //注意ByteArrayOutputStream的所有构造函数都没有byte[] buf这样的形参,即定义ByteArrayOutputStream流对象时是不能指定byte数组的,因为这个连接到的byte数组是由ByteArrayOutputStream自动生成的 9行 API:"public ByteArrayOutputStream(): 创建一个新的 byte 数组输出流。缓冲区的容量最初是 32 字节,如有必要可增加其大小。 "
//9行代码一旦执行完毕,意味着两点: 1、在内存中生成了一个大小为32个字节的byte数组 2、有一根叫做baos的管道已链接到了该byte数组中,并且可以通过这个管道向该数组中写入数据
//虽然此时可以通过baos向baos所连接到的在内存中分配好的byte数组中写入数据,但是ByteArrayOutputStream流并没有提供可以直接把long类型数据直接写入ByteArrayOutputStream流所连接到的byte数组中的方法, 简单说我们没法通过baos向baos所连接到的byte数组中写入long类型的数据, 查API文档可以发现: ByteArrayOutputStream流中并没有类似writeLong()这样的方法,但是DataOutputStream流中却有writeLong() writeFloat()等方法
DataOutputStream dos = new DataOutputStream(baos);
dos.writeLong(n); //把n变量所代表的10000L写入dos所依附的baos管道所连接到的内存中的大小为32字节的byte数组中
byte[] buf = baos.toByteArray(); //DataOutputStream 流中并没有toByteArray()方法,但是ByteArrayOutputStream 流中却有toByteArray()方法, 所以不可以把baos 改为dos,否则编译时会出错! ByteArrayOutputStream流中toByteArray()方法的含义,摘自API“创建一个新分配的 byte 数组。其大小是此输出流的当前大小,并且缓冲区的有效内容已复制到该数组中”
DatagramPacket dp = new DatagramPacket(buf, buf.length,
new InetSocketAddress("127.0.0.1", 5678)
); //23行
//在码头上把集装箱中的数据发送给对方
ds.send(dp);
//关闭码头
ds.close();
}
}
UDP服务端:
import java.net.*;
import java.io.*;
public class TestUDPServer
{
public static void main(String args[]) throws Exception
{
//定义码头
DatagramSocket ds = new DatagramSocket(5678); //5678表示该码头占用的是5678这个编号,因为一台计算机可以有多个码头接收多个数据,这些码头用不同的编号来表示,这些编号的专业术语就是端口号
//定义可以用来接受数据的集装箱
byte buf[] = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
try
{
while(true)
{
//在码头上用集装箱接受对方发送过来的数据
ds.receive(dp); //注意:本语句执行完毕就意味着,dp数据包中就已经含有了从客户端接收过来的数据
//从集装箱中取出对方发送过来的数据
ByteArrayInputStream bais = new ByteArrayInputStream(dp.getData()); //1、 ByteArrayInputStream的内核必须是个字节数组,并且是从该字节数组中读取数据 2、dp.getData()表示把dp集装箱中的数据转化为一个字节数组并返回该字节数组
DataInputStream dis = new DataInputStream(bais);
System.out.println(dis.readLong());
}
}
catch (Exception e)
{
e.printStackTrace();
ds.close(); //关闭码头
}
}
}
/*
在JDK 1.6中的运行结果是:
----------------------------------
10000
数据源自: 127.0.0.1 : 1464
10000
数据源自: 127.0.0.1 : 1471
10000
数据源自: 127.0.0.1 : 1474
----------------------------------
*/