两类传输协议:TCP;UDP
TCP是Tranfer Control Protocol的 简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个socket之间必须建 立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送 或接收操作。
UDP是User Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
比较:
UDP:
- 1,每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接。
- 2,UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内。
- 3,UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方
TCP:
- 1,面向连接的协议,在socket之间进行数据传输之前必然要建立连接,所以在TCP中需要连接时间。
- 2,TCP传输数据大小限制,一旦连接建立起来,双方的socket就可以按统一的格式传输大的
数据。 - 3,TCP是一个可靠的协议,它确保接收方完全正确地获取发送方所发送的全部数据。
应用:
1,TCP在网络通信上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输。但是可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽,因此TCP传输的效率不如UDP高。
2,UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。
网络套接字
网络套接字是IP地址与端口的组合。所谓套接字,实际上是一个通信端点,每个套接字都有一个套接字序号,包括主机的IP地址与一个16位的主机端口号,即形如(主机IP地址:端口号)
Java中的套接字
-
ServerSocket类
此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。 -
Socket类
此类实现客户端套接字。
ServerSocket
构造函数:
- ServerSocket()
创建非绑定服务器套接字。 - ServerSocket(int port)
创建绑定到特定端口的服务器套接字。
主要API:
- int getLocalPort()
返回此套接字在其上侦听的端口。 - Socket accept()
侦听并接受到此套接字的连接。(这是个阻塞方法,此方法在连接传入之前一直阻塞)
Socket
构造函数:
- Socket()
通过系统默认类型的 SocketImpl 创建未连接套接字 - Socket(InetAddress address, int port)
创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 - Socket(String host, int port)
创建一个流套接字并将其连接到指定主机上的指定端口号。
主要API:
- InputStream getInputStream()
返回此套接字的输入流。 - OutputStream getOutputStream()
返回此套接字的输出流。 - void shutdownInput()
此套接字的输入流置于“流的末尾”。 - void shutdownOutput()
禁用此套接字的输出流。
Socket通讯的过程
Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,
Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server端和Client 端都可以通过Send,Write等方法与对方通信。
对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:
(1) 创建Socket;
(2) 打开连接到Socket的输入/出流;
(3) 按照一定的协议对Socket进行读/写操作;
(4) 关闭Socket.(在实际应用中,并未使用到显示的close,虽然很多文章都推荐如此,不过在我的程序中,可能因为程序本身比较简单,要求不高,所以并未造成什么影响。)
群聊系统
要求能够收发信息独立进行(收发分别各为线程)
public class Client {
private Socket s = null;
private String Id = null;
private BufferedReader br = null;
private PrintWriter pw = null;
public Client() {
try {
s = new Socket("localhost",8080);
pw = new PrintWriter(s.getOutputStream(),true);
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void send() {
System.out.print("客户端已开启");
System.out.println(s);
Scanner sc = new Scanner(System.in);
String line = null;
System.out.println("请输入用户名:");
Id = sc.nextLine();
pw.println(Id);
while((line = sc.nextLine())!= null)
{
if(line.equalsIgnoreCase("exit"))
break;
pw.println(line);
}
sc.close();
}
public void receive() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
while( true)
System.out.println(br.readLine());
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
t.start();
}
public static void main(String[] args) {
Client c = new Client();
c.receive();
c.send();
}
}
服务器端,监听成功即创建线程任务
public class Server {
private ServerSocket ss = null;
static Map<String, Socket> map = new HashMap<>();
public Server(ServerSocket ss) {
this.ss = ss;
System.out.println("服务器已启动");
}
public void listen() {
Socket client = null;
try {
while(true) {
client = ss.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
String newconnectId = br.readLine();
System.out.println(newconnectId+"上线了-"+client.getInetAddress());
map.put(newconnectId, client);
ServerThread t = new ServerThread(client);
t.setUserId(newconnectId);
t.start();
System.out.println("当前连入服务器的用户有:"+map.size()+"个");
}
}catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server s = null;
try {
s = new Server(new ServerSocket(8080));
s.listen();
} catch (IOException e) {
e.printStackTrace();
}
}
}
群发线程任务,服务器每监听到一个客户端即开启一个该线程任务,它负责监听到客户端后立即发送给所有已监听到的客户端
public class ServerThread extends Thread{
private String UserId;
private Socket client = null;
public ServerThread(Socket s) {
this.client = s;
}
public void setUserId(String name) {
this.UserId = name;
}
@Override
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter pw = null;
while(true) {
String line = br.readLine();
if(line == null || line.equalsIgnoreCase("exit"))
break;
System.out.println("接收到有人发消息,开始群发");
Collection<Socket> c = Server.map.values();
for(Socket s : c) {
pw = new PrintWriter(s.getOutputStream(), true);
pw.println(this.UserId+": "+line);
}
}
if(pw != null)
pw.close();
if(br != null)
br.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
私聊系统
服务器端单收单发任务,因为对于每个监听到的客户端,当读到信息时,发给本用户和目标用户
public class ServerThread extends Thread{
String UserId;
Socket client = null;
String talkTo;
public ServerThread(Socket s) {
this.client = s;
}
public void setUserId(String name) {
this.UserId = name;
}
public void settalkTo(String target) {
this.talkTo = target;
}
@Override
public void run() {
InputStream is = null;
OutputStream os = null;
try {
is = client.getInputStream();
os = client.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
PrintWriter pw = null;
while(true) {
String line = br.readLine();
pw = new PrintWriter(os, true);
pw.println(this.UserId +": "+ line);//发给本用户
System.out.println("接收到有人发消息,开始发送给目标用户");
Socket s = Server.map.get(talkTo);//发给目标用户
pw = new PrintWriter(s.getOutputStream(),true);
pw.println(this.UserId +": "+ line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Server {
ServerSocket ss = null;
static Map<String, Socket> map = new HashMap<>();
public Server(ServerSocket ss) {
this.ss = ss;
System.out.println("服务器已启动");
}
public void work() {
Socket client;
try {
while(true) {
client = ss.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
String newconnectId = br.readLine();
String talkTo = br.readLine();
System.out.println(newconnectId+"上线了-"+client.getInetAddress());
map.put(newconnectId, client);
ServerThread t = new ServerThread(client);
t.setUserId(newconnectId);
t.settalkTo(talkTo);
t.start();
System.out.println("当前连入服务器的用户有:"+map.size()+"个");
}
}catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server s = null;
try {
s = new Server(new ServerSocket(8080));
s.work();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端,每个客户端先要向服务器发送用户ID和目标用户两条信息
public class Client {
Socket s = null;
String Id = null;
String target = null;
BufferedReader br = null;
PrintWriter pw = null;
public Client() {
try {
s = new Socket("localhost",8080);
pw = new PrintWriter(s.getOutputStream(),true);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void send() {
System.out.println("客户端已开启");
System.out.println(s);
Scanner sc = new Scanner(System.in);
String line = null;
System.out.println("请输入用户名:");
Id = sc.nextLine();
pw.println(Id);
System.out.println("请输入聊天对象:");
target = sc.nextLine();
pw.println(target);
while((line = sc.nextLine())!= null) {
if(line.equalsIgnoreCase("exit"))
break;
pw.println(line);
}
sc.close();
}
public void receive() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
while(true) {
System.out.println(br.readLine());
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
t.start();
}
public static void main(String[] args) {
Client c = new Client();
c.receive();
c.send();
}
}