在解释TCP编程之前,我们先引入两个重要类:InetAddress和Socket
与一般的Java类不同的是,InetAddress类没有构造方法,所以不能直接new出一个对象。一般我们都是通过InetAddress类的静态方法获得InetAddress的对象。最常用的主要有一下几个方法:
获取本机InetAddress对象。对象中包含主机名和IP地址。
获取指定主机名或IP的InetAddress对象。
获取当前InetAddress对象的IP地址
4、getHostName()
获取当前InetAddress对象的主机名(域名)
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。在TCP通信中客户端和服务端各有一个Socket,是两台机器间通信的端点。事实上,网络通信就可两台设备的Socket之间的通信,数据在两个Socket间通过IO传输。
客户端和服务端的IO流是使用Socket.getInputStream或Socket.getOutputStream通过Socket获取网络通道中的IO对象,如图:
客户端
1)新建一个Socket对象,参数是服务器的ip(或域名)和端口号。
2)新建OutPutStream对象out,把Socket . getOutputStream传递给新建的OutputStream。
3)与正常IO流一样发送文字、文件等(out . write)
4)发送完数据后一定要发送结束标志:Socket.shutdownOutput()。代表客户端异界发送结束了,服务器端可以关闭连接了,不用再等了。
5)关闭IO流和Socket。
6)客户端接受数据与服务端的步骤 (3)(4)相同。
服务端
1)新建SreverSocket对象,参数是客户端中设置的端口号。
2)Socket socket = serverSocket.accept();
新建Socket对象,接受客户端连接。这一步骤就是用来监听参数中的端口,如果没有客户端连接到端口中,程序会阻塞,直到有客户端连接为止。
3) 新建InputStream对象in,用Socket.getInputStream( ) 把套接字中的输入流传递给in。
4)与正常的IO一样接受数据。
5)服务端想发送数据步骤同客户端的(2)(3)(4)一样
5)关闭IO流、Socket和ServerSocket。
大概思路
发送方式与1中的字节流大体相同,但是要使用字符流。因此要先用getOutputStream获取字节流对象,然后使用OutputWriter(参数是字节流对象)把字节流转换为字符流。
客户端
1)新建一个Socket对象,参数是服务器的ip(或域名)和端口号。
2)使用Socket.getOutputStream获取字节流对象,然后使用OutPutStreamWriter把字节流转换为字符流。把转换后的字符流对象作为参数放入处理流中。(处理流简单高效)
3)使用处理流发送数据
4)发送完数据后一定要发送一个换行:BufferedWriter.newLine();表示发送结束。随后flush一下,这样数据才能在流中显示
5)客户端想发送数据步骤同服务端的(2)(3)(4)一样
6)关闭IO流和Socket。
代码:
public static void main(String[] args) throws IOException { Socket socket = new Socket(InetAddress.getLocalHost(), 9999); OutputStream out = socket.getOutputStream(); out.write("hello,server".getBytes()); System.out.println("发送成功"); socket.shutdownOutput(); InputStream inC = socket.getInputStream(); int len; byte []bytes = new byte[1024]; while ((len = inC.read(bytes)) != -1){ System.out.println(new String(bytes,0,len)); } out.close(); inC.close(); socket.close(); }
服务端
1)新建SreverSocket对象,参数是客户端中设置的端口号。
2)Socket socket = serverSocket.accept();
新建Socket对象,接受客户端连接。这一步骤就是用来监听参数中的端口,如果没有客户端连接到端口中,程序会阻塞,直到有客户端连接为止。
3) 新建InputStream对象in,用Socket.getInputStream( ) 把套接字中的输入流传递给in。
4)使用InputStreamReader把字节流对象in转换为字符流对象,再装入处理流中。(处理流简单高效)
5)与正常的IO一样接受数据。接受数据不用flush
6)服务端想发送数据步骤同客户端的(2)(3)(4)一样
7)关闭IO流、Socket和ServerSocket。
代码:
public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(9999); System.out.println("服务端准备就绪,等待客户端连接......"); Socket socket = serverSocket.accept(); System.out.println("客户端连接成功!"); InputStream in = socket.getInputStream(); int len; byte []bytes = new byte[1024]; while((len = in.read(bytes)) != -1){ System.out.println(new String(bytes,0,len)); } OutputStream outS = socket.getOutputStream(); outS.write("helle,client".getBytes()); socket.shutdownOutput(); in.close(); outS.close(); socket.close(); serverSocket.close(); }
当客户端连接服务器时,我们指定了服务器的监听端口。事实上,客户端在连接服务器时也会有一个端口分配给它,但这个端口不像服务器的监听端口那样由Socket指定,而是由TCP/IP协议随机发的,是不固定的。而在高并发的情况下,大量的客户端需要TCP/IP去随机分配端口,这样就导致了65535个端口不够用。