Java学习---TCP Socket的学习

时间:2024-11-21 18:06:38

基础知识

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所示。

Java学习---TCP Socket的学习

图2 ServerSocket类描述

ServerSocket构造方法有:

ServerSocket() 创建一个空的服务端socket;

ServerSocket(int port) 在指定端口创建一个服务端socket,;

ServerSocket(int port, int backlog) 在指定端口创建一个服务端socket和日志;

ServerSocket(int port, int backlog, InetAddress bindAddr) 
 在指定端口和地址上创建一个服务端socket和日志。

 6. Socket

  该类实现一个客户端socket,这个socket表示在通信的两台设备之间的端点,其类描述如图3所示。

Java学习---TCP Socket的学习

图3 Socket类描述

Socket构造方法有:

Socket() 创建一个空的客户端socket;

Socket(InetAddress address, int port)

创建一个连接指定远程地址和端口的客户端socket;

Socket(InetAddress address, int port, InetAddress localAddr, int localPort)

在本地指定地址和端口,创建一个连接指定远程地址和端口的客户端socket;

Socket(String host, int port)

创建一个连接指定主机名称和端口的客户端socket;

Socket(String host, int port, InetAddress localAddr, int localPort)

在本地指定地址和端口,创建一个连接指定远程主机名称和端口的客户端socket。

基本工作原理,如图所示。

1. 启动服务器端ServerSocket,监听指定端口;

2. 启动客户端Socket,连接服务器端Socket;

3. 服务器端Accept确认连接,建立通信通道;

4. 建立输入和输出流,进行通信;

5. 通信完毕,关闭Socket连接。

Java学习---TCP 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

常用方法

Socket

accept() 接受客户端的连接请求;

void

bind(SocketAddress endpoint) 绑定到指定的地址和端口;

void

close() 关闭该ServerSocket;

InetAddress

getInetAddress() 获得该ServerSocket的本地地址;

int

getLocalPort() 获得本地监听端口;

int

getReceiveBufferSize() 获得接收缓存尺寸;

int

getSoTimeout() 获得数据包超时时间;

boolean

isClosed()判断ServerSocket是否关闭;

void

setReceiveBufferSize(int size)设置接收缓存尺寸;

void

setSoTimeout(int timeout) 以毫秒设置数据包超时时间;

String

toString() 将地址和端口作为字符串返回。

2. Socket

常用方法

void

bind(SocketAddress bindpoint) 绑定socket在本地;

void

close() 关闭该socket;

void

connect(SocketAddress endpoint) 连接该socket到服务器上;

void

connect(SocketAddress endpoint, int timeout) 连接该socket到服务器并设置超时时间;

InetAddress

getInetAddress() 获得该socket连接的地址;

InputStream

getInputStream() 获得该 socket的输入流;

InetAddress

getLocalAddress() 获得该socket的本地地址;

int

getLocalPort()获得该socket的本地端口;

OutputStream

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) 设置毫秒级的超时时间;

String

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();
}
}
}