基础知识
1. TCP协议
TCP是一种面向连接的、可靠的、基于字节流的运输层(Transport layer)通信协议。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,UDP是同一层内另一个重要的传输协议。
TCP所提供服务的主要特点:面向连接的传输;端到端的通信;高可靠性,确保传输数据的正确性,不出现丢失或乱序;全双工方式传输;采用字节流方式,即以字节为单位传输字节序列;紧急数据传送功能
TCP支持的服务:文件传送File Transfer;远程登录Remote login;计算机邮件Mail;网络文件系统(NFS);远程打印(Remote printing);远程执行(Remote execution);名字服务器(Name servers);终端服务器(Terminal servers)。
2. 端口
TCP/IP协议中提出了端口(port)的概念,用于标识网络主机上通信的软件进程。 端口实际上是一个抽象的软件结构(包括一些数据结构和I/O缓冲区)。应用程序(即进程)通过系统调用与某端口建立关联(binding)后,传输层传给该端口的数据都被相应的应用进程所接收。端口又是在网络体系结构中应用进程访问传输服务的入口点SAP(Service Access Point服务访问点)。
在TCP/IP体系中,用于存储端口号长度为16bit ,取值范围0~65535,它用于存储本地软件进程,所以仅具有本地意义。通常,端口分为:熟知端口,取值范围0~1023,为常用应用进程指定的固定值;一般端口,取值范围1024~49151,供一般程序使用;动态端口:49152~65535供某些默认服务使用,如表1所示。
表1 常用进程和熟知端口
echo |
7 |
验证2台计算机连接有效性 |
daytime |
13 |
服务器当前时间文本描述 |
ftp |
20/21 |
21用于命令,20用户数据 |
telnet |
23 |
远程登录 |
smtp |
25 |
邮件发送 |
whois |
43 |
网络管理的目录服务 |
dns |
53 |
域名解析 |
tftp |
69 |
小文件传输 |
finger |
79 |
主机用户信息 |
http |
80 |
HTTP |
pop3 |
110 |
邮局协议 |
nntp |
119 |
网络新闻传输协议, 发布Usenet新闻 |
snmp |
161 |
网络管理协议 |
rip |
520 |
路由协议 |
3. 套接字
套接字Socket原意是 “插座”,简单的说就是参与通信两方的一种约定,用套接字中的相关函数来完成通信过程。为了区分不同应用程序进程间的网络通信和连接,主要使用3个参数:通信的目的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号,通过将这3个参数结合起来,与一个Socket绑定,应用层就可以和传输层通过套接字接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。通常的表示方式为:SOCKET=(传输协议,IP,Port)。
4 . Netstat
NetStat是DOS命令,是一个监控TCP/IP网络的非常有用的工具,它可以显示路由表、实际的网络连接以及每一个网络接口设备的状态信息.Netstat用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况。
可以通过执行Netstat /help获得该应用程序选项的相关帮助。
在Java语言中,实现TCP 套接字中有两个基础类,分别为:
l Socket类: 建立一个客户端标识
l ServerSocket类: 建立一个服务器端标识
5. ServerSocket
该类实现服务器socket,一个服务器socket等待网络上的连接请求。通常操作都是基于这个请求,并且会返回一个结果给请求连接者,其类描述如图2所示。
图2 ServerSocket类描述
ServerSocket构造方法有:
ServerSocket() 创建一个空的服务端socket; |
ServerSocket(int port) 在指定端口创建一个服务端socket,; |
ServerSocket(int port, int backlog) 在指定端口创建一个服务端socket和日志; |
ServerSocket(int port, int backlog, InetAddress bindAddr) |
6. Socket
该类实现一个客户端socket,这个socket表示在通信的两台设备之间的端点,其类描述如图3所示。
图3 Socket类描述
Socket构造方法有:
Socket() 创建一个空的客户端socket; |
Socket(InetAddress address, int port) 创建一个连接指定远程地址和端口的客户端socket; |
Socket(InetAddress address, int port, InetAddress localAddr, int localPort) 在本地指定地址和端口,创建一个连接指定远程地址和端口的客户端socket; |
创建一个连接指定主机名称和端口的客户端socket; |
Socket(String host, int port, InetAddress localAddr, int localPort) 在本地指定地址和端口,创建一个连接指定远程主机名称和端口的客户端socket。 |
基本工作原理,如图所示。
1. 启动服务器端ServerSocket,监听指定端口;
2. 启动客户端Socket,连接服务器端Socket;
3. 服务器端Accept确认连接,建立通信通道;
4. 建立输入和输出流,进行通信;
5. 通信完毕,关闭Socket连接。
7. 多线程(Thread)
每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。进程也可能是整个程序或者是部分程序的动态执行。线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行。也可以把它理解为代码运行的上下文。所以线程基本上是轻量级的进程,它负责在单个程序里执行多任务。
Java里面有2个方法实现多线程,不论哪种方法都需要覆盖public void run()方法
1 继承 Thread类,比如 class MyThread extends Thread { public void run() { // 这里写上线程的内容 } public static void main(String[] args) { // 使用这个方法启动一个线程 new MyThread().start(); } } 2 实现 Runnable接口 class MyThread implements Runnable{ public void run() { // 这里写上线程的内容 } public static void main(String[] args) { // 使用这个方法启动一个线程 new Thread(new MyThread()).start(); } } 一般鼓励使用第二种方法,因为Java里面只允许单一继承,但允许实现多个接口。第二个方法更加灵活。
类及方法
1. ServerSocket
常用方法 |
|
accept() 接受客户端的连接请求; |
|
void |
bind(SocketAddress endpoint) 绑定到指定的地址和端口; |
void |
close() 关闭该ServerSocket; |
getInetAddress() 获得该ServerSocket的本地地址; |
|
int |
getLocalPort() 获得本地监听端口; |
int |
getReceiveBufferSize() 获得接收缓存尺寸; |
int |
getSoTimeout() 获得数据包超时时间; |
boolean |
isClosed()判断ServerSocket是否关闭; |
void |
setReceiveBufferSize(int size)设置接收缓存尺寸; |
void |
setSoTimeout(int timeout) 以毫秒设置数据包超时时间; |
toString() 将地址和端口作为字符串返回。 |
2. Socket
常用方法 |
|
void |
bind(SocketAddress bindpoint) 绑定socket在本地; |
void |
close() 关闭该socket; |
void |
connect(SocketAddress endpoint) 连接该socket到服务器上; |
void |
connect(SocketAddress endpoint, int timeout) 连接该socket到服务器并设置超时时间; |
getInetAddress() 获得该socket连接的地址; |
|
getInputStream() 获得该 socket的输入流; |
|
getLocalAddress() 获得该socket的本地地址; |
|
int |
getLocalPort()获得该socket的本地端口; |
getOutputStream()获得该 socket的输出流; |
|
int |
getPort()获得该 socket的远端端口; |
int |
getReceiveBufferSize() 获得该Socket的接收缓存容量; |
int |
getSendBufferSize()获得该Socket的发送缓存容量; |
int |
getSoTimeout() 获得超时时间; |
boolean |
isClosed() 判断该socket是否关闭; |
boolean |
isConnected() 判断该socket是否连接; |
void |
setKeepAlive(boolean on) 设置该socket的连接状态; |
void |
setReceiveBufferSize(int size) 设置接收缓存容量; |
void |
setSendBufferSize(int size) 设置发送缓存容量; |
void |
setSoTimeout(int timeout) 设置毫秒级的超时时间; |
toString() 将该socket转换为字符串输出。 |
代码示例
对指定IP范围内主机上指定PORT范围进行连接测试
1 import java.net.*; 2 import java.io.*; 3 4 class myJava{ 5 public static void main(String [] args){ 6 String host = "222.24.16."; 7 String host_ip = null; 8 Socket cs = null; 9 for(int i=1; i<17; i++){ 10 host_ip = host + i; 11 for(int j=0; j<65536; j++){ 12 try{ 13 cs = new Socket(host_ip, j); 14 System.out.println(host_ip + " : " + j); 15 }catch(Exception e){ 16 System.err.println(); 17 } 18 } 19 } 20 } 21 }
简单的单线程,一问一答式通信的TCP Server和TCP Client
服务端:
View Code
客户端:
1 //客户端 2 import java.io.*; 3 import java.net.*; 4 public class MyClient{ 5 public static void main(String args[]) throws IOException{ 6 Socket comSocket = null; 7 PrintStream out = null; 8 DataInputStream in = null; 9 try{ 10 //建立socket连接 11 comSocket = new Socket("localhost", 1080);//相指定服务器的端口发出连接请求,注意服务器开放的TCP端口号 12 //分别对应服务器端的O/I流 13 in = new DataInputStream(comSocket.getInputStream()); 14 out = new PrintStream(comSocket.getOutputStream()); 15 }catch(UnknownHostException e){ 16 System.err.println("Can't find the Server host"); 17 System.exit(0); 18 }catch(IOException e){ 19 System.err.println("Can't get I/O for the Connection"); 20 System.exit(0); 21 } 22 23 DataInputStream stdIn = new DataInputStream(System.in); 24 String fromServer, fromUser; 25 26 while((fromServer = in.readLine()) != null){ 27 System.out.println("Server:" + fromServer); 28 if(fromServer.equals("bye")) break; 29 fromUser = stdIn.readLine(); 30 if(fromUser != null){ 31 System.out.println("Client:" + fromUser); 32 out.println(fromUser); 33 } 34 } 35 out.close(); 36 in.close(); 37 stdIn.close(); 38 comSocket.close(); 39 } 40 }
探测目标计算机开放的TCP 端口
1 import java.io.*; 2 import java.net.*; 3 4 public class ScanPort{ 5 public static void main(String args[]) throws IOException{ 6 Socket comSocket = null; 7 8 for(int i=0;i<1024; i++){ 9 try{ 10 //建立socket连接 11 comSocket = new Socket("localhost", i);//发出连接请求 12 13 System.out.println("Can get I/O for the Connection, Port:" + i); 14 }catch(UnknownHostException e){ 15 System.err.println("Can't find the Server host"); 16 //System.exit(0); 17 }catch(IOException e){ 18 System.err.println("Can't get I/O for the Connection, Port:" + i); 19 //System.exit(0); 20 } 21 } 22 23 try{ 24 comSocket.close(); 25 }catch(Exception e){} 26 } 27 }
TCP传输文件
服务端:
1 //采用TCP进行通讯,需要服务器和客户端两个部分,因此程序包含SendFileServer.java和SendFileClient.java两个部分。两个文件的IP,端口都在程序中指定, 传输的文件路径也在程序中指定 2 ////////////SendFileServer.java 3 import java.io.FileInputStream; 4 import java.io.IOException; 5 import java.io.OutputStream; 6 import java.net.ServerSocket; 7 import java.net.Socket; 8 /* 9 * 用TCP进行文件传输 10 * 此文件为服务器文件 11 * 当接受到客户端的请求之后,先向其传输文件名 12 * 当客户端接受完毕之后,向客户端传输文件 13 * */ 14 public class SendFileServer implements Runnable{ 15 // 服务器监听端口 16 private static final int MONITORPORT = 12345; 17 private Socket s ; 18 public SendFileServer(Socket s) { 19 super(); 20 this.s = s; 21 } 22 public static void server() 23 { 24 try { 25 // 创建服务器socket 26 ServerSocket ss = new ServerSocket(MONITORPORT); 27 while(true) 28 { 29 // 接收到一个客户端连接之后,创建一个新的线程进行服务 30 // 并将联通的socket传给该线程 31 Socket s = ss.accept(); 32 new Thread(new SendFileServer(s)).start(); 33 } 34 } catch (IOException e) { 35 // TODO Auto-generated catch block 36 e.printStackTrace(); 37 } 38 } 39 /** 40 * @param args 41 */ 42 public static void main(String[] args) { 43 // TODO Auto-generated method stub 44 SendFileServer.server(); 45 } 46 @Override 47 public void run() { 48 // TODO Auto-generated method stub 49 byte[] buf = new byte[100]; 50 OutputStream os=null; 51 FileInputStream fins=null; 52 try { 53 os = s.getOutputStream(); 54 // 文件路径 55 String filePath = "/home/newton/cangjie.mp3"; 56 // 文件名 57 String fileName = "cangjie.mp3"; 58 System.out.println("将文件名:"+fileName+"传输过去"); 59 //先将文件名传输过去 60 os.write(fileName.getBytes()); 61 System.out.println("开始传输文件"); 62 //将文件传输过去 63 // 获取文件 64 fins = new FileInputStream(filePath); 65 int data; 66 // 通过fins读取文件,并通过os将文件传输 67 while(-1!=(data = fins.read())) 68 { 69 os.write(data); 70 } 71 System.out.println("文件传输结束"); 72 } catch (IOException e) { 73 // TODO Auto-generated catch block 74 e.printStackTrace(); 75 }finally 76 { 77 try { 78 if(fins!=null) fins.close(); 79 if(os!=null) os.close(); 80 this.s.close(); 81 } catch (IOException e) { 82 e.printStackTrace(); 83 } 84 } 85 } 86 }
客户端:
1 import java.io.FileOutputStream; 2 import java.io.IOException; 3 import java.io.InputStream; 4 import java.net.InetSocketAddress; 5 import java.net.Socket; 6 /* 7 * 用TCP进行文件传输 8 * 此文件为客户端文件 9 * 连接上服务器之后,直接接受文件 10 * 11 * */ 12 public class SendFileClient { 13 private static final String SERVERIP = "127.0.0.1"; 14 private static final int SERVERPORT = 12345; 15 private static final int CLIENTPORT = 54321; 16 /** 17 * @param args 18 */ 19 public static void main(String[] args) { 20 // TODO Auto-generated method stub 21 // 用来接受传输过来的字符 22 byte[] buf = new byte[100]; 23 Socket s = new Socket(); 24 try { 25 // 建立连接 26 s.connect(new InetSocketAddress(SERVERIP,SERVERPORT), CLIENTPORT); 27 InputStream is = s.getInputStream(); 28 // 接收传输来的文件名 29 int len = is.read(buf); 30 String fileName = new String(buf,0,len); 31 System.out.println(fileName); 32 //接收传输来的文件 33 FileOutputStream fos = new FileOutputStream(fileName); 34 int data; 35 while(-1!=(data = is.read())) 36 { 37 fos.write(data); 38 } 39 is.close(); 40 s.close(); 41 } catch (IOException e) { 42 // TODO Auto-generated catch block 43 e.printStackTrace(); 44 } 45 } 46 }