1:网络编程TCP协议
TCP是一个可靠的协议,面向连接的协议。
实现TCP程序,需要编写服务器端和客户端,Java API为我们提供了java.net包,为实现网络应用程序提供类。
ServerSocket :此类实现服务器套接字。
Socket :此类实现客户端套接字(也可以就叫“套接字”)。
通过Socket实现网络编程 Socket是网络驱动层提供给应用程序编程的接口和一种机制。
2:TCP实现ECHO程序
Echo,意为应答,程序的功能是客户端向服务器发送一个字符串,服务器不做任何处理,直接把字符串返回给客户端,Echo程序是最为基本的客户/服务器程序。
3:服务器与多客户端通信
服务器端通过加入线程池来处理多个客户端请求,简单的线程数可以与CPU的核数匹配,过多的线程空闲会浪费cpu的资源
服务器端:
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 处理多个客户端
* 主线程用于监听客户端的连接,每次连接成功,会开启一个线程来处理
* 这样就会很方便的做到一个服务器处理多个客户端响应的数据了
*
*/
public class MuyilServerDemo {
public static void main(String[] args) {
//创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。返回类型是ExecutorService
ExecutorService es= Executors.newFixedThreadPool(3);
try {
ServerSocket server = new ServerSocket(6666);
System.out.println("服务器已经启动,等待客服端连接...");
while(true){
Socket s=server.accept();
System.out.println(s.getInetAddress().getHostAddress());
es.execute(new UserThread(s));//需要传参Runnable类型
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class UserThread implements Runnable{
private Socket s;
public UserThread(Socket s) {
super();
this.s = s;
}
@Override
public void run() {
try {
//服务器端先读后写,先写入输入流
BufferedReader br=new BufferedReader(
new InputStreamReader(s.getInputStream()));
PrintStream ps=new PrintStream(
new BufferedOutputStream(s.getOutputStream()));
String info=br.readLine();
System.out.println(info);
ps.println("echo"+info);
ps.flush();
//关闭流
ps.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端:
public class MutilClientDemo {
/**
* 客户端需要多次传向服务端
*/
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
try {
Socket socket=new Socket("localhost",6666);
PrintStream ps=new PrintStream(new BufferedOutputStream(socket.getOutputStream()));
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//客户端需要先写再读
System.out.println("请输入数据: ");
String info=input.nextLine();
ps.println(info);
ps.flush();
info=br.readLine();
System.out.println(info);
br.close();
ps.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4:多客户端之间的通信(难点)
之前的学习是可以实现客户端与服务器之间的通信,那么如何实现多个客户端之间的通信呢?
实现多个客户端之间的通信,使用服务器作为中间站,把消息进行集中处理
Message.java:
import java.io.Serializable;
/**
* 1:需要将发消息的信息进行封装
* String from 发送者
* Stirng to 接受者
* int type 发送消息的类型
* String info 信息内容
*/
@SuppressWarnings("serial")
public class Message implements Serializable{
private String from;
private String to;
private int type;
private String info;
public Message(String from, String to, int type, String info) {
super();
this.from = from;
this.to = to;
this.type = type;
this.info = info;
}
public Message() {
super();
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "Message [from=" + from + ", info=" + info + ", to=" + to
+ ", type=" + type + "]";
}
}
MessageType.java
/**
* 定义消息的类型
* 可以是登陆.发送
* 后续还可以是注册等等
*/
public class MessageType {
public static final int TYPE_SEND=1;
public static final int TYPE_LOGIN=2;
}
Client.java
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class Client {
/**
* 多客户端之间连接
*/
public static void main(String[] args) {
//1:与服务器建立好连接
//2:启动读取(接受Message)消息的线程
//3:使用主线程来实现发送信息
Scanner input=new Scanner(System.in);
//加入线程池
Executor ex=Executors.newSingleThreadExecutor();
ObjectOutputStream oos=null;
ObjectInputStream ois=null;
try {
Socket socket=new Socket("localhost",7777);
System.out.println("服务器连接成功");
oos=new ObjectOutputStream(socket.getOutputStream());
ois=new ObjectInputStream(socket.getInputStream());
//向服务器发送登陆信息
System.out.println("请输入名称:");
//登陆用户客户端名称
String name=input.nextLine();
Message msg=new Message(name, null, MessageType.TYPE_LOGIN, null);
oos.writeObject(msg);
msg=(Message)ois.readObject();
System.out.println(msg.getInfo()+msg.getFrom());//其中的Info就是服务器端我的欢迎你,.getForm就是写入的名称
//启动读取消息的线程
ReaderInfoThread rit=new ReaderInfoThread(ois);
ex.execute(rit);
boolean flag=true;
while(flag){
msg=new Message();
System.out.println("发给;");
msg.setTo(input.nextLine());
msg.setFrom(name);
msg.setType(MessageType.TYPE_SEND);
System.out.println("发送的内容:");
msg.setInfo(input.nextLine());
oos.writeObject(msg);
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally{
try {
ois.close();
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//读取消息的线程
class ReaderInfoThread implements Runnable{
private ObjectInputStream ois;
private boolean flag=true;
public ReaderInfoThread(ObjectInputStream ois) {
this.ois = ois;
}
public void setFlag(boolean flag){
this.flag=flag;
}
@Override
public void run() {
try {
while(flag){
Message msg=(Message)ois.readObject();
System.out.println("["+msg.getFrom()+"]"+"对你说:"+msg.getInfo());
}
//如果不关闭 ,就会一直读
if(ois!=null){
ois.close();
}
ois.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Server.java
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Server{
/**
* 多客户端之间进行通信
*/
public static void main(String[] args) {
//1:保存客户端处理的线程(使用Vector去保存)
//2:创建服务器端的Socket,socket在接受到后需要到线程中去处理
//3:在方法快中使用定义的类去将vector传参,在UserThread类中去操作数据处理
Vector<UserThread> v=new Vector<UserThread>();
//创建线程池
ExecutorService es=Executors.newFixedThreadPool(5);
try {
ServerSocket ss=new ServerSocket(7777);
System.out.println("服务器已经启动,等待客户端连接,,,");
while(true){
Socket socket=ss.accept();
UserThread nt=new UserThread(socket,v);
es.execute(nt);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class UserThread implements Runnable{
private String name;//客户端的名称,在登陆后
private Socket socket;
private ObjectInputStream ois;
private ObjectOutputStream oos;
private boolean flag=true;
//写入集合是为了线程之间可以进行认识,以确定客户端的信息课通过线程传输得到其他的客户端
private Vector<UserThread> v;
public UserThread(Socket socket, Vector<UserThread> v) {
this.v=v;
this.socket = socket;
//需要将创建的对象添加到集合中去
v.add(this);
}
@Override
public void run() {
System.out.println("客户端"+socket.getInetAddress().getHostAddress()+"已连接");
try {
ois=new ObjectInputStream(socket.getInputStream());
oos=new ObjectOutputStream(socket.getOutputStream());
while(flag){
//读取消息
Message msg=(Message)ois.readObject();
int type=msg.getType();//或许需要操作的类型
switch(type){
case MessageType.TYPE_LOGIN://登陆
name=msg.getFrom();
msg.setInfo("欢迎你: ");//写入内容之中
oos.writeObject(msg);//发送给客户端
break;
case MessageType.TYPE_SEND://发送信息
String to=msg.getTo();//指定需要发现谁
//需要将to和服务器之中的线程池做比较,如果相等,则发送信息
UserThread ut ;
for (int i = 0; i <v.size(); i++) {
ut=v.get(i);
if(to.equals(ut.name)&&ut!=this ){
ut.oos.writeObject(msg);
break;
}
}
break;
}
}
ois.close();
oos.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
5:网络编程UDP协议
UDP协议是一种无连接的协议
主要使用以下的两个类:
DatagramPacket:此类表示数据报包。
DatagramSocket:此类表示用来发送和接收数据报包的套接字
public class Send {
/**
* 实现无链接发生信息.类似QQ聊天,文件传输等
*
* 发送方实现步骤:
* 1:获取本地地址
* ` 2:创建DatagramPacket对象来存储数据
* 3:创建DatagramSocket对象发送DatagramPacket中存储的数据
*/
public static void main(String[] args) {
DatagramSocket ds=null;
String mess="咨询问题";
try {
//创建DatagramSocket对象,并连接套接字
ds=new DatagramSocket();
//获取本机地址
InetAddress ia=InetAddress.getByName("localhost");
//实现无连接传输数据,封装数据
DatagramPacket dp=new DatagramPacket(mess.getBytes(),mess.getBytes().length,ia,8888);
ds.send(dp);
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
ds.close();
}
}
}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class Receive {
/**
* 实现无链接发生信息.类似QQ聊天,文件传输等
*
* 接收方实现步骤:
* 1:创建DatagramSocket对象,实现套接字作为连接
* 2:创建DatagramPacket对象,实现需要传输的数据保存
* 3:使用DatagramSocket创建的对象来接受和传输DatagramPacket中存储的数据到知道ip和端口去
*/
public static void main(String[] args) {
//DatagramSocket表示发送和接受包的套接字
DatagramSocket ds=null;
//创建DatagramPacket来实现无连接包传输
byte[] by=new byte[1024];
DatagramPacket dp=new DatagramPacket(by, 1024);
try {
//创建DatagramSocket来接受数据
ds=new DatagramSocket(8888);
//接受由DatagramPacket创建的对象传输的包
ds.receive(dp);
//输出传输的数据
String mess=new String(dp.getData(), 0, dp.getLength());
System.out.println(ds.getInetAddress().getHostAddress()+"说:"+mess);
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
ds.close();
}
}
}