您的位置:首页 > 理论基础 > 计算机网络

黑马程序员------java基础----网络编程

2015-03-21 16:31 447 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

网络编程=网络+IO
网络模型:
OSI七层模型:物理层 数据链路层 网络层 传输层 会话层 表示层 应用层
TCP/IP参考模型:物理层 网络层 传输层 应用层
网络通讯的要素:
1.IP地址:网络中设备的标识,不容易记忆,可以使用主机名
本地回环地址127.0.0.1
广播地址X.X.X.255
2.端口号:用于标识进程的逻辑地址,是不同进程的标识。
有效端口号:0-65535,其中0-1024是系统使用或者保留端口
3.传输协议:通讯的规则,常见协议:TCP UDP
常见网络架构:
C/S:client/server
特点:
1.程序员需要开发客户端和服务端
2.维护麻烦
3.将一部分运算移到客户端来完成,减轻服务器端的压力
B/S:browser/server
特点:
1.程序员只需要开发服务端。客户端使用系统已有的浏览器即可
2.维护简单,只需维护服务端
3.所有的运算都在服务端完成
目前流行B/S
端口:物理端口
逻辑端口--软件应用程序的数字标识
传输协议:
TCP传输控制协议
面向连接,可靠的协议
需要建立连接,所以效率低
通过三次握手建立连接
UDP数据报文包协议
无连接,不可靠的协议
不需要建立连接,所以效率高
每个数据报大小限制在64K内
UDP分为发送和接收
TCP分为客户端和服务端
Socket:为网络服务提供的一种机制通信的两端都需要由Socket,数据在两个Socket之间通过IO传输
网络通信其实就是Socket之间进行通信,网络传输只能传输字节数据
1. InetAddress表示互联网协议 (IP) 地址
示例代码:
public class NetDemo {
public static void main(String[] args) throws Exception {
//获取本地IP实例
InetAddress address=InetAddress.getLocalHost();
//输出本地IP地址以及主机名
System.out.println(address.getHostAddress()+":"+address.getHostName());

//获取指定主机的地址对象
InetAddress address2=InetAddress.getByName("192.168.1.18");
//打印指定主机的IP地址以及主机名
System.out.println(address2.getHostAddress()+":"+address2.getHostName());

InetAddress ip=InetAddress.getByName("www.baidu.com");
System.out.println(ip.getHostAddress()+":"+ip.getHostName());

}
}

2.UDP数据报文包协议
无连接,不可靠的协议
不需要建立连接,所以效率高
每个数据报大小限制在64K内
DatagramSocket:表示用来发送和接收数据报包的套接字
DatagramPacket:此类表示数据包
数据包分为发送数据包和接收数据包
凡是发送数据包,参数都是带着地址的
UDP是无连接不可靠的协议,所以在没有接收端的情况下,发送端可以发送数据,且不会抛错,只不过是数据发丢了
示例代码:
发送数据到接收端并显示
/*
* 通过UDP协议发送一段文本数据
*
* 接收端
* 	   思路:
* 		1.先建立UDP的Socket.它具备发送或者接收功能
* 		2.将数据封装到数据包中。数据包对象:DatagramPacket
* 		3.使用Socket对象的send方法将数据包发送出去
* 		4.关闭资源
*/
public class UDPSender {
public static void main(String[] args) throws IOException {
System.out.println("UDP发送端运行");

// 1.建立Socket
DatagramSocket ds = new DatagramSocket();

// 2.将数据封装到数据包中
String text = "UDP,你好";
// 将数据转成字节数组
byte[] buff = text.getBytes();
// 将字节数组封装到数据包中
InetAddress address = InetAddress.getByName("ZFY");
DatagramPacket dp = new DatagramPacket(buff, buff.length, address,
10086);

// 3.使用Socket对象的send方法发出数据包
ds.send(dp);

// 4.关闭资源
ds.close();

System.out.println("发送结束");
}
}

