tcp/udp编程示例程序
tcp聊天
客户端类
主类
package TCP_ServerSendAll;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
/**
* 功能:tcp发送消息 客户端
* @author 155
*2016-11-30
*思路:客户端取得连接后 循环读服务器发送的消息 开启:子线程 读控制台 发送到服务端
*/
public class TestKeHu {
static Socket ss;
public static void main(String[] args) {
TestKeHu t=new TestKeHu();
try {
ss=new Socket("172.18.64.49",5656);
BufferedReader br=new BufferedReader(new InputStreamReader(ss.getInputStream()));
String s=br.readLine();
System.out.println(s);
OutputStream os = ss.getOutputStream();
PrintStream ps=new PrintStream(os);
Scanner sc=new Scanner(System.in);
String msg=sc.next();
ps.println(msg);
while(true){
s=br.readLine();
System.out.println(s);
if (s.equals("登录失败")) {
break;
}
Thread_Client_write th=new Thread_Client_write(ss);
th.start();
}
ss.shutdownInput();
ss.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
线程类
package TCP_ServerSendAll;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
/**
* 功能: 客户端发送消息线程
* @author 155
*2016-12-1
* 思路:通过构造函数出入套接字 得到输出流 提示输入信息 不停读取控制台内容 发送到服务器
*/
public class Thread_Client_write extends Thread{
private Socket s;
public Thread_Client_write(Socket s) {
super();
this.s = s;
}
@Override
public void run() {
try {
OutputStream os = s.getOutputStream();
PrintStream ps=new PrintStream(os);
Scanner sc=new Scanner(System.in);
// System.out.println("输入用户信息 (用户名,密码)如 张三:123");
String msg=sc.next();
ps.println(msg);
while(true){
System.out.println("【开始聊天】输入格式:“用户名-内容”为私聊 默认为群聊");
String msg1=sc.next();
ps.println(msg1);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务器类
主类
package TCP_ServerSendAll;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Properties;
import java.util.Scanner;
import java.util.Vector;
/**
* 功能:tcp协议聊天 服务器端
* @author 155
*2016-11-30
* 思路:服务器侦听到连接后 把套接字放入集合
* 广播:在输入字符后把该字符作为广播消息发送到所有连接的客户端
* 群聊:服务器接受线程收到信息后判断格式 调用群聊方法 (默认为群聊)
* 私聊: 服务器接受线程收到信息号判断格式(用户名-内容) 调用私聊方法
* ^通过判断以“-”拆分后数组长度是否为1来判断格式
*/
public class TestServer {
static ArrayList<Thread_Serve_Read> re=new ArrayList<Thread_Serve_Read>();
static Vector<Socket> v=new Vector<Socket>();
static ServerSocket ss;
public static void main(String[] args) {
TestServer t=new TestServer();
try {
ss=new ServerSocket(5656);
t.kaixiancheng();
while(true){
t.guangbo();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void kaixiancheng(){
new Threadserve().start();
}
//一个线程的成员内部类 用于侦听用户连接
class Threadserve extends Thread{
@Override
public void run() {
while(true){
try {
Socket acc = ss.accept();
InputStream is = acc.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is));
OutputStream os=acc.getOutputStream();
PrintStream ps=new PrintStream(os);
ps.println("输入用户信息 (用户名:密码)如 张三:123");
//向用户推送登录提示
String readLine = br.readLine();
String[] spl = readLine.split(":");
System.out.println("分割后"+spl[0]+" "+spl[spl.length-1]);
Thread_Serve_Read thr=new Thread_Serve_Read(acc,spl[0],spl[spl.length-1]);
thr.setName(spl[0]);
boolean denglu=yanzheng(spl[0],spl[spl.length-1]);
// System.out.println(denglu+"77777777777777");
if (denglu) {
ps.println("登录成功,现在开始聊天吧");
re.add(thr);
thr.start();
v.add(acc);
}else{
ps.println("登录失败");
acc.shutdownInput();
acc.shutdownOutput();
acc.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void guangbo(){
System.out.println("请输入广播内容:");
Scanner sc=new Scanner(System.in);
String s=sc.next();
Date d=new Date();
String yangshi="MM月dd日hh时mm分ss秒";
SimpleDateFormat sdf=new SimpleDateFormat(yangshi);
String date = sdf.format(d);
String zs="【系统公告】@内容【"+s+"】#时间["+date+"]";
for (Socket s0 : v) {
try {
OutputStream os = s0.getOutputStream();
PrintStream ps=new PrintStream(os);
ps.println(zs);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void qunliao(Socket s,String masg,String namefor){
Date d=new Date();
String yangshi="hh:mm:ss";
SimpleDateFormat sdf=new SimpleDateFormat(yangshi);
String date = sdf.format(d);
String zs="【"+namefor+"】@说:"+masg+"。";
for (Socket s0 : v) {
try {
if (s!=s0) {
//实现自己发送的群聊消息不会自己收到的功能
OutputStream os = s0.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println(zs);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void siliao(String masg){
String[] x = masg.split("-");
// System.out.println("线程长度"+re.size()+" neirong"+x[1]);
for (Thread_Serve_Read t : re) {
// System.out.println(t.getName()+"--------name---------"+x[0]);
Socket s = t.getS();
if (t.getName().equals(x[0])) {
// System.out.println("//////////////////");
try {
OutputStream o = s.getOutputStream();
PrintStream ps=new PrintStream(o);
ps.println("["+x[0]+"]"+x[1]+"-------私聊");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static boolean yanzheng(String name,String pass){
try {
File f=new File("d:/a.properties");
FileReader fr=new FileReader(f);
Properties p=new Properties();
p.load(fr);
// System.out.println("验证的密码"+p.getProperty(name)+"传入"+name+"----"+pass);
if (pass.equals(p.getProperty(name))) {
// System.out.println("^^^^^^^^^^^^^^^^^^^^");
return true;
}else{
return false;
}
} catch (Exception e) {
return false;
}
}
}
线程类
package TCP_ServerSendAll;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class Thread_Serve_Read extends Thread{
private Socket s;
public String ename;
public String emima;
public Socket getS() {
return s;
}
public void setS(Socket s) {
this.s = s;
}
public String geteName() {
return ename;
}
public void seteName(String ename) {
this.ename = ename;
}
public String geteMima() {
return emima;
}
public void setMima(String emima) {
this.emima = emima;
}
public Thread_Serve_Read(Socket s,String ename,String mima) {
super();
this.s = s;
this.ename=ename;
this.emima=emima;
}
@Override
public void run() {
try {
InputStream is = s.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is));
while (true) {
String r = br.readLine();
int length = r.split("-").length;
// System.out.println("私聊拆分长度是"+length);
if (length==1) {
TestServer.qunliao(s, r,Thread.currentThread().getName());
}else{
TestServer.siliao(r);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
udp聊天
先接受 后发送
package UDP_SendAndJieShou;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* 功能: udp 聊天 先接收端
* @author 155
*2016-11-30
* 思路:构造数据报的包 实例化接收套接字 调用接受方法 实例化发送套接字 构造发送的数据报的包 调用发送的方法
*/
public class TestJieShou {
public static void main(String[] args) {
try{
byte [] bj=new byte [1024];
DatagramPacket dpj=new DatagramPacket(bj, bj.length);
DatagramSocket dsj=new DatagramSocket(9999);
dsj.receive(dpj);
String s=new String(bj,0,dpj.getLength());
System.out.println("接收的数据是:"+s);
byte[] b = "xiao明jojokjkl".getBytes();
DatagramPacket dp=new DatagramPacket(b, 0, b.length,
dpj.getAddress(),dpj.getPort());
dsj.send(dp);
System.out.println("数据发送完毕");
dsj.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
先发送 后接受
package UDP_SendAndJieShou;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* 功能: udp 聊天 先发送端
* @author 155
*2016-11-30
* 思路:实例化发送套接字 构造发送的数据报的包 调用发送的方法 构造数据报的包 实例化接收套接字 调用接受方法
*/
public class TestSend {
public static void main(String[] args) {
try{
byte[] b = "xiao明,出去.-先发送".getBytes();
InetAddress by = InetAddress.getByName("172.18.64.49");
DatagramPacket dp=new DatagramPacket(b, 0, b.length, by, 9999);
DatagramSocket ds=new DatagramSocket();
ds.send(dp);
System.out.println("数据发送完毕");
byte [] bj=new byte [1024];
DatagramPacket dpj=new DatagramPacket(bj, bj.length);
ds.receive(dpj);
String s=new String(bj,0,dpj.getLength());
System.out.println("接收的数据是:"+s);
ds.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
网络编程
网络基础知识
计算机网络
计算机网络就是把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地相互传递信息,共享硬件、软件、数据信息等资源。
OIS网络七层模型
物理层
数据链路层
网络层
传输层
会话层
表示层
应用层
网络工作模式
对等网络 peer-to-peer
专用服务器架构 server-based
服务器/客户端 模式 server/brower
IP地址
P: 网络之间互连的协议(IP)是Internet Protocol的外语缩写, 网络之间互连的协议也就是为计算机网络相互连接进行通信而设计的协议。
在因特网中,它是能使连接到网上的所有计算机网络实现相互通信的一套规则,规定了计算机在因特网上进行通信时应当遵守的规则。任何厂家生产的计算机系统,只要遵守IP协议就可以与因特网互连互通。
A类IP地址 1.0.0.0-126.255.255.255
0.0.0.0 127.0.0.0 保留
B类IP地址 128.0.0.0-191.255.255.255
C类IP地址 192.0.0.0-223.255.255.255
D类IP地址 224.0.0.0-239.255.255.255
E类IP地址 240.0.0.0-255.255.255.254
端口号
端口号: 一个IP地址的端口可以有65536(即:2^16)个之多!端口是通过端口号来标记的,端口号只有整数,范围是从0 到65535(2^16-1)
基本端口知识
80 http 端口
53 DNS端口
23 telnet端口
3306 mysol
21 FTP
关于传输中编码问题的类
URLEncode
标记: 当URL地址里包含非西欧字符的字符串时,系统会将这些非西欧字符串转换成%和十六进制。
URLDecode
标记: 系统会将这些非西欧字符串转换成%和十六进制。转换为汉字等编码
解码
(URLDecode)
编码
(URLEncode)
java中关于网络主机等信息的类
InetAddress
Inet4Address
标记: Internet Portocol version4(IPv4)地址
Inet6Address
标记: Internet Protocol version(IPv6)地址
基本方法
getAllByName(String host) 在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址所组成的数组。
getLocalHost() 返回本地主机。
isReachable(NetworkInterface netif, int ttl, int timeout) 测试是否可以达到该地址。
getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。
getAddress() 返回此 InetAddress 对象的原始 IP 地址。
getByAddress(byte[] addr) 在给定原始 IP 地址的情况下,返回 InetAddress 对象。
关于网络传输的类
示例代码
try {
URL u=new URL(urlstr);
URLConnection o = u.openConnection();
InputStream is = o.getInputStream();
FileOutputStream fos=new FileOutputStream("d:/编程测试/"+newname);
int t=is.read();
while(t!=-1){
fos.write(t);
t=is.read();
}
is.close();
fos.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
URL
URL(String spec)
根据 String 表示形式创建 URL 对象。
标记: 构造函数
URL(String protocol, String host, int port, String file)
根据指定 protocol、host、port 号和 file 创建 URL 对象。
标记: 构造函数
URLConnection openConnection() 返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。
标记: 通过该方法可以得到一个 URLConnection 对象
openStream()
打开到此 URL 的连接并返回一个用于从该连接读入的 InputStream。
getPort()
获取此 URL 的端口号。
getProtocol()
获取此 URL 的协议名称。
getPort()
获取此 URL 的端口号。
URLConnection
connect()
打开到此 URL 引用的资源的通信链接(如果尚未建立这样的连接)。
getInputStream() 返回从此打开的连接读取的输入流。
标记: 必须先打开URL连接才可以调用此方法
套接字类 socket
标记: 文字 图片 等传输
Java提供的网络功能有三大类: URL, Socket, Datagram.
URL是三大功能中最高级的一种,通过URL Java程序可以直接送出或读入网络上的数据.
Socket是传统网络程序最常用的方式,可以想象为两个不同的程序通过网络的通信信道.
Datagram是更低级的网络传输方式,它把数据的目的纪录在数据包中,然后直接放在网络上.
tcp协议
标记: 是一个高级协议 数据安全
参见: TCP协议:可靠,传输数据包的大小没有限制,但是连接建立需要时间,差错控制的开销大;
UDP协议:不可靠,差错控制开销小,传输数据包大小有限制(64K以下),不需要建立连接。
serverSocket 用于 服务器/客户端 中的 服务器端
基本 思路
先实例化一个servesocket 然后开启侦听 (当有客户端连接后程序才向下运行)
开启侦听后会返回一个socket (套接字)
用返回的套接字socket可以得到输入 或者 输出 的字节流
从得到的流里就可以操作数据 输入流即接受数据 输出流即发送数据
public class Server {
public static void main(String args[]) {
ServerSocket server;
try {
server = new ServerSocket(1234);
Socket sk = server.accept();
BufferedReader br = new BufferedReader(
new InputStreamReader(sk.getInputStream()));
System.out.println(br.readLine());
br.close();
server.close();
} catch (IOException e) {
System.out.println(e);
}
}
}
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
accept() 侦听并接受到此套接字的连接。
标记: 必须调用此方法 返回一个服务器端的套接字
getLocalPort() 返回此套接字在其上侦听的端口。
IP是一个无连接、不可靠的协议。在向另一台主机传输数据之前,它不交换控制信息,数据包只是传送到目的主机,并且假设能够被正确地处理。
IP 是面向包的协议,即数据被分成若干小数据包,然后分别传输它们。
IP 网络上的主机只能直接向本地网上的其他主机(也就是具有相同IP 网址的主机)发送数据包。
socket 用于 服务器/客户端中 的客户端
基本 思路
先实例化一个socket (构造函数里传入网络地址IP 端口号port ) 即可连接到服务端
用套接字socket可以得到输入 或者 输出 的字节流
从得到的流里就可以操作数据 输入流即接受数据 输出流即发送数据
public class Client {
public static void main(String args[]) {
Socket client;
PrintStream ps;
try {
client = new Socket("localhost", 1234);
System.out.println("连接成功");
ps = new PrintStream(client.getOutputStream());
ps.println("Hello");
ps.close();
client.close();
} catch (IOException e) {
System.out.println(e);
}
}
}
Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
标记: 构造函数
getInputStream() 返回此套接字的输入流。
getOutputStream() 返回此套接字的输出流。
TCP协议:可靠,传输数据包的大小没有限制,但是连接建立需要时间,差错控制的开销大;
UDP协议:不可靠,差错控制开销小,传输数据包大小有限制(64K以下),不需要建立连接。
参见: tcp协议, udp协议
udp协议
标记: UDP协议的主要作用是完成网络数据流量和数据包之间的连接转换
参见: TCP协议:可靠,传输数据包的大小没有限制,但是连接建立需要时间,差错控制的开销大;
UDP协议:不可靠,差错控制开销小,传输数据包大小有限制(64K以下),不需要建立连接。
DatagramSocket ds = new DatagramSocket(5000) 用于构建接受的套接字 DatagramPacket dp = new DatagramPacket(bytes, bytes.length);构造一个接受的数据报的包
基本思路
先得到一个套接字
创建一个byte数组
调用接受方法 (此方法会阻塞线程)
把数组转换成字符串
打印该字符串
/**
* UDP接收端,运行是需要先运行
*
*/
public class UDPClientDemo {
public static void main(String[] args) throws IOException {
//创建UDP协议的Socket对象
DatagramSocket ds = new DatagramSocket(5000);//在此端口进行监听
System.out.println("接收端正在等待接收数据...");
byte[] bytes = new byte[1024];
//创建一个数据报对象
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
ds.receive(dp);//接收数据,该方法会造成阻塞
String info = new String(dp.getData(),0,dp.getLength());//从数据包中提取数据
System.out.println(info);
ds.close();
}}
信息的发送端,UDP协议将网络数据流量封装成数据包,然后将数据包发送出去。
UDP协议(User Datagram Protocol)即:用户数据包协议,是一种不可靠的网络协议,它在通信示例的两端各各建立一个Socket,但这两个Socket之间没有虚拟连接,这两个Scoket只是发送、接收数据包的对象。
DatagramSocket ds = new DatagramSocket(); 用于构建发送数据的套接字 DatagramPacket(bytes,0,bytes.length,InetAddress.getByName("127.0.0.1"),5000);
构造一个数据报文的包
基本思路
得到一个套接字 (构造函数)
构造一个数据报文的数据包
调用套接字的发送方法
public class UDPServerDemo {
public static void main(String[] args) {
try {
//创建一个UDP协议的 Socket对象
DatagramSocket ds = new DatagramSocket(3000);//从哪个端口发送出去
String info = "小新有一只狗,小时候叫小白,大了叫大白,老了叫老白,死了就是白白!";
//把数据转换成字节数组
byte[] bytes = info.getBytes();
//创建一个数据报包对象,参数(要发送的字节数组,数组起始位置,要发送数组的长度,InetAddress,接收端端口号)
DatagramPacket data = new DatagramPacket(bytes,0,bytes.length,InetAddress.getByName("127.0.0.1"),5000);
ds.send(data);//发送数据报
System.out.println("发送完毕");
ds.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
信息的接收端,UDP协议将数据包接收后转换成实际的数据内容