Java学习—网络编程(TCP)

今天简单来介绍一下Java的另一种TCP协议下的网络编程。作为开始后先介绍一下什么是Socket(可能讲的有些晚了,应该在上一篇UDP中就进行讲解)。

Socket也就是平时所说的“套接字”,用于描述IP地址和端口。应用程序可以通过使用Socket来向网络发出请求或者应答网络的请求。

TCP协议是一种面向连接的协议,也就是说需要在通信的两端形成一条通信的线路。它与打电话的情形相似。如果想要接通一个电话的话,首先要在通信双方建立一条通信的线路(可以物理可以虚拟),当这条线路连通后便可以进行说话了。TCP协议就是需要先形成这样的一条通信线路在进行数据的传输(会使用“三次握手”原则建立这条通路)。这种方式的好处是不会丢失数据(具体的检测原则可以自行查阅相关的资料),但是接通线路的延时较长。

下面介绍一下应用的几个重要的java.net包中的用于TCP传输的几个类。

Socket:实现了客户端的套接字(两台机器间通讯的端点)。常用的构造方法有:Socket(),如果用空参的构造方法需要使用Socket类中的connect(SocketAddress endpoint)来连接指定的服务端;Socket(InetAddress address, int port),这个构造方法需要传入指定的地址和端口号,用这个构造器(或者其他重载的功能相似的构造器)表示客户端一建立就需要连接服务端。这个类中既有输入流又有输出流。常用的方法有void shutdownOutput(), 关闭客户端的输出流,相当于给流加入一个结束标记“-1”.

ServerSocket:实现服务端的套接字,用来监听和接受客户端的套接字。常用的构造方法有:ServerSocket(int port),绑定到指定端口的服务端套接字。常用的方法有Socket accept(),监听并接收客户端套接字的连接。

说一下例子完成的功能,服务器端接收客服端发来的请求(字符串),在服务器端进行输出,并将该字符串转换成大写返回给客户端,并在客户端进行输出下面说一下编写程序的流程:

首先要了解Java中需要使用的两个和Socket有关的类是Socket和ServerSocket。ServerSocket用于服务器端用于监听端口,Socket用于建立网络连接。在连接成功的时候,应用程序的服务器端和客户端都会产生一个Socket实例,而应用程序只需要操作Socket实例来完成所需要的会话。

回到例子的需求,要在服务器端建立一个ServerSocket的对象用于监听端口,端口可以随意指定,但是不能使用已被占用的端口(如80端口等),如果监听到客户端的请求则和客户端连接,和客户端进行会话。完成会话后关闭连接。对于客户端那边,用Socket对服务器端的某一个端口发送连接请求,当服务器端接受请求后便进行会话,会话完成后关闭Socket即可。

这个博客稍后给出的例子只完成了服务器端处理一个客户端,处理多个客户端的情况会在介绍完多线程编程后在给出。

客户器端的代码如下:

package lzl.demo.socket.client;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class Client 
{
	public static int PORT = 8088;
	
	public static void main(String[] args)
	{
		Socket socket = null;
		BufferedReader cbr = null;
		BufferedReader sbr = null;
		BufferedWriter sbw = null;
		String str = null;
		
		try
		{
			socket = new Socket("localhost",PORT);
			System.out.println("Socket = " + socket);
			
			cbr = new BufferedReader(new InputStreamReader(System.in));
			sbr = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			sbw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			
			System.out.println("Please input : ");
			
			while(null != (str = cbr.readLine()))
			{	
				if("quit".equalsIgnoreCase(str))
					break;
				//to write the message to the socket's output stream and add a new line
				sbw.write(str);
				sbw.newLine();
				sbw.flush();
				
				System.out.println("Server send back message: " + sbr.readLine());
			}
			
			
		}catch(Exception e)
		{
			e.printStackTrace();
		}
		finally
		{
			try
			{
				System.out.println("Close ...");
				//socket.shutdownOutput();
				cbr.close();
				sbr.close();
				sbw.close();
				socket.close();
				
			}catch(Exception e2)
			{
				e2.printStackTrace();
			}
		}
	}
}
服务器端的代码如下:

package lzl.demo.socket.server;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server
{
	public static final int PORT = 8088;
	
	public static void main(String[] args)
	{
		ServerSocket ss = null;
		Socket socket = null;
		BufferedReader br = null;
		PrintWriter pw = null;
		String str = null;
		
		try
		{
			ss = new ServerSocket(PORT);
			System.out.println("ServerSocket Start:" + ss);
			socket = ss.accept();
			System.out.println(socket + " connection accept");
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			//instead of BufferedReader, because it can flush automatic
			pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
			
			while(null != (str = br.readLine()))
			{	
				System.out.println("the client say : " + str);

				pw.println(str.toUpperCase());
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		finally
		{
			System.out.println("Close ...");
			try
			{
				br.close();
				pw.close();
				socket.close();
				ss.close();	
			}catch(Exception e2)
			{
				e2.printStackTrace();
			}
			
		}
	}
}

看完代码之后,再来一起分析一下。和net打交道肯定会涉及到io操作,所以首先需要选择流操作的类。分析方法可以参照Java学习—I/O概述篇。服务端和客户端分别运用两种对于写的流的操作对象,一个是BufferedWriter,一个是PrintWriter。PrintWriter相对于BufferedWriter来说是可以允许自动刷新的,即自动从当前的缓冲区中刷新到写入的对象中。回到客户端的代码,BufferedReader的readLine方法返回的是不包含在控制台输入结束后的那个回车的,但是判断循环的条件需要这个换行符,所以需要加上。而服务端调用pw.println时也是把这个换行符加上了。当客户端输入“quit”后,循环跳出,会关闭socket,当关闭socket后相当于在socket流中加入了结束标记“-1”,所以服务端也会跳出循环并关闭。

进一步思考一下,如果要实现从客服端想服务器端上传一个文件,当文件中的内容已经全部传送完毕时,这是服务器端的循环读是跳不出来的(因为没有文件结束的标志),所以这时候可以在客户端中加入socket.shutdownOutput()方法来往流中加入一个文件的技术符,即“-1”。当然,也可以加入另一个标记辅助判断是否文件结束,如用时间戳。

这篇关于应用TCP协议进行网络编程的博客内容就讲这多,关于网络编程这一块也就想介绍这么多,不过在后续的学习中可能还会涉及到这一块的程序,如实现多线程的时候可能还会实现这一块的东西,毕竟服务器端处理请求时需要能够同时处理多个

参考资料:

1. JDK1.7 API:http://docs.oracle.com/javase/7/docs/api/

2. Java编程思想(第四版)

3. Java核心技术 卷1/卷2

4. 传智播客毕向东Java基础视频教程


说明:

如有错误还请各位指正,欢迎大家一起讨论给出指导。

上述程序完整代码的下载链接:
https://github.com/zeliliu/BlogPrograms/tree/master/Java%E5%AD%A6%E4%B9%A0/tcp_touppercase_demo

最后更新时间:2013-04-19


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值