/*
* UDP接收端,接收发送过来的数据,并显示在屏幕上
* 	思路:
* 		1.先有UDP Socket服务(接收端必须明确端口,否则无法接收数据)
* 		2.接收数据
* 		3.先定义数据包
* 		4.通过数据包对象获取数据包内容:发送端IP、端口、数据
* 		5.关闭资源
*/
public class UDPReceive {
public static void main(String[] args) throws Exception {
System.out.println("UDP接收端运行");

//1 创建Socket服务
DatagramSocket ds=new DatagramSocket(10086);

//2 3 接收数据,将数据存储到数据包
byte[] buff=new byte[1024];
DatagramPacket dp=new DatagramPacket(buff, buff.length);
ds.receive(dp);//阻塞

//4 获取数据包内容
String ipString=dp.getAddress().getHostAddress();
int port=dp.getPort();
String text=new String(dp.getData(),0,dp.getLength());

System.out.println("接收到来自"+ipString+":"+port+"的数据:"+text);

//5  关闭资源
ds.close();
System.out.println("UDP接收结束");
}
}
键盘录入信息发到接收端,由接收端将数据发回发送端进行显示
/*
* 发送端:从键盘录入数据发送到接收端,输入bye退出
*
*/
public class UDPSender {
public static void main(String[] args) throws Exception {
System.out.println("发送端启动");
System.out.println("等待输入信息...");
//创建Socket
DatagramSocket socket=new DatagramSocket(10001);
DatagramPacket dp=null;
//获取本机地址
InetAddress address=InetAddress.getByName("127.0.0.1");

//从键盘录入信息
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String line=null;
byte[] buff=null;
while((line=br.readLine())!=null){
buff=line.getBytes();
//创建数据包
dp=new DatagramPacket(buff, buff.length,address,10010);
//发送数据
socket.send(dp);
if(("bye".equals(line))){
break;
}
//接收从服务端发来的信息
socket.receive(dp);
//打印来自服务端的信息
System.out.println("来自接收端的信息:"+new String(dp.getData(),0,dp.getLength()));

}
//关闭资源
br.close();
socket.close();
System.out.println("发送端关闭");
}
}

/*
* 接收端:接收信息并显示到控制台,返回数据给发送端,接收到bye退出
*/
public class UDPReceive {
public static void main(String[] args) throws Exception {
System.out.println("接收端启动");

//创建Socket
DatagramSocket socket=new DatagramSocket(10010);
byte[] buff=new byte[1024];
//创建接收数据包
DatagramPacket recdp=new DatagramPacket(buff, buff.length);
//创建发送数据包
DatagramPacket senddp=null;

//接收来自客户端的数据包
socket.receive(recdp);
String line=null;

while((line=new String(recdp.getData(),0,recdp.getLength())) != null&&!("bye".equals(line))){
//打印来自客户端的信息
System.out.println("来自发送端的信息:"+line);
byte[] sendbuff=line.getBytes();
//发送数据包到客户端
senddp=new DatagramPacket(sendbuff, sendbuff.length,recdp.getAddress(),recdp.getPort());
socket.send(senddp);

//等待接收来自客户端的数据
socket.receive(recdp);
}
//关闭资源
socket.close();
System.out.println("接收端关闭");

}
}
通过UDP实现群聊
/*
* 案例:通过UDP实现群聊
* 		思路:
* 			1.这个程序中既有收,又有发,需要同时执行,需要用到多线程技术
* 			  一个线程负责发送,一个线程负责接收
*/
public class UDPChatTest {
public static void main(String[] args) throws Exception {
//定义发送端socket
DatagramSocket sendDs=new DatagramSocket();

//定义接收端socket
DatagramSocket recDs=new DatagramSocket(10086);

//发送&接收
Send send=new Send(sendDs);
Receive rec=new Receive(recDs);

Thread t1=new Thread(send);
Thread t2=new Thread(rec);

//启动线程
t1.start();
t2.start();
}

}

/*
* 发送端代码
*/
public class Send implements Runnable {

private DatagramSocket ds;
private DatagramPacket dp;

public Send(DatagramSocket ds) {
this.ds = ds;
}

@Override
public void run() {
try{
//从键盘录入信息
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String line=null;
while((line=br.readLine())!=null){
byte[] by=line.getBytes();
//实现局域网群聊,每个网段的最后一位如果写成255,没有此台主机,他会发往该网段的所有主机X.X.X.255
//dp=new DatagramPacket(by, by.length,InetAddress.getByName("192.168.1.255"),10086);

//发送信息到指定接收端
dp=new DatagramPacket(by, by.length,InetAddress.getByName("127.0.0.1"),10086);
ds.send(dp);

//输入over,则推出聊天
if("over".equals(line)){
System.out.println("关闭发送端");
break;
}
}
ds.close();
}
catch(Exception e){
e.printStackTrace();
}

}
}

