基础知识
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范围进行连接测试
import java.net.*;
import java.io.*; class myJava{
public static void main(String [] args){
String host = "222.24.16.";
String host_ip = null;
Socket cs = null;
for(int i=1; i<17; i++){
host_ip = host + i;
for(int j=0; j<65536; j++){
try{
cs = new Socket(host_ip, j);
System.out.println(host_ip + " : " + j);
}catch(Exception e){
System.err.println();
}
}
}
}
}
简单的单线程,一问一答式通信的TCP Server和TCP Client
服务端:
客户端:
//客户端
import java.io.*;
import java.net.*;
public class MyClient{
public static void main(String args[]) throws IOException{
Socket comSocket = null;
PrintStream out = null;
DataInputStream in = null;
try{
//建立socket连接
comSocket = new Socket("localhost", 1080);//相指定服务器的端口发出连接请求,注意服务器开放的TCP端口号
//分别对应服务器端的O/I流
in = new DataInputStream(comSocket.getInputStream());
out = new PrintStream(comSocket.getOutputStream());
}catch(UnknownHostException e){
System.err.println("Can't find the Server host");
System.exit(0);
}catch(IOException e){
System.err.println("Can't get I/O for the Connection");
System.exit(0);
} DataInputStream stdIn = new DataInputStream(System.in);
String fromServer, fromUser; while((fromServer = in.readLine()) != null){
System.out.println("Server:" + fromServer);
if(fromServer.equals("bye")) break;
fromUser = stdIn.readLine();
if(fromUser != null){
System.out.println("Client:" + fromUser);
out.println(fromUser);
}
}
out.close();
in.close();
stdIn.close();
comSocket.close();
}
}
探测目标计算机开放的TCP 端口
import java.io.*;
import java.net.*; public class ScanPort{
public static void main(String args[]) throws IOException{
Socket comSocket = null; for(int i=0;i<1024; i++){
try{
//建立socket连接
comSocket = new Socket("localhost", i);//发出连接请求 System.out.println("Can get I/O for the Connection, Port:" + i);
}catch(UnknownHostException e){
System.err.println("Can't find the Server host");
//System.exit(0);
}catch(IOException e){
System.err.println("Can't get I/O for the Connection, Port:" + i);
//System.exit(0);
}
} try{
comSocket.close();
}catch(Exception e){}
}
}
TCP传输文件
服务端:
//采用TCP进行通讯,需要服务器和客户端两个部分,因此程序包含SendFileServer.java和SendFileClient.java两个部分。两个文件的IP,端口都在程序中指定, 传输的文件路径也在程序中指定
////////////SendFileServer.java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
* 用TCP进行文件传输
* 此文件为服务器文件
* 当接受到客户端的请求之后,先向其传输文件名
* 当客户端接受完毕之后,向客户端传输文件
* */
public class SendFileServer implements Runnable{
// 服务器监听端口
private static final int MONITORPORT = 12345;
private Socket s ;
public SendFileServer(Socket s) {
super();
this.s = s;
}
public static void server()
{
try {
// 创建服务器socket
ServerSocket ss = new ServerSocket(MONITORPORT);
while(true)
{
// 接收到一个客户端连接之后,创建一个新的线程进行服务
// 并将联通的socket传给该线程
Socket s = ss.accept();
new Thread(new SendFileServer(s)).start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
SendFileServer.server();
}
@Override
public void run() {
// TODO Auto-generated method stub
byte[] buf = new byte[100];
OutputStream os=null;
FileInputStream fins=null;
try {
os = s.getOutputStream();
// 文件路径
String filePath = "/home/newton/cangjie.mp3";
// 文件名
String fileName = "cangjie.mp3";
System.out.println("将文件名:"+fileName+"传输过去");
//先将文件名传输过去
os.write(fileName.getBytes());
System.out.println("开始传输文件");
//将文件传输过去
// 获取文件
fins = new FileInputStream(filePath);
int data;
// 通过fins读取文件,并通过os将文件传输
while(-1!=(data = fins.read()))
{
os.write(data);
}
System.out.println("文件传输结束");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally
{
try {
if(fins!=null) fins.close();
if(os!=null) os.close();
this.s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
/*
* 用TCP进行文件传输
* 此文件为客户端文件
* 连接上服务器之后,直接接受文件
*
* */
public class SendFileClient {
private static final String SERVERIP = "127.0.0.1";
private static final int SERVERPORT = 12345;
private static final int CLIENTPORT = 54321;
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// 用来接受传输过来的字符
byte[] buf = new byte[100];
Socket s = new Socket();
try {
// 建立连接
s.connect(new InetSocketAddress(SERVERIP,SERVERPORT), CLIENTPORT);
InputStream is = s.getInputStream();
// 接收传输来的文件名
int len = is.read(buf);
String fileName = new String(buf,0,len);
System.out.println(fileName);
//接收传输来的文件
FileOutputStream fos = new FileOutputStream(fileName);
int data;
while(-1!=(data = is.read()))
{
fos.write(data);
}
is.close();
s.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}