同步和异步同步通信
同步:发送进程与接收进程在每一个信息上都是同步的,这时,send和receive都是阻塞操作,每次发出一个receive后,发送进程都会一直阻塞,知道发送了相应的receive操作为止,每次发送了一个receive时,都会一直阻塞直到消息到达为止。
异步:send操作是非阻塞的,只要消息被复制到本地缓冲区,发送进程就可继续其他的操作,而receive是具有阻塞和非阻塞两种形式,非阻塞的接收进程可在发送receive操作后,可继续执行其他操作,但是后台要提供一个缓冲区,必须通过轮询或是中断独立接收缓冲区已满的通知
套接字
套接字提供进程间通信的一个端点(UDP和TCP都是用套接字抽象)
进程间的通信是在两个进程各自的一个套接字之间传送一个消息,对接收消息的进程它的套接字必须绑定一个计算机英特网地址和一个端口。
UDP数据包通信
数据报的数据包
包含消息的字节数组 | 消息长度 | 英特网地址 | 端口号
UDP数据包的javaAPI DatagramPacket和DatagramSocket这两个类提供数据包通信
DatagramPacket:实例可以在进程之间传送,此时其中一个进程发送,另一个进程接收。
DatagramSocket:该类支持套接字发送和接收UDP数据报,提供一个以端口号位参数的构造函数,用于使用特定的端口的进程。
UDP客户发送一个消息到服务器并获得一个应答
import java.net.*;
import java.io.*;
public class UDPClient{
public static void main(String args[]){
//args字符数组传递了内容和主机名
DatagramSocket aSocket = null;
try{
aSocket = new DatagramSocket();
byte[] m = args[0].getBytes();
InetAddress aHost = InetAddress.getByName(args[1]);
int serverPort = 6789;
DatagramPacket request = new DatagramPacket(m, args[0].length, aHost, serverPort);
aSocket.send(request);
byte[] buffer = new byte[1000];
DatagramPacket reply = new DatagramPacket(buffer, buffer.length);
aSocket.receive(reply);
System.out.println("reply" + new String(reply.getData()));
}catch(SocketException e){
System.out.println("Socket:" + e.getMessage());
}catch(IOException e){
System.out.println("IO:" + e.getMessage());
}finally{
if(aSocket != null){
aSocket.close();
}
}
}
}
UDP服务器不断的接收请求并将它发回给客户
import java.net.*;
import java.io.*;
public class UDPServer{
public static void main(String args[]){
DatagramSocket aSocket = null;
try{
aSocket = new DatagramSocket(6789);
byte[] buffer = new byte[1000];
while(true){
DatagramPacket request = new DatagramPacket(buffer, buffer.length,);
aSocket.receive(request);
DatagramPacket reply = new DatagramPcaket(request.getData(), reuqest.getLength(), request.getAdderss(), request.getPort());
aSocket.send(reply);
}catch(SocketException e){
System.out.println("Socket:" + e.getMessage());
}catch(IOException e){
System.out.println("IO:" + e.getMessage());
}finally{
if(aSocket != null){
aSocket.close();
}
}
}
}
}
TCP流通信
数据项的匹配问题:
两个进程需要对在流上传送数据的内容要达成一致。否则会造成数据类型的解释出错或是由于流中数据的不足而造成的阻塞
阻塞:
写入流的数据保存在目的地的套接字的队列中,当程序试图从输入通道读取数据时候将从队列中获取数据或是一直阻塞到有数据获取为止。
线程:
当服务器连接的时候,它通常需要创建一个新的线程用于新的客户通信。为每一个用户使用单独的线程的好处是服务器在等待输入的时候能阻塞而不会延误其他的用户。
TCP流的JAVA API
ServerSocket:服务器使用该类在服务器端口上创建一个套接字,以便监听客户的connect请求
accept()方法从队列中获得一个connect请求,如果队列为空,就会阻塞,直到有消息到达队列为止。执行accept的结果是获得一个Socket实例—该套接字用于访问与客户的通信流
Socket:该类可供连接的一对进程使用。客户使用构造函数创建函数创建套接字。
TCP客户与服务建立连接,发送请求并接收应答
import java.net.*
import java.io.*
public class TCPClient{
public static void main(String args[]){
Socket s = null;
try{
int serverPort = 7890;
s = new Socket(args[1], serverPort);
DataInputStream in = new DataInputStream(s.getInputStream());
DataOutputStream out = new DataOutStream(s.getOutputStream());
out.writeUTF(args[0]);
String data = in.read();
System.out.println("reveive:" + data);
}catch(UnkwonHostException e){
}catch(EOFEXCException e){\
}catch(IOException e){
}finally{
if(s != null){
try{
s.close();
}catch(IOException e){}
}
}
}
}
TCP服务器为每一个客户建立连接,然后回应客户的请求
import java.net.*;
import java.io.*;
public class TCPServer{
public static void main(String args[]){
try{
int serverPort = 7890;
ServerSocket listenSocket = new ServerSocket(servePort);
while(true){
Socket clientSocket = listenSocket.accetp();
Connection c = new Connection(cilentSocket);
}
}catch(IOException e){}
}
}
class Connection extends Thread{
DataInputStram in;
DataOutputStream out;
Socket client;
public Connection(Socket aClientSocket){
try{
client = aClientSocket;
in = new DataInputStream(clientSocket.getInputStream());
out = new DataOutputStream(cilentSocket.getOutputStream());
this.start();
}catch(IOException e){}
}
public voidrun(){
try{
String data = in.readUTF();
out.writeUTF(data);
}cach(EOFException e){
}catch(IOException e){
}finally{
try{
cilentSocket()
}
}
}
}