基于TCP/IP协议的Socket编程
1 TCP/IP协议
2 套接字(Socket)
2.1 Client/Server模式
2.2 套接字(Socket)概念
3 Socket类和ServerSocket类
3.1 Socket类
3.2 ServerSocket类
4 Socket编程应用举例
4.1 Socket编程的基本步骤
4.2单客户/服务器Socket编程应用举例
4.3多客户/服务器Socket编程应用举例本章小结
1 TCP/IP协议
TCP/IP协议(传输控制协议/网际协议)是一种网络通信协议,是一种面向连接的可靠的传输协议。它规范了网络上的所有通信设备,尤其是一个主机与另一个主机之间的数据传输格式以及传送方式。TCP/IP是Internet的基础协议。
TCP/IP协议的基本传输单位是数据包(datagram)。TCP协议负责把数据分成若干个datagram,并给每个datagram加上包头;IP在每个包头上再加上接收端主机地址,以确定数据传输的目的地。如果传输过程中出现数据丢失或数据失真等异常情况,TCP协议会自动要求数据重发,并重新分包。TCP保证数据传输的质量,IP协议保证数据的传输。
TCP/IP协议数据的传输是基于TCP/IP模型的4层结构:应用层、传输层、网络层和网络接口层。
1、传输层协议
有两个主要的传输层协议:传输控制协议(TCP)和用户数据报协议(UDP)。
(1)传输控制协议(TCP)TCP传输数据建立在面向连接的基础上,实现了一种“虚电路”的概念。双方通信之前,先建立连接,然后双方即可在其上发送数据流,发送完毕后再关闭连接。这种数据交换方式的优点是效率高,缺点是建立连接和关闭连接需要额外的开销。
(1)TCP协议主要提供下列服务:
- 可靠的数据传输
- 面向连接的虚电路
- 缓冲的传输
- 重新排序
- 多路复用技术
- 高效的、全双工传输
- 流量控制
TCP协议提供的是一种可靠的数据流服务。得到的是一个顺序的无差错的数据流。
(2)用户数据报协议(UDP)。UDP是比TCP简单得多的协议,是对IP协议组的扩充。
由于大多数网络应用程序都在同一台机器上运行,计算机上必须能够确保目的地址机器上的软件程序能从源地址机器处获得数据包,以及源计算机能收到正确的回复。这是通过使用UDP 的“端口号”完成的。源端口号标识了请求域名服务的本地计算机的应用程序,同时需要将所有由目的主机生成的响应包都指定到源主机的这个端口上。
(3)UDP与TCP比较
与TCP不同,UDP提供的是面向无连接、“不可靠”的数据报服务。它不提供对IP 协议的可靠机制、流控制以及错误恢复功能等。
由于UDP比较简单,比TCP负载消耗少。主要用于不需要TCP排序和流量控制能力而是自己完成这些功能的应用程序。
2、应用层协议
在应用层中定义了许多高层协议:
(1)TELNET(远程终端访问)。TELNET连接是一个TCP连接。允许一台计算机上的用户登录到远程计算机上并进行交互。支持终端到终端的连接及进程到进程分布式计算的通信。
(2)FTP(文件传输协议)FTP使得主机间可以共享文件。FTP使用TCP生成一个虚拟连接用于控制信息,然后再生成一个单独的TCP连接用于数据传输。FTP主要提供文件共享、支持间接使用远程计算机、使用户不因各类主机文件存储器系统的差异而受影响、可靠且有效的传输数据等功能。
(3)SMTP(简单邮件传输协议)SMTP支持在因特网上传递电子邮件,用于可靠且有效的数据传输。它保证把各种类型的电子邮件通过这一协议从一台邮件服务器发送到另一台邮件服务器上。
(4)DNS(域名服务)DNS提供域名到IP地址的转换,允许对域名资源进行分散管理。
(5)HTTP(超文本传输协议)是Web浏览器和Web服务器之间通信的标准协议。它指明客户端如何与服务器建立连接,客户端如何从服务器请求数据,服务器如何响应请求,最后连接如何关闭等。HTTP连接是一个TCP连接。
TCP/IP模型中还有其他一些协议,如地址解析协议(ARP)、因特网控制消息协议(ICMP)等。
2 套接字(Socket)
套接字允许程序员把网络连接当成一个流(Stream),并向这个流读写字节。Socket对程序员掩盖了网络的低层细节,如纠错、包大小、包重传、网络地址等。本节将介绍Client/Server模式、套接字(Socket)概念。
2.1 Client/Server模式
计算机网络最重要的3个功能是数据通信、资源共享和分布处理。为了满足这些功能需求,计算机网络产生了两种重要的应用模式:客户端/服务器(Client/Server,C/S)模式和浏览器/服务器(Brower/Server,B/S)模式。采用何种应用模式在编程实现时非常重要。下面将主要介绍C/S模式。
在C/S模式下,客户向服务器发出服务请求,服务器接收到请求后,提供相应的服务。其工作方式可通过现实生活中一个例子来说明。在一个酒店中,顾客向服务员点菜,服务员把点菜单通知厨师,厨师按点菜单做好菜后让服务员端给顾客,这就是一种C/S工作方式。如果把酒店看作一个系统,服务员就是客户端,厨师就是服务器,这种系统分工和协同工作的方式就是C/S的工作模式。
由此可见,工作在C/S模式下的系统被分成两大部分:
(1)客户端部分:为每个用户所专有的,负责执行前台功能。
(2)服务器部分:由多个用户共享的信息与功能,招待后台服务。
二是网间进程通信完全是异步的,相互通信的进程既不存在父子关系,又不共享内存缓冲区,因此需要一种机制为希望通信的进程建立联系,为二者的数据交换提供同步,这就是基于C/S的TCP/IP。
(1)服务器侦听相应端口的请求;
(2)客户端发出一个请求;
(3)服务器接收到此请求;
(4)服务器处理这个请求,并把处理结果返回给客户端。
从上面所描述的过程可知:客户与服务器进程的作用是非对称的,因此编码不同。服务进程一般是先于客户请求而启动的。只要系统运行,该服务进程一直存在,直到正常或强迫终止。
2.2 套接字(Socket)概念
数据在Internet中是以有限大小的分组的形式传输的。一个分组是一个数据报,包括首部和负载。首部包含目的的地址和端口、源地址和端口以及用于保证可靠传输的各种其他管理信息。负载包含数据本身。但由于分组长度有限,通常必须将数据分解为多个分组,在目的地再重新组合。在传输过程中,有可能发生一个或多个分组丢失或被破坏的情况,此时就需要重传分组。或者分组乱序到达,则需要重新排序。这些工作将是非常繁重的。
幸运的是,套接字(Socket)的出现使我们不必关心这些事情,我们只需要把网络看成一个流,就象对文件操作一样对这个流进行操作就行了。
- 流套接字提供一个面向连接的、可靠的数据传输服务,保证数据无差错、无重复、按顺序发送。具有流量控制功能。数据被看作字节流,无长度限制。TCP即是一种基于流套接字的通信协议。
- 数据报套接字提供一个面向无连接的数据传输服务,不保证数据传输是可靠的、有序的、无重复的。UDP即是一种基于数据报套接字的通信协议。
本章主要介绍基于TCP/IP协议的C/S模式下的Socket编程。
在两个网络应用程序发送和接收信息时都需建立一个可靠的连接,流套接字依靠TCP来保证信息正确到达目的地。实际上,IP分组有可能在网络中丢失或者在传送过程中发生错误。当任何一种情况发生时,作为接收方的TCP将请求发送方TCP重发这个IP分组。因此,两个流套接字之间建立的连接是可靠的连接。
(1)连接远程主机
(2)发送数据
(3)接收数据
(4)关闭连接
(5)绑定端口
(6)侦听入站数据
(7)在所绑定端口上接收远程主要的连接
(2)打开连接到Socket的输入输出流。Socket对象包含两个流:一个是输入流,表示流入的数据流,其作用是接收数据;一个是输出流,表示流出的数据流,其作用是向外发送数据,其流操作与I/O流相同。
3 Socket类和ServerSocket类
在Java语言中,服务器端Socket使用ServerSocket类,客户端Socket使用Socket类,由此来区别服务器端和客户端。
3.1 Socket类
java.net .Socket类是Java用来实现客户端TCP操作的基础类,在Java编程中一切与TCP有关的操作包括连接的建立、传输、连接关闭及Socket选项的设置都由Socket类负责,这个类本身使用直接代码通过主机操作系统的本地TCP栈进行通信。Socket类的输入输出都是通过流来实现的。 Socket类常用方法如下:
(1)public Socket(String host,int port) throws UnknownHostException, IOException功能:在客户端以指定的服务器host和端口号port创建一个Socket对象,并向服务器发出连接请求。
参数:
若域名服务器无法解析该服务器名或域名服务器没有运行,此时host无法可知,则抛出UnknownHostException异常;若生成Socket时发生I/O错误则抛出IOException异常。
(2)public Socket(InetAddress address,int port) throws IOException功能:在客户端以指定的服务器地址address和端口号port创建一个Socket对象,并向服务器发出连接请求。
参数:
若生成Socket时发生I/O错误则抛出IOException异常。
(3)public Socket(String host,int port,boolean stream) throws IOException功能:在客户端以指定的服务器host和端口号port创建Socket对象,并向服务器发出连接请求。如果stream值为true,则创建流Socket对象,否则创建数据报Socket 对象。
参数:
若生成Socket时发生I/O错误则抛出IOException异常。
(4)public Socket(InetAddress address,int port,boolean stream) throws IOException功能:在客户端以指定的服务器IP地址address和端口号port创建Socket对象,并向服务器发出连接请求。如果stream值为true,则创建流Socket对象,否则创建数据报Socket 对象。
参数:
若生成Socket时发生I/O错误则抛出IOException异常。
(5)public Socket(InetAddress address,int port,InetAddress localAddr,int localPort) throws IOException功能:生成一个Socket并且连接到由address指定的服务器的端口port上。该Socket将捆绑到由localAddr指定的本地主机的localPort端口上。
参数:
若生成Socket时发生I/O错误则抛出IOException异常。
(6)public Socket(String host,int port, InetAddress localAddr,int localPort) throws IOException功能:生成一个Socket并且连接到由host指定的服务器的端口port上。该Socket将捆绑到由localAddr指定的本地主机的localPort端口上。
参数:
若生成Socket时发生I/O错误则抛出IOException异常。
(7)protected Socket(SocketImpl impl) throws SocketException功能:根据用户指定的SocketImpl生成一个无连接的socket。
参数:
建立无连接失败时抛出SocketException异常。
客户端流Socket的创建可通过使用Socket类的构造函数完成。构造函数不仅可以创建Socket对象,并且会尝试连接服务器端的Socket。可通过这个方法测试某个端口的连接是否允许。
2.与Socket数据读写有关的常用方法
(1)public InetAddress getInetAddress()
(2)public InetAddress getLocalAddress()
(3)public int getPort()
(4)public int getLocalPort()
(5)public InputStream getInputStream() throws IOException
(6)public OutputStream getOutputStream() throws IOException
(7)public String toString()
3.关闭Socket连接的方法
public synchronized void close() throws IOException
若在关闭Socket连接时发生I/O错误则抛出IOException异常。
【例 1】
//创建socket连接后获取socket连接的相关信息
import java.net.*;
import java.io.*;
public class SocketInfo {
public static void main(String args[]){
try{
Socket sktobj = new Socket("www.sina.com.cn",8080);
System.out.println("1:connect to "+sktobj.getInetAddress()+"on port "+sktobj.getPort());
System.out.println("2:from port "+sktobj.getLocalPort()+" of"+sktobj.getLocalAddress());
}
catch(UnknownHostException e){System.out.println(e);}
catch(IOException e){System.out.println(e);}
}
}
图 4
【例 2】
//客户端向服务器发送一个字符串,服务器接收并返回
import java.net.*;
import java.io.*;
public class SocketAppClient{
public static void main(String args[]){
int port=134;
try{
Socket sc = new Socket("127.0.0.1",port);//创建本地socket连接
OutputStream out = sc.getOutputStream();
DataOutputStream dout = new DataOutputStream(out);
//获取输出流,并创建相应的数据输出流
dout.writeUTF("Tom"); //将tom写入输出流
InputStream in = sc.getInputStream();
DataInputStream din = new DataInputStream(in);
//获取输入流,并创建相应的数据输入流
String str = din.readUTF();
System.out.println(str);
in.close();
out.close();
sc.close();
}
catch(UnknownHostException e){System.out.println(e);}
catch(IOException e){System.out.println(e);}
}
}
运行结果:单方通信,缺服务器
图 5
3.2 ServerSocket类
ServerSocket类用在服务器端,侦听和响应客户端的连接请求,并接收客户端发送的数据。ServerSocket类的主要任务是在服务器端耐心地等候客户端的连接请求,一旦客户端应用程序申请建立一个Socket连接,ServerSocket类就会通过accept()方法返回一个对应的服务器端Socket对象,以便进行直接通信。从两台计算机连接成功时起,服务器端与客户端就得到了一个真正的“Socket-Socket”连接。
(1)public ServerSocket(int port) throws IOException
若无法创建Socket并绑定于所请求的端口号则抛出IOException异常。
(2)public ServerSocket(int port,int backlog) throws IOException
若无法创建Socket并绑定于所请求的端口号则抛出IOException异常。
(3)public ServerSocket(int port,int backlog,InetAddress bindAddr) throws IOException
若无法创建Socket并绑定于所请求的端口号则抛出IOException异常。
【例 3】
//采用ServerSocket类查询端口号为130~140的端口号中哪个端口可创建连接。
import java.net.*;
import java.io.*;
public class PortScan2{
public static void main(String args[]){
String host="localhost";
for(int i=130;i<140;i++)
try{ServerSocket a=new ServerSocket(i);
//在端口号i创建连接
System.out.println("there is a server on port:"+i);
//若可以创建连接,则输出提示信息
}catch(IOException e){System.out.println(e);
}//若端口已被占用,则抛出异常
}
}
运行结果:断口号为135的端口已被占用,当试图在端口135建立连接时抛出异常,即端口已被绑定.
图 6
2.ServerSocket类的常用方法
(1)public InetAddress getInetAddress()功能:获取服务器端Socket的IP地址。
(2)public int getLocalPort()功能:获取服务器正侦听的端口号。
(3)public Socket accept() throws IOException功能:在服务器端指定端口侦听客户端发出的连接请求并与之连接。该方法一直阻塞直到连接成功。该方法返回一个新的Socket对象,通过该对象与客户端进行通信。若等待连接时发生I/O错误则抛出IOException异常。
(4)public String toString()功能:返回该socket的字符串表示
3.关闭Socket连接的方法
public void close() throws IOException
功能:关闭该socket连接。当服务器需要关闭,不处理任何其他入站连接时,调用该方法。
若关闭socket时发生I/O错误则抛出IOException异常。
【例 4】
//与8.2对应的服务器端程序
import java.net.*;
import java.io.*;
public class SocketAppServer{
public static void main(String args[]){
ServerSocket ss = null;
int port = 134;
String hello = "From Server:Hello World!";
try{
ss = new ServerSocket(port);
//建立服务器Socket连接,其侦听端口为port,port与客户端相同
}
catch(IOException e){
System.out.println(e);//若端口已被占用,则抛出异常
System.exit(1);
}
while(true){
try{
System.out.println("正在等待连接端口port=" + port + "...");
Socket cs = ss.accept();
InputStream in = cs.getInputStream();
DataInputStream din = new DataInputStream(in);
//获取输入流,并创建相应的数据输入流
String name = din.readUTF();
OutputStream out = cs.getOutputStream();
DataOutputStream dout = new DataOutputStream(out);
//获取输出流,并创建相应的数据输出流
dout.writeUTF(hello+"Your name:" + name);//将字符串写入输出流
System.out.println("已成功连接端口port=" + port);
System.out.println("=========================================");
in.close();
out.close();
cs.close();
}
catch(IOException e){
System.out.println(e);//若端口已被占用,则抛出异常
}
}
}
}
运行结果:
(1)首先运行例4的服务器端程序
图 7
(2)运行例2的客户端程序
4 Socket编程应用举例
4.1 Socket编程的基本步骤
1.服务器端应用程序编写步骤
(1)创建一个等待连接的ServerSocket对象,如Sersocket;
(2)调用Sersocket对象的accept()方法侦听接收客户端的连接请求。当侦听到一个客户的连接请求时,连接成功,并返回一个用于通信的Socket对象;
(3)创建与Socket对象绑定的输入输出流,并建立相应的数据输入输出流;
(4)通过数据输入输出流与客户端进行数据读写,完成双向通信;
(5)当客户端断开连接时,关闭各个流对象,结束通信。(2)~(5)可循环执行。
2.客户端应用程序编写步骤
(1)创建指定服务器上指定端口号的Socket对象;
(2)创建与Socket对象绑定的输入输出流,并建立相应的数据输入输出流;
(3)通过数据输入输出流与服务器端进行数据读写,完成双向通信;
(4)通过调用close()方法关闭与服务器端的连接,并关闭各个流对象,结束通信。
4.2单客户/服务器Socket编程应用举例
【例 5】
编写一个基于TCP协议的Socket网络点对点聊天程序,可实现客户端和服务器端信息的互发。
1.服务器端程序talkserver.java
//点对点聊天程序,服务器端
import java.net.*;
import java.io.*;
public class talkserver{
public static void main(String args[]){
ServerSocket server = null;
Socket socket;
String s;//中间字符串
int port = 2000;
String hello = "From Server:Hello World!";
try{
server = new ServerSocket(port);
//建立服务器Socket连接,其侦听端口为port,port与客户端相同
System.out.println("正在等待连接端口port=" + port + "...");
socket = server.accept();
System.out.println("已成功连接端口port=" + port);
System.out.println("=========================================");
InputStream in = socket.getInputStream();
DataInputStream din = new DataInputStream(in);
//获取输入流,并创建相应的数据输入流
OutputStream out = socket.getOutputStream();
DataOutputStream dout = new DataOutputStream(out);
//获取输出流,并创建相应的数据输出流
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请等待客户发送信息...");
while(true){
System.out.println("");
s = din.readUTF(); //读入从client传来的字符串
System.out.println("从客户端接收的信息为:" + s);//显示字符串
if(s.trim().equals("BYE")) {System.out.print("通话结束" );break;}
System.out.println("");
System.out.print("请输入要发送的信息:" );//显示字符串
s = sin.readLine(); //读入从client传来的字符串
dout.writeUTF(s);
if(s.trim().equals("BYE")) {System.out.print("通话结束" );break;}
}
din.close();
dout.close();
in.close();
out.close();
socket.close();
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}
程序分析:
程序首先在端口2000上创建一个等待连接请求的ServerSocket对象sever:
server=new ServerSocket(2000);
接着调用Server对象的accept()方法等待某客户程序发出的连接请求。该方法一直阻塞直到有客户连接到该端口。一旦有客户发送正确请求,则连接成功,accept()方法返回一个Socket对象,于是得到一个新的用于通信的Socket对象,通信链路建立成功.然后利用Socket类提供的getInputStream()和getOutputStream()方法创建与Socket对象绑定的输入/输出流.此后即可与客户进行通信,直到客户端断开连接即关闭各个流结束通信.
2.客户端程序talkclient.java
import java.net.*;
import java.io.*;
public class talkclient{
public static void main(String args[]){
Socket socket;
String s;
int port = 2000;
try{
//向本地服务器申请连接
//注意端口号要和服务器的一致
socket = new Socket("localhost",port);//localhost=127.0.0.1
System.out.println("连接成功");
System.out.println("=========================================");
System.out.println("");
//获得对应Socket的输入/输出流
InputStream in = socket.getInputStream();
DataInputStream din = new DataInputStream(in);
//获取输入流,并创建相应的数据输入流
OutputStream out = socket.getOutputStream();
DataOutputStream dout = new DataOutputStream(out);
//获取输出流,并创建相应的数据输出流
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
while(true){
System.out.print("请输入要发送的信息:" );//显示字符串
s = sin.readLine(); //读入从client传来的字符串
dout.writeUTF(s);
if(s.trim().equals("BYE")) {System.out.print("通话结束" );break;}
System.out.println("");
s = din.readUTF(); //读入从client传来的字符串
System.out.println("从服务器接收的信息为:" + s);//显示字符串
if(s.trim().equals("BYE")) {System.out.print("通话结束" );break;}
System.out.println("");
}
din.close();
dout.close();
in.close();
out.close();
socket.close();
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}
运行结果:
(1) 运行服务器端程序talkserver.java
(2)运行客户端程序talkclient.java
(3)客户端向服务端发送信息
【例 6】
多客户/服务器程序。
1.服务器端程序
import java.net.*;
import java.io.*;
public class ServerSocketThread extends Thread{
private Socket socket;
private DataInputStream din;
private DataOutputStream dout;
public ServerSocketThread(Socket s) throws IOException{
socket = s;
din = new DataInputStream(socket.getInputStream());
dout = new DataOutputStream(socket.getOutputStream());
start();
}
public void run(){
String str;
try{
System.out.println("连接成功");
System.out.println("=========================================");
System.out.println("请等待客户发送信息...");
System.out.println("");
while(true){
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("");
str = din.readUTF(); //读入从client传来的字符串
System.out.println("从客户端接收的信息为:" + str);//显示字符串
if(str.trim().equals("BYE")) {System.out.print("通话结束" );break;}
System.out.println("");
System.out.print("请输入要发送的信息:" );//显示字符串
str = sin.readLine(); //读入从键盘传来的字符串
dout.writeUTF(str);
dout.flush(); // http://blog.sina.com.cn/s/blog_82f2fc280100z3rr.html
if(str.trim().equals("BYE")) {System.out.print("通话结束" );break;}
}
din.close();
dout.close();
socket.close();
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}
(2)MultitalkServer.java:是一个应用程序,通过一个无限循环,等待并侦听多个客户的连接请求,每个客户的连接都由服务器端的一个单独线程来处理,实现了多用户/服务器之间的通信。
import java.net.*;
import java.io.*;
public class MultitalkServer{
public static void main(String args[]){
int port = 2000;
try{
System.out.println("正在等待连接...");
ServerSocket server = new ServerSocket(port);
Socket socket = null;
while(true){
socket = server.accept();
new ServerSocketThread(socket);//侦听连接请求,等待连接
}
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}
2.客户端程序
import java.net.*;
import java.io.*;
public class ClientSocketThread extends Thread{
private Socket socket;
private DataInputStream din;
private DataOutputStream dout;
private int port;
public ClientSocketThread(InetAddress addr,int port,String clientname) throws IOException{
super(clientname);
socket = new Socket(addr,port);
din = new DataInputStream(socket.getInputStream());
dout = new DataOutputStream(socket.getOutputStream());
start();
}
public void run(){
String str;
try{
System.out.println(getName());
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("连接成功");
System.out.println("=========================================");
System.out.println("");
while(true){
System.out.println("");
System.out.print("请输入要发送的信息:" );//显示字符串
str = sin.readLine(); //读入从client传来的字符串
dout.writeUTF(str);
dout.flush();
if(str.trim().equals("BYE")) {System.out.print("通话结束" );break;}
System.out.println("");
str = din.readUTF(); //读入从client传来的字符串
System.out.println("从客户端接收的信息为:" + str);//显示字符串
if(str.trim().equals("BYE")) {System.out.print("通话结束" );break;}
}
din.close();
dout.close();
socket.close();
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}
(2)TalkClient1.java和TalkClient2.java:建立和服务器通信的客户1和客户2
//------------------------------TalkClient1.java--------------------------------------------
import java.net.*;
import java.io.*;
public class talkClient1{
public static void main(String args[]){
int port = 2000;
try{
new ClientSocketThread(InetAddress.getLocalHost(),port,"客户端1");
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}
//-------------------------TalkClient2.java--------------------------------------
import java.net.*;
import java.io.*;
public class talkClient2{
public static void main(String args[]){
int port = 2000;
try{
new ClientSocketThread(InetAddress.getLocalHost(),port,"客户端2");
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}
运行结果:
TCP/IP协议
Client/Server模式
套接字(Socket)概念
Socket类
ServerSocket类
基于TCP协议的Socket编程的基本步骤
单客户/服务器Socket编程应用
多客户/服务器Socket编程应用
源文件:
http://dl.dbank.com/c0obwrpfzz
TCP协议的基本流程图,以及三次握手机制
常见的使用tcp协议的有ftp,smtp,telnet,snmp等。
MSL,最长分节生命周期,maximum segment lifetime,每个TCP实现都必须有个MSL值,它代表IP数据包能
在互联网上生存的最长时间。
TTL,Time to life是IP协议包中的一个值,它告诉网络,数据包(例如ICMP包)在网络中的时间是否太长而应
被丢弃。有很多原因使包在一定时间内不能被传递到目的地。
MTU ,最大传输单元,maximum transport unit,由链路层规定。一个IP数据包传出时,如果大于MTU,则会执行分片,各分片在到达目的地前是不会进行重组的。另外,IPV4主机和路由都可能对数据包执行分片,但是IPV6只有主机会在数据包产生时执行分片,IPV6路由器是不会对过路的数据包进行分片的。
MSS,最大分节大小,maximux segment size, 能接受的每个tcp分节中的最大数据量。
TCP,传输控制协议,transport control protocol
UDP,用户数据报协议,userdata protocol
ICMP ,网际控制消息协议,internet control message protocol
ARP, 地址解析协议, address resolv protocol
基于TCP/IP协议的Socket编程
1 TCP/IP协议
2 套接字(Socket)
2.1 Client/Server模式
2.2 套接字(Socket)概念
3 Socket类和ServerSocket类
3.1 Socket类
3.2 ServerSocket类
4 Socket编程应用举例
4.1 Socket编程的基本步骤
4.2单客户/服务器Socket编程应用举例
4.3多客户/服务器Socket编程应用举例本章小结
1 TCP/IP协议
TCP/IP协议(传输控制协议/网际协议)是一种网络通信协议,是一种面向连接的可靠的传输协议。它规范了网络上的所有通信设备,尤其是一个主机与另一个主机之间的数据传输格式以及传送方式。TCP/IP是Internet的基础协议。
TCP/IP协议的基本传输单位是数据包(datagram)。TCP协议负责把数据分成若干个datagram,并给每个datagram加上包头;IP在每个包头上再加上接收端主机地址,以确定数据传输的目的地。如果传输过程中出现数据丢失或数据失真等异常情况,TCP协议会自动要求数据重发,并重新分包。TCP保证数据传输的质量,IP协议保证数据的传输。
TCP/IP协议数据的传输是基于TCP/IP模型的4层结构:应用层、传输层、网络层和网络接口层。
1、传输层协议
有两个主要的传输层协议:传输控制协议(TCP)和用户数据报协议(UDP)。
(1)传输控制协议(TCP)TCP传输数据建立在面向连接的基础上,实现了一种“虚电路”的概念。双方通信之前,先建立连接,然后双方即可在其上发送数据流,发送完毕后再关闭连接。这种数据交换方式的优点是效率高,缺点是建立连接和关闭连接需要额外的开销。
(1)TCP协议主要提供下列服务:
- 可靠的数据传输
- 面向连接的虚电路
- 缓冲的传输
- 重新排序
- 多路复用技术
- 高效的、全双工传输
- 流量控制
TCP协议提供的是一种可靠的数据流服务。得到的是一个顺序的无差错的数据流。
(2)用户数据报协议(UDP)。UDP是比TCP简单得多的协议,是对IP协议组的扩充。
由于大多数网络应用程序都在同一台机器上运行,计算机上必须能够确保目的地址机器上的软件程序能从源地址机器处获得数据包,以及源计算机能收到正确的回复。这是通过使用UDP 的“端口号”完成的。源端口号标识了请求域名服务的本地计算机的应用程序,同时需要将所有由目的主机生成的响应包都指定到源主机的这个端口上。
(3)UDP与TCP比较
与TCP不同,UDP提供的是面向无连接、“不可靠”的数据报服务。它不提供对IP 协议的可靠机制、流控制以及错误恢复功能等。
由于UDP比较简单,比TCP负载消耗少。主要用于不需要TCP排序和流量控制能力而是自己完成这些功能的应用程序。
2、应用层协议
在应用层中定义了许多高层协议:
(1)TELNET(远程终端访问)。TELNET连接是一个TCP连接。允许一台计算机上的用户登录到远程计算机上并进行交互。支持终端到终端的连接及进程到进程分布式计算的通信。
(2)FTP(文件传输协议)FTP使得主机间可以共享文件。FTP使用TCP生成一个虚拟连接用于控制信息,然后再生成一个单独的TCP连接用于数据传输。FTP主要提供文件共享、支持间接使用远程计算机、使用户不因各类主机文件存储器系统的差异而受影响、可靠且有效的传输数据等功能。
(3)SMTP(简单邮件传输协议)SMTP支持在因特网上传递电子邮件,用于可靠且有效的数据传输。它保证把各种类型的电子邮件通过这一协议从一台邮件服务器发送到另一台邮件服务器上。
(4)DNS(域名服务)DNS提供域名到IP地址的转换,允许对域名资源进行分散管理。
(5)HTTP(超文本传输协议)是Web浏览器和Web服务器之间通信的标准协议。它指明客户端如何与服务器建立连接,客户端如何从服务器请求数据,服务器如何响应请求,最后连接如何关闭等。HTTP连接是一个TCP连接。
TCP/IP模型中还有其他一些协议,如地址解析协议(ARP)、因特网控制消息协议(ICMP)等。
2 套接字(Socket)
套接字允许程序员把网络连接当成一个流(Stream),并向这个流读写字节。Socket对程序员掩盖了网络的低层细节,如纠错、包大小、包重传、网络地址等。本节将介绍Client/Server模式、套接字(Socket)概念。
2.1 Client/Server模式
计算机网络最重要的3个功能是数据通信、资源共享和分布处理。为了满足这些功能需求,计算机网络产生了两种重要的应用模式:客户端/服务器(Client/Server,C/S)模式和浏览器/服务器(Brower/Server,B/S)模式。采用何种应用模式在编程实现时非常重要。下面将主要介绍C/S模式。
在C/S模式下,客户向服务器发出服务请求,服务器接收到请求后,提供相应的服务。其工作方式可通过现实生活中一个例子来说明。在一个酒店中,顾客向服务员点菜,服务员把点菜单通知厨师,厨师按点菜单做好菜后让服务员端给顾客,这就是一种C/S工作方式。如果把酒店看作一个系统,服务员就是客户端,厨师就是服务器,这种系统分工和协同工作的方式就是C/S的工作模式。
由此可见,工作在C/S模式下的系统被分成两大部分:
(1)客户端部分:为每个用户所专有的,负责执行前台功能。
(2)服务器部分:由多个用户共享的信息与功能,招待后台服务。
二是网间进程通信完全是异步的,相互通信的进程既不存在父子关系,又不共享内存缓冲区,因此需要一种机制为希望通信的进程建立联系,为二者的数据交换提供同步,这就是基于C/S的TCP/IP。
(1)服务器侦听相应端口的请求;
(2)客户端发出一个请求;
(3)服务器接收到此请求;
(4)服务器处理这个请求,并把处理结果返回给客户端。
从上面所描述的过程可知:客户与服务器进程的作用是非对称的,因此编码不同。服务进程一般是先于客户请求而启动的。只要系统运行,该服务进程一直存在,直到正常或强迫终止。
2.2 套接字(Socket)概念
数据在Internet中是以有限大小的分组的形式传输的。一个分组是一个数据报,包括首部和负载。首部包含目的的地址和端口、源地址和端口以及用于保证可靠传输的各种其他管理信息。负载包含数据本身。但由于分组长度有限,通常必须将数据分解为多个分组,在目的地再重新组合。在传输过程中,有可能发生一个或多个分组丢失或被破坏的情况,此时就需要重传分组。或者分组乱序到达,则需要重新排序。这些工作将是非常繁重的。
幸运的是,套接字(Socket)的出现使我们不必关心这些事情,我们只需要把网络看成一个流,就象对文件操作一样对这个流进行操作就行了。
- 流套接字提供一个面向连接的、可靠的数据传输服务,保证数据无差错、无重复、按顺序发送。具有流量控制功能。数据被看作字节流,无长度限制。TCP即是一种基于流套接字的通信协议。
- 数据报套接字提供一个面向无连接的数据传输服务,不保证数据传输是可靠的、有序的、无重复的。UDP即是一种基于数据报套接字的通信协议。
本章主要介绍基于TCP/IP协议的C/S模式下的Socket编程。
在两个网络应用程序发送和接收信息时都需建立一个可靠的连接,流套接字依靠TCP来保证信息正确到达目的地。实际上,IP分组有可能在网络中丢失或者在传送过程中发生错误。当任何一种情况发生时,作为接收方的TCP将请求发送方TCP重发这个IP分组。因此,两个流套接字之间建立的连接是可靠的连接。
(1)连接远程主机
(2)发送数据
(3)接收数据
(4)关闭连接
(5)绑定端口
(6)侦听入站数据
(7)在所绑定端口上接收远程主要的连接
(2)打开连接到Socket的输入输出流。Socket对象包含两个流:一个是输入流,表示流入的数据流,其作用是接收数据;一个是输出流,表示流出的数据流,其作用是向外发送数据,其流操作与I/O流相同。
3 Socket类和ServerSocket类
在Java语言中,服务器端Socket使用ServerSocket类,客户端Socket使用Socket类,由此来区别服务器端和客户端。
3.1 Socket类
java.net .Socket类是Java用来实现客户端TCP操作的基础类,在Java编程中一切与TCP有关的操作包括连接的建立、传输、连接关闭及Socket选项的设置都由Socket类负责,这个类本身使用直接代码通过主机操作系统的本地TCP栈进行通信。Socket类的输入输出都是通过流来实现的。 Socket类常用方法如下:
(1)public Socket(String host,int port) throws UnknownHostException, IOException功能:在客户端以指定的服务器host和端口号port创建一个Socket对象,并向服务器发出连接请求。
参数:
若域名服务器无法解析该服务器名或域名服务器没有运行,此时host无法可知,则抛出UnknownHostException异常;若生成Socket时发生I/O错误则抛出IOException异常。
(2)public Socket(InetAddress address,int port) throws IOException功能:在客户端以指定的服务器地址address和端口号port创建一个Socket对象,并向服务器发出连接请求。
参数:
若生成Socket时发生I/O错误则抛出IOException异常。
(3)public Socket(String host,int port,boolean stream) throws IOException功能:在客户端以指定的服务器host和端口号port创建Socket对象,并向服务器发出连接请求。如果stream值为true,则创建流Socket对象,否则创建数据报Socket 对象。
参数:
若生成Socket时发生I/O错误则抛出IOException异常。
(4)public Socket(InetAddress address,int port,boolean stream) throws IOException功能:在客户端以指定的服务器IP地址address和端口号port创建Socket对象,并向服务器发出连接请求。如果stream值为true,则创建流Socket对象,否则创建数据报Socket 对象。
参数:
若生成Socket时发生I/O错误则抛出IOException异常。
(5)public Socket(InetAddress address,int port,InetAddress localAddr,int localPort) throws IOException功能:生成一个Socket并且连接到由address指定的服务器的端口port上。该Socket将捆绑到由localAddr指定的本地主机的localPort端口上。
参数:
若生成Socket时发生I/O错误则抛出IOException异常。
(6)public Socket(String host,int port, InetAddress localAddr,int localPort) throws IOException功能:生成一个Socket并且连接到由host指定的服务器的端口port上。该Socket将捆绑到由localAddr指定的本地主机的localPort端口上。
参数:
若生成Socket时发生I/O错误则抛出IOException异常。
(7)protected Socket(SocketImpl impl) throws SocketException功能:根据用户指定的SocketImpl生成一个无连接的socket。
参数:
建立无连接失败时抛出SocketException异常。
客户端流Socket的创建可通过使用Socket类的构造函数完成。构造函数不仅可以创建Socket对象,并且会尝试连接服务器端的Socket。可通过这个方法测试某个端口的连接是否允许。
2.与Socket数据读写有关的常用方法
(1)public InetAddress getInetAddress()
(2)public InetAddress getLocalAddress()
(3)public int getPort()
(4)public int getLocalPort()
(5)public InputStream getInputStream() throws IOException
(6)public OutputStream getOutputStream() throws IOException
(7)public String toString()
3.关闭Socket连接的方法
public synchronized void close() throws IOException
若在关闭Socket连接时发生I/O错误则抛出IOException异常。
【例 1】
//创建socket连接后获取socket连接的相关信息
import java.net.*;
import java.io.*;
public class SocketInfo {
public static void main(String args[]){
try{
Socket sktobj = new Socket("www.sina.com.cn",8080);
System.out.println("1:connect to "+sktobj.getInetAddress()+"on port "+sktobj.getPort());
System.out.println("2:from port "+sktobj.getLocalPort()+" of"+sktobj.getLocalAddress());
}
catch(UnknownHostException e){System.out.println(e);}
catch(IOException e){System.out.println(e);}
}
}
图 4
【例 2】
//客户端向服务器发送一个字符串,服务器接收并返回
import java.net.*;
import java.io.*;
public class SocketAppClient{
public static void main(String args[]){
int port=134;
try{
Socket sc = new Socket("127.0.0.1",port);//创建本地socket连接
OutputStream out = sc.getOutputStream();
DataOutputStream dout = new DataOutputStream(out);
//获取输出流,并创建相应的数据输出流
dout.writeUTF("Tom"); //将tom写入输出流
InputStream in = sc.getInputStream();
DataInputStream din = new DataInputStream(in);
//获取输入流,并创建相应的数据输入流
String str = din.readUTF();
System.out.println(str);
in.close();
out.close();
sc.close();
}
catch(UnknownHostException e){System.out.println(e);}
catch(IOException e){System.out.println(e);}
}
}
运行结果:单方通信,缺服务器
图 5
3.2 ServerSocket类
ServerSocket类用在服务器端,侦听和响应客户端的连接请求,并接收客户端发送的数据。ServerSocket类的主要任务是在服务器端耐心地等候客户端的连接请求,一旦客户端应用程序申请建立一个Socket连接,ServerSocket类就会通过accept()方法返回一个对应的服务器端Socket对象,以便进行直接通信。从两台计算机连接成功时起,服务器端与客户端就得到了一个真正的“Socket-Socket”连接。
(1)public ServerSocket(int port) throws IOException
若无法创建Socket并绑定于所请求的端口号则抛出IOException异常。
(2)public ServerSocket(int port,int backlog) throws IOException
若无法创建Socket并绑定于所请求的端口号则抛出IOException异常。
(3)public ServerSocket(int port,int backlog,InetAddress bindAddr) throws IOException
若无法创建Socket并绑定于所请求的端口号则抛出IOException异常。
【例 3】
//采用ServerSocket类查询端口号为130~140的端口号中哪个端口可创建连接。
import java.net.*;
import java.io.*;
public class PortScan2{
public static void main(String args[]){
String host="localhost";
for(int i=130;i<140;i++)
try{ServerSocket a=new ServerSocket(i);
//在端口号i创建连接
System.out.println("there is a server on port:"+i);
//若可以创建连接,则输出提示信息
}catch(IOException e){System.out.println(e);
}//若端口已被占用,则抛出异常
}
}
运行结果:断口号为135的端口已被占用,当试图在端口135建立连接时抛出异常,即端口已被绑定.
图 6
2.ServerSocket类的常用方法
(1)public InetAddress getInetAddress()功能:获取服务器端Socket的IP地址。
(2)public int getLocalPort()功能:获取服务器正侦听的端口号。
(3)public Socket accept() throws IOException功能:在服务器端指定端口侦听客户端发出的连接请求并与之连接。该方法一直阻塞直到连接成功。该方法返回一个新的Socket对象,通过该对象与客户端进行通信。若等待连接时发生I/O错误则抛出IOException异常。
(4)public String toString()功能:返回该socket的字符串表示
3.关闭Socket连接的方法
public void close() throws IOException
功能:关闭该socket连接。当服务器需要关闭,不处理任何其他入站连接时,调用该方法。
若关闭socket时发生I/O错误则抛出IOException异常。
【例 4】
//与8.2对应的服务器端程序
import java.net.*;
import java.io.*;
public class SocketAppServer{
public static void main(String args[]){
ServerSocket ss = null;
int port = 134;
String hello = "From Server:Hello World!";
try{
ss = new ServerSocket(port);
//建立服务器Socket连接,其侦听端口为port,port与客户端相同
}
catch(IOException e){
System.out.println(e);//若端口已被占用,则抛出异常
System.exit(1);
}
while(true){
try{
System.out.println("正在等待连接端口port=" + port + "...");
Socket cs = ss.accept();
InputStream in = cs.getInputStream();
DataInputStream din = new DataInputStream(in);
//获取输入流,并创建相应的数据输入流
String name = din.readUTF();
OutputStream out = cs.getOutputStream();
DataOutputStream dout = new DataOutputStream(out);
//获取输出流,并创建相应的数据输出流
dout.writeUTF(hello+"Your name:" + name);//将字符串写入输出流
System.out.println("已成功连接端口port=" + port);
System.out.println("=========================================");
in.close();
out.close();
cs.close();
}
catch(IOException e){
System.out.println(e);//若端口已被占用,则抛出异常
}
}
}
}
运行结果:
(1)首先运行例4的服务器端程序
图 7
(2)运行例2的客户端程序
4 Socket编程应用举例
4.1 Socket编程的基本步骤
1.服务器端应用程序编写步骤
(1)创建一个等待连接的ServerSocket对象,如Sersocket;
(2)调用Sersocket对象的accept()方法侦听接收客户端的连接请求。当侦听到一个客户的连接请求时,连接成功,并返回一个用于通信的Socket对象;
(3)创建与Socket对象绑定的输入输出流,并建立相应的数据输入输出流;
(4)通过数据输入输出流与客户端进行数据读写,完成双向通信;
(5)当客户端断开连接时,关闭各个流对象,结束通信。(2)~(5)可循环执行。
2.客户端应用程序编写步骤
(1)创建指定服务器上指定端口号的Socket对象;
(2)创建与Socket对象绑定的输入输出流,并建立相应的数据输入输出流;
(3)通过数据输入输出流与服务器端进行数据读写,完成双向通信;
(4)通过调用close()方法关闭与服务器端的连接,并关闭各个流对象,结束通信。
4.2单客户/服务器Socket编程应用举例
【例 5】
编写一个基于TCP协议的Socket网络点对点聊天程序,可实现客户端和服务器端信息的互发。
1.服务器端程序talkserver.java
//点对点聊天程序,服务器端
import java.net.*;
import java.io.*;
public class talkserver{
public static void main(String args[]){
ServerSocket server = null;
Socket socket;
String s;//中间字符串
int port = 2000;
String hello = "From Server:Hello World!";
try{
server = new ServerSocket(port);
//建立服务器Socket连接,其侦听端口为port,port与客户端相同
System.out.println("正在等待连接端口port=" + port + "...");
socket = server.accept();
System.out.println("已成功连接端口port=" + port);
System.out.println("=========================================");
InputStream in = socket.getInputStream();
DataInputStream din = new DataInputStream(in);
//获取输入流,并创建相应的数据输入流
OutputStream out = socket.getOutputStream();
DataOutputStream dout = new DataOutputStream(out);
//获取输出流,并创建相应的数据输出流
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请等待客户发送信息...");
while(true){
System.out.println("");
s = din.readUTF(); //读入从client传来的字符串
System.out.println("从客户端接收的信息为:" + s);//显示字符串
if(s.trim().equals("BYE")) {System.out.print("通话结束" );break;}
System.out.println("");
System.out.print("请输入要发送的信息:" );//显示字符串
s = sin.readLine(); //读入从client传来的字符串
dout.writeUTF(s);
if(s.trim().equals("BYE")) {System.out.print("通话结束" );break;}
}
din.close();
dout.close();
in.close();
out.close();
socket.close();
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}
程序分析:
程序首先在端口2000上创建一个等待连接请求的ServerSocket对象sever:
server=new ServerSocket(2000);
接着调用Server对象的accept()方法等待某客户程序发出的连接请求。该方法一直阻塞直到有客户连接到该端口。一旦有客户发送正确请求,则连接成功,accept()方法返回一个Socket对象,于是得到一个新的用于通信的Socket对象,通信链路建立成功.然后利用Socket类提供的getInputStream()和getOutputStream()方法创建与Socket对象绑定的输入/输出流.此后即可与客户进行通信,直到客户端断开连接即关闭各个流结束通信.
2.客户端程序talkclient.java
import java.net.*;
import java.io.*;
public class talkclient{
public static void main(String args[]){
Socket socket;
String s;
int port = 2000;
try{
//向本地服务器申请连接
//注意端口号要和服务器的一致
socket = new Socket("localhost",port);//localhost=127.0.0.1
System.out.println("连接成功");
System.out.println("=========================================");
System.out.println("");
//获得对应Socket的输入/输出流
InputStream in = socket.getInputStream();
DataInputStream din = new DataInputStream(in);
//获取输入流,并创建相应的数据输入流
OutputStream out = socket.getOutputStream();
DataOutputStream dout = new DataOutputStream(out);
//获取输出流,并创建相应的数据输出流
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
while(true){
System.out.print("请输入要发送的信息:" );//显示字符串
s = sin.readLine(); //读入从client传来的字符串
dout.writeUTF(s);
if(s.trim().equals("BYE")) {System.out.print("通话结束" );break;}
System.out.println("");
s = din.readUTF(); //读入从client传来的字符串
System.out.println("从服务器接收的信息为:" + s);//显示字符串
if(s.trim().equals("BYE")) {System.out.print("通话结束" );break;}
System.out.println("");
}
din.close();
dout.close();
in.close();
out.close();
socket.close();
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}
运行结果:
(1) 运行服务器端程序talkserver.java
(2)运行客户端程序talkclient.java
(3)客户端向服务端发送信息
【例 6】
多客户/服务器程序。
1.服务器端程序
import java.net.*;
import java.io.*;
public class ServerSocketThread extends Thread{
private Socket socket;
private DataInputStream din;
private DataOutputStream dout;
public ServerSocketThread(Socket s) throws IOException{
socket = s;
din = new DataInputStream(socket.getInputStream());
dout = new DataOutputStream(socket.getOutputStream());
start();
}
public void run(){
String str;
try{
System.out.println("连接成功");
System.out.println("=========================================");
System.out.println("请等待客户发送信息...");
System.out.println("");
while(true){
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("");
str = din.readUTF(); //读入从client传来的字符串
System.out.println("从客户端接收的信息为:" + str);//显示字符串
if(str.trim().equals("BYE")) {System.out.print("通话结束" );break;}
System.out.println("");
System.out.print("请输入要发送的信息:" );//显示字符串
str = sin.readLine(); //读入从键盘传来的字符串
dout.writeUTF(str);
dout.flush(); // http://blog.sina.com.cn/s/blog_82f2fc280100z3rr.html
if(str.trim().equals("BYE")) {System.out.print("通话结束" );break;}
}
din.close();
dout.close();
socket.close();
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}
(2)MultitalkServer.java:是一个应用程序,通过一个无限循环,等待并侦听多个客户的连接请求,每个客户的连接都由服务器端的一个单独线程来处理,实现了多用户/服务器之间的通信。
import java.net.*;
import java.io.*;
public class MultitalkServer{
public static void main(String args[]){
int port = 2000;
try{
System.out.println("正在等待连接...");
ServerSocket server = new ServerSocket(port);
Socket socket = null;
while(true){
socket = server.accept();
new ServerSocketThread(socket);//侦听连接请求,等待连接
}
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}
2.客户端程序
import java.net.*;
import java.io.*;
public class ClientSocketThread extends Thread{
private Socket socket;
private DataInputStream din;
private DataOutputStream dout;
private int port;
public ClientSocketThread(InetAddress addr,int port,String clientname) throws IOException{
super(clientname);
socket = new Socket(addr,port);
din = new DataInputStream(socket.getInputStream());
dout = new DataOutputStream(socket.getOutputStream());
start();
}
public void run(){
String str;
try{
System.out.println(getName());
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("连接成功");
System.out.println("=========================================");
System.out.println("");
while(true){
System.out.println("");
System.out.print("请输入要发送的信息:" );//显示字符串
str = sin.readLine(); //读入从client传来的字符串
dout.writeUTF(str);
dout.flush();
if(str.trim().equals("BYE")) {System.out.print("通话结束" );break;}
System.out.println("");
str = din.readUTF(); //读入从client传来的字符串
System.out.println("从客户端接收的信息为:" + str);//显示字符串
if(str.trim().equals("BYE")) {System.out.print("通话结束" );break;}
}
din.close();
dout.close();
socket.close();
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}
(2)TalkClient1.java和TalkClient2.java:建立和服务器通信的客户1和客户2
//------------------------------TalkClient1.java--------------------------------------------
import java.net.*;
import java.io.*;
public class talkClient1{
public static void main(String args[]){
int port = 2000;
try{
new ClientSocketThread(InetAddress.getLocalHost(),port,"客户端1");
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}//-------------------------TalkClient2.java--------------------------------------
import java.net.*;
import java.io.*;
public class talkClient2{
public static void main(String args[]){
int port = 2000;
try{
new ClientSocketThread(InetAddress.getLocalHost(),port,"客户端2");
}
catch(IOException e){
System.out.println("Error" + e);//若端口已被占用,则抛出异常
System.exit(1);
}
}
}
运行结果:
TCP/IP协议
Client/Server模式
套接字(Socket)概念
Socket类
ServerSocket类
基于TCP协议的Socket编程的基本步骤
单客户/服务器Socket编程应用
多客户/服务器Socket编程应用
源文件:
http://dl.dbank.com/c0obwrpfzz
TCP协议的基本流程图,以及三次握手机制
常见的使用tcp协议的有ftp,smtp,telnet,snmp等。
MSL,最长分节生命周期,maximum segment lifetime,每个TCP实现都必须有个MSL值,它代表IP数据包能
在互联网上生存的最长时间。
TTL,Time to life是IP协议包中的一个值,它告诉网络,数据包(例如ICMP包)在网络中的时间是否太长而应
被丢弃。有很多原因使包在一定时间内不能被传递到目的地。
MTU ,最大传输单元,maximum transport unit,由链路层规定。一个IP数据包传出时,如果大于MTU,则会执行分片,各分片在到达目的地前是不会进行重组的。另外,IPV4主机和路由都可能对数据包执行分片,但是IPV6只有主机会在数据包产生时执行分片,IPV6路由器是不会对过路的数据包进行分片的。
MSS,最大分节大小,maximux segment size, 能接受的每个tcp分节中的最大数据量。
TCP,传输控制协议,transport control protocol
UDP,用户数据报协议,userdata protocol
ICMP ,网际控制消息协议,internet control message protocol
ARP, 地址解析协议, address resolv protocol