/*
* 接收端代码
*/
public class Receive implements Runnable {
private DatagramSocket ds;
private DatagramPacket dp;

public Receive(DatagramSocket ds) {
this.ds = ds;
}

@Override
public void run() {
while (true) {
try {
//接收发送到当前端口的数据
byte[] buff = new byte[1024];
dp = new DatagramPacket(buff, buff.length);
ds.receive(dp);

//获取发送端Ip、端口号、内容等信息
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String content = new String(dp.getData(), 0, dp.getLength());
//打印信息到控制台
System.out.println(ip + ":" + port + ":" + content);

//如果接收到的信息是over,则关闭接收端
if("over".equals(content)){
System.out.println("关闭接收端");
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}

}
}
3、TCP传输控制协议
面向连接,可靠的协议
需要建立连接,所以效率低
通过三次握手建立连接
Socket:客户端socket

ServerSocket:服务器socket

TCP是面向连接的、可靠的传输协议,如果在没有服务端的情况下,客户端发送数据,会抛错,这种情况是不允许的

代码示例:

发送数据到服务端并显示

/*
* TCP实现客户端和服务端的收发过程
*
* 客户端的实现:发送信息到服务端,接收服务端返回的信息并显示
*/
public class TCPClient {
public static void main(String[] args) throws Exception {

System.out.println("客户端启动...");
//创建客户端socket
Socket client=new Socket("127.0.0.1",10086);

//通过socket输出流发送数据
OutputStream out=client.getOutputStream();
out.write("服务器,你好".getBytes());

//读取服务器返回的数据,通过socket输入流
InputStream in=client.getInputStream();
byte[] buf=new byte[1024];
int len=in.read(buf);
String content=new String(buf,0,len);
System.out.println(content);

//关闭资源
client.close();
}
}

/*
* 服务器端:接收来自客户端的数据,并发送信息到客户端
*/
public class TCPServer {
public static void main(String[] args) throws Exception {
System.out.println("服务端启动");

//创建服务端socket
ServerSocket server=new ServerSocket(10086);

//获取客户端
Socket client=server.accept();

//读取客户端数据
InputStream in=client.getInputStream();
byte[] buff=new byte[1024];
int len=in.read(buff);
String content=new String(buff,0,len);
String ip=client.getInetAddress().getHostAddress();
System.out.println(ip+":"+content);

//给客户端发送数据
OutputStream out=client.getOutputStream();
out.write("你好,客户端".getBytes());

//关闭资源
client.close();
//如果是不断的获取客户端,则不需要关闭服务端
server.close();
}
}
客户端和服务器之间的频繁通信

需求:

客户端通过键盘录入发送数据到服务端

服务端将接收到的数据显示到屏幕上的同时

将这些数据转换成大写发往客户端

当客户端输入over时,大写转换结束

注意:

数据不刷新的话,因为缓冲区,数据没有被发到服务器端

数据刷新后,数据可以被发到服务器端

但是服务器端readLine中带有缓冲区,没有读到行终止符,就一直在阻塞状态

方式1:手动加行终止符

PrintWriter pw = newPrintWriter(out);

pw.print(line+"\r\n");

pw.flush();

方式2:启用打印流的自动刷新

PrintWriter pw = newPrintWriter(out,true);

pw.println(line);

代码示例:

/*
* 客户端:
* 		思路:
* 			1.创建客户端socket,明确地址和端口
* 			2.源:键盘录入,获取要转换的数据
* 			3. 目的:网络,Socket输出流
* 			4.源:socket读取流,读取服务器发回来的大写数据
* 			5.目的:控制台,显示大写数据
* 			6.频繁的读写操作
* 			7.关闭资源
*
*/
public class TCPClient {
public static void main(String[] args) throws Exception {

System.out.println("客户端运行");

// 获取目的端ip
InetAddress address = InetAddress.getByName("127.0.0.1");

// 创建客户端socket
Socket s = new Socket(address, 10086);

// 获取键盘录入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

// 获取socket读取流
OutputStream out = s.getOutputStream();
// BufferedWriter bout=new BufferedWriter(new OutputStreamWriter(out));
PrintWriter pw = new PrintWriter(out);

// 获取socket输出流
InputStream in = s.getInputStream();
BufferedReader bRead = new BufferedReader(new InputStreamReader(in));

// 频繁的读写操作
String line = null;
while ((line = br.readLine()) != null) {

pw.println(line);

if ("over".equals(line)) {
break;
}

String upper = bRead.readLine();
System.out.println(upper);
}

// 关闭资源
br.close();
s.close();
}
}

/*
* 服务端:
* 		思路:
* 			1.创建服务端socket,监听一个端口
* 			2.源:socket输入流,读取客户端发来的数据
* 			3.目的:socket输出流,将转成大写的数据发送给客户端
* 			4.频繁的读写资源
* 			5.关闭客户端
*/
public class TCPServer {
public static void main(String[] args) throws Exception {
System.out.println("服务端运行...");
// 创建服务器socket
ServerSocket ss = new ServerSocket(10086);
while (true) {
// 获取客户端对象
Socket s = ss.accept();

//打印客户端IP
System.out.println(s.getInetAddress().getHostAddress()+"....connected");
// socket输入流
BufferedReader bin = new BufferedReader(new InputStreamReader(
s.getInputStream()));

// socket输出流
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);

// 频繁的读写操作
String line = null;
while ((line = bin.readLine()) != null) {
System.out.println(line);
if("over".equals(line)){
break;
}
// 转换大写,发送给客户端
pw.println(line.toUpperCase());
pw.flush();
}
s.close();
}
}

}
TCP实现图片上传

/*
* 客户端:上传图片到服务端,获取服务端发来的上传结果并显示
*/
public class Client {
public static void main(String[] args) throws Exception {
System.out.println("客户端启动...");
// 创建客户端socket
Socket s = new Socket(InetAddress.getByName("127.0.0.1"), 10010);
// 获取socket输出流
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());

// 读取要上传的文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
"D:\\1.jpg"));
byte[] b = new byte[2048];
int len;
// 上传文件
while ((len = bis.read(b)) != -1) {
bos.write(b, 0, len);
}
// 向服务端发送了一个结束标记,让服务端结束读取的动作
s.shutdownOutput();
// 获取从服务器端发来的上传成功的信息
InputStream in = s.getInputStream();
byte[] b2 = new byte[1024];
int len2 = in.read(b2);
System.out.println(new String(b2, 0, len2));

