在数据传输过程中按照传输的协议我们分为两种TCP和UDP,其中TCP是面向连接的传输控制协议,UDP是用户数据报协议。
TCP Socket:
Socket:通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,象一个多孔插座。
Socket通讯的过程:
Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server端和Client 端都可以通过Send,Write等方法与对方通信
在java中为实现TCP Socket提供了两个基础的类(Socket和ServerSocket),Socket类有两个作用:1、作为客户端向服务器发出请求 2、在服务器端生成一个与客户端对等的通信实体,实现一对一的通信。ServerSocket主要用于在TCP传输的服务器端建立一个监听端口,监听本地服务器是否接受到客户机端的连接请求(当接收到请求的时候,采用accept方法确认连接,并在本地返回一个Socket对象,利用该Socket对象与客户端的Socket实现建立通信)
首先实现一个最简单的TCP,一对一的Socket通信:
客户端代码:
package com.net.tcp.chat;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class Client {
public static void main(String []args) throws IOException{
Socket cs = null;
DataOutputStream out = null;
DataInputStream in = null;
try {
cs = new Socket("localhost",8000); //send request
in = new DataInputStream(cs.getInputStream());
out = new DataOutputStream(cs.getOutputStream());
} catch (IOException e) {
System.out.println("can not access to port 8000!");
}
DataInputStream stdIn = new DataInputStream(System.in);
System.out.println("Please input your name:");
String username = stdIn.readLine();
String fromServer,fromUser;
while((fromServer = in.readUTF())!=null){
System.out.println("Server:"+fromServer);
if(fromServer.equals("bye")) break;
System.out.print("Client:");
fromUser = stdIn.readLine();
out.writeUTF(username+"#"+fromUser);
out.flush();
}
in.close();
out.close();
stdIn.close();
cs.close();
}
}
服务器端代码:
package com.net.tcp.chat;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
@SuppressWarnings("resource")
public static void main(String args[]) throws IOException{
ServerSocket ss = null;
ss = new ServerSocket(8000);
System.out.println("server starts to monitor port:8000!");
Socket cs = null;
cs = ss.accept();
DataInputStream in = new DataInputStream(cs.getInputStream());
DataOutputStream out = new DataOutputStream(cs.getOutputStream());
String inputStr,outputStr;
out.writeUTF("----Welcom to Chat Server----");
out.flush();
DataInputStream stdIn = new DataInputStream(System.in);
while((inputStr = in.readUTF())!=null){
System.out.println("Customer:"+inputStr);
System.out.print("Server:");
outputStr = stdIn.readLine();
out.writeUTF(outputStr);
out.flush();
}
in.close();
out.close();
stdIn.close();
cs.close();
}
}
上面的Client/Server程序只能实现Server和一个客户的对话。在实际应用 中,往往是在服务器上运行一个永久的程序,它可以接收来自其他多个客户端的请求,提供相应的服务。为了实现在服务器方给多个客户提供服务的功能,需要对上面的程序进行改造,利用多线程实现多客户机制。服务器总是在指定的端口上监听是否有客户请求,一旦监听到客户请求,服务器就会启动一个专门的服务线程来响应该客户的请求,而服务器本身在启动完线程之后马上又进入监听状态,等待下一个客户的到来。那么如何才能做到这一点呢?下面我们对于服务器端代码进行改进:生成单独的线程与每一个客户端进行独立的通信:
package com.net.tcp.chat;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerThread implements Runnable{
ServerSocket ss = null;
ServerThread(int port){
try {
ss = new ServerSocket(port);
System.out.println("启动本地"+port+"端口");
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
while(true){ //一直不停的监听客户端的连接请求,并做处理
try {
clentThread ct = new clentThread(ss.accept()); //生成一个新的、单独的Socket
Thread tct = new Thread(ct);
tct.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class clentThread implements Runnable{ //建立单独的客户端进行,分别与每一个客户进行通信
Socket cs = null;
clentThread(Socket cs){
this.cs = cs;
}
public void run() {
try {
DataInputStream in = new DataInputStream(cs.getInputStream());
DataOutputStream out = new DataOutputStream(cs.getOutputStream());
String inputStr,outputStr;
out.writeUTF("----Welcom to Chat Server----");
out.flush();
DataInputStream stdIn = new DataInputStream(System.in);
while((inputStr = in.readUTF())!=null){
System.out.println("Customer:"+inputStr);
System.out.print("Server:");
outputStr = stdIn.readLine();
out.writeUTF(outputStr);
out.flush();
if(outputStr.equals("bye")) break;
}
in.close();
out.close();
stdIn.close();
cs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ServerThread ms = new ServerThread(8000);
Thread tms = new Thread(ms);
tms.start();
}
}
这样修改完以后的确实现了多客户端的Socket通信,但是我们发现服务器端发送的信息只能轮流发送给客户端(即第一次发送信息给A,下一次再发送信息就发送给B....),但是我们希望实现的效果是服务器发送的信息所有连接上的客户端都可以收到,那又该如何做呢?我们至少应该存储客户端的信息(使用Collection或者Map之类)
我们继续修改服务器端的代码:
1、为ServerThread添加一个成员变量Vector<Socket> userList = new Vector();
2、将所有accept的Socket添加到userList中
3、服务器仅作为信息的转发者,不做键盘的输入
For(Socket ct:userList){
Out = new dataOutputStream(ct.getOutputStream());
out.writeUTF(in.readUTF());
}
4、为了能使客户端和服务器端可以及时的转发过来的信息,需要为客户端的接受和发送数据分别设置2个线程
UDP
UDP丢包的情况比较严重(网速差的情况更是如此),但是其资源消耗小、处理速度快,因此如ICQ、QQ等都是使用的UDP协议。广播的形式分为三种:单播、广播(一对所有)、组播(一对一组)。在TCP中只能实现点到点的单播形式(我们在上面实现消息的群发的时候,就是依次遍历userList,但是这样的效率过于低下)。在UDP模式下,按照数据报文的形式进行数据的通信,不存在输入/输出流。注:在UDP模式下,创建待发送的数据报的二进制数组,打包成UDP数据包,然后通过send发送指定的数据包:
Buf = “Hello”.getBytes();
P = new DatagramPacket(buf,buf.length,address,1080);
Socket.send();
在java中为实现UDP提供了两个基础的类(DatagramSocket(相当于ServerSocket)和DatagramPacket(相当于Socket))
服务器端代码:
package com.net.udp.chat;
import java.io.*;
import java.net.*;
class UDPServer{
public static void main(String[] args)throws IOException{
DatagramSocket server = new DatagramSocket(5050); //开启UDP服务
byte[] recvBuf = new byte[100];
DatagramPacket recvPacket = new DatagramPacket(recvBuf , recvBuf.length);
server.receive(recvPacket);
String recvStr = new String(recvPacket.getData() , 0 , recvPacket.getLength());
System.out.println("Hello World!" + recvStr);
int port = recvPacket.getPort();
InetAddress addr = recvPacket.getAddress();
String sendStr = "Hello ! I'm Server";
byte[] sendBuf;
sendBuf = sendStr.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendBuf , sendBuf.length , addr , port );
server.send(sendPacket);
server.close();
}
}
客户端代码:
package com.net.udp.chat;
import java.io.*;
import java.net.*;
public class UDPClient{
public static void main(String[] args)throws IOException{
DatagramSocket client = new DatagramSocket();
String sendStr = "Hello! I'm Client";
byte[] sendBuf;
sendBuf = sendStr.getBytes();
InetAddress addr = InetAddress.getByName("127.0.0.1");
int port = 5050;
DatagramPacket sendPacket
= new DatagramPacket(sendBuf ,sendBuf.length , addr , port);
client.send(sendPacket);
byte[] recvBuf = new byte[100];
DatagramPacket recvPacket
= new DatagramPacket(recvBuf , recvBuf.length);
client.receive(recvPacket);
String recvStr = new String(recvPacket.getData() , 0 ,recvPacket.getLength());
System.out.println("收到:" + recvStr);
client.close();
}
}