// 关闭资源
bis.close();
s.close();

System.out.println("客户端关闭");

}
}

/*
* 服务端:接收客户端发送来的上传文件,存到指定目录中
* 		    为防止出现同名覆盖,文件名=客户端ip+编号
* 		    操作成功,发送“上传成功”到客户端
*/
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("服务端启动..");
// 创建服务器socket
ServerSocket ss = new ServerSocket(10010);
while (true) {
// 获取客户端socket
Socket s = ss.accept();
//打印客户端IP
String ip=s.getInetAddress().getHostAddress();
System.out.println(ip+"...conneced");
// 获取socket输入流
BufferedInputStream bis = new BufferedInputStream(
s.getInputStream());

//新建File对象,指向目的文件夹
File dir=new File("D:\\uploadTest");
//目的文件夹不存在,则创建
if(!dir.exists()){
dir.mkdir();
}
//解决上传文件的重名覆盖问题
int count=1;
File desc=new File(dir,ip+"("+count+").jpg");
while(desc.exists()){
count++;
desc=new File(dir,ip+"("+count+").jpg");
}

// 创建输出流,指向目的地址
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(desc));

// 复制文件
byte[] b = new byte[2048];
int len;
while ((len = bis.read(b)) != -1) {
bos.write(b, 0, len);
}

// 向客户端发送上传成功的信息
OutputStream out = s.getOutputStream();
out.write("上传成功!".getBytes());

// 关闭资源
bos.close();
s.close();
}
}
}
多客户端并发上传

实现多客户端的并发上传,客户端代码不需变动,服务端必须启动多个线程来实现

代码示例

/*
* 客户端
*/
public class Client {
public static void main(String[] args) throws Exception {
System.out.println("客户端启动...");
// 创建客户端socket
Socket s = new Socket(InetAddress.getByName("127.0.0.1"), 10010);
// 获取socket输出流
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());

// 读取要上传的文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
"D:\\1.jpg"));
byte[] b = new byte[2048];
int len;
// 上传文件
while ((len = bis.read(b)) != -1) {
bos.write(b, 0, len);
}
// 向服务端发送了一个结束标记,让服务端结束读取的动作
s.shutdownOutput();
// 获取从服务器端发来的上传成功的信息
InputStream in = s.getInputStream();
byte[] b2 = new byte[1024];
int len2 = in.read(b2);
System.out.println(new String(b2, 0, len2));

// 关闭资源
bis.close();
s.close();

System.out.println("客户端关闭");

}
}

/*
* 要想实现多个客户端并发上传,服务器必须启动多个线程来完成
*/
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("服务端启动..");
// 创建服务器socket
ServerSocket ss = new ServerSocket(10010);
while (true) {
// 获取客户端socket
Socket s = ss.accept();

//将客户端socket封装进Upload,启动线程
new Thread(new Upload(s)).start();
}
}
}

/*
* 实现上传的方法
*/
public class Upload implements Runnable {

private Socket s;

public Upload(Socket s) {
this.s = s;
}

@Override
public void run() {
try {
// 打印客户端IP
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip + "...conneced");
// 获取socket输入流
BufferedInputStream bis = new BufferedInputStream(
s.getInputStream());

// 新建File对象,指向目的文件夹
File dir = new File("D:\\uploadTest");
// 目的文件夹不存在,则创建
if (!dir.exists()) {
dir.mkdir();
}
// 解决上传文件的重名覆盖问题
int count = 1;
File desc = new File(dir, ip + "(" + count + ").jpg");
while (desc.exists()) {
count++;
desc = new File(dir, ip + "(" + count + ").jpg");
}

// 创建输出流,指向目的地址
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(desc));

// 复制文件
byte[] b = new byte[2048];
int len;
while ((len = bis.read(b)) != -1) {
bos.write(b, 0, len);
}

// 向客户端发送上传成功的信息
OutputStream out = s.getOutputStream();
out.write("上传成功!".getBytes());

// 关闭资源
bos.close();
s.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端并发登录

客户端通过键盘录入用户名,服务端对这个用户名进行校验。

如果该用户存在,在服务端显示xxx,已登陆;并在客户端显示xxx,欢迎光临。

如果用户不存在,在服务端显示xxx,尝试登陆;并在客户端显示xxx,该用户不存在。

最多就登录三次。
代码示例:
import java.io.*;
import java.net.*;
//客户端
class  LoginClient
{
public static void main(String[] args) throws Exception
{
//创建服务
Socket s=new Socket("localhost",10000);
//键盘录入
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

//用Socket服务输出流写入数据
PrintWriter out =new PrintWriter(s.getOutputStream(),true );

//接收服务器返回的信息
BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));

String line=null;

for(int x=0;x<3;x++)
{
line=br.readLine();//读取键盘录入
if (line==null)
{
break;//如果键盘没有输入,则直接结束
}

out.println(line);//将数据写入流中

String info=in.readLine();//读取返回信息

System.out.println(info);

if (info.contains("欢迎"))//---------------
{
break;//如果登录成功,就跳出循环
}
}

br.close();//关流
s.close();
}
}

//服务端
class LoginServer
{
public static void main(String [] args)throws Exception
{
//创建服务,监听端口
ServerSocket ss=new ServerSocket(10000);

while (true)
{
//获取客户端对象
Socket s=ss.accept();

//客户端执行线程
new Thread(new LoginThread(s)).start();
}

//ss.close();
}
}

//利用多线程实现并发登录
class LoginThread implements Runnable
{
private Socket s;
LoginThread(Socket s)
{
this.s=s;
}
public void run()
{
//获取客户端ip
String ip=s.getInetAddress().getHostAddress();
System.out.println(ip+"  connected.....");
try
{
for (int x=0;x<3 ;x++ )
{
//通过客户端的读取流读取数据
BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));

//读取数据库中的数据,这里用文件来表示数据库
BufferedReader br=new BufferedReader(new FileReader("users.txt"));

String line=in.readLine();//读取客户端数据
if (line==null)//--------------
{
break;//如果客户端没有发送数据,则跳出循环
}
String data=null;
boolean flag=false;//设置标记
//读取数据库中的用户数据
while ((data=br.readLine())!=null)
{
if (line.equals(data))
{
flag=true;//如果用户存在,则将标记设为true
break;
}
}

//将数据写入到指定文件中
PrintWriter out=new PrintWriter(s.getOutputStream(),true);

if (flag)
{
System.out.println(line+",已登陆!");

out.println(line+",欢迎光临!");

break;//-----------
}
else
{
System.out.println(line+",尝试登陆!");
out.println(line+",用户名不存在!");
}
}
s.close();//关流
}
catch (Exception e)
{
throw new RuntimeException("用户登陆失败");
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: