JAVA TCP网络编程学习笔记
一、JAVA网络编程概述
网络应用程序,就是在已实现网络互联的不同计算机上运行的应用程序,这些程序之间可以相互交换数据。JAVA是优秀的网络编程语言,Java网络编程的类库位于java.net包中。Java支持TCP/UDP及其上层的网络编程,对TCP/UDP以下层,如IP包的捕获,侦听,数据链路层的帧的捕获,需要借助第三方的java包,如UNIX/Linux下著名的libpcap包的Java版本jpcap包。
在网络编程中,服务器与客户程序只需关心发送什么样的数据给对方,而不必考虑如何把这些数据传输给对方,传输数据的任务由计算机网络完成。两个进程顺利通信的前提条件是它们所在的主机都连接到了计算机网络上。网络协议是网络中主机之间通信的语言。不同网络之间的互联靠网络上的标准语言——TCP/IP协议。
二、TCP网络编程
1.OSI参考模型
OSI参考模型把网络分为7层,分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。不同主机之间的相同层称为对等层。
物理层:为上一层提供物理连接,以及规定通信节点之间的机械和电气特性。数据作为原始的比特(bit)流传输。
数据链路层:数据链路层负责在两个相邻节点间的线路上,无差错地传送以帧为单位的数据。典型设备是交换机。
网络层:选择合适的网间路由和交换节点,确保数据及时传送到目标主机。典型设备是路由器。
传输层:根据通信子网的特性最佳地利用网络资源,为两个端系统的会话层提供建立、维护和取消传输连接的功能,以可靠方式或不可靠方式传输数据。信息的传送单位是报文。
会话层:管理进程间的会话过程,即负责建立、管理、终止进程间的会话。数据传送单位是报文。
表示层:对上层数据进行转换,以保证一个主机的应用层的数据可以被另一个主机的应用层理解。数据传送单位是报文。
应用层:确定进程间通信的实际用途,以满足用户实际需求。
发送方每一层会给上一层传递来的数据加上一个信息头。接收方每一层会把信息头去掉。对等层之间互相通信需要遵守一定的规则,如通信的内容和通信的方式,这种规则称为网络协议。TCP/IP参考模型分为4个层次:应用层、传输层、网络互联层和主机-网络层。
2.TCP/IP参考模型
TCP/IP参考模型中有两个非常重要的协议:TCP协议:面向连接的、可靠的协议;UDP协议:不可靠的、无连接协议。IP协议:IP地址(IP网址+IP主机地址),网络掩码与IP地址进行二进制与操作,得到的结果就是IP网址。DNS协议采用DNS服务器来提供把域名转换为IP地址。TCP采用端口区分进程。TCP连接的两个端点用端口来标识。每个进程有了唯一的地址,TCP就能保证把数据顺利送达特定的进程。端口号的范围为0到65535。0-1023操作系统保留。服务端端口号需要指定,客户端端口号一般系统动态分配。TCP和UDP都用端口来标识进程,取值范围各自独立,允许存在取值相同的TCP端口和UDP端口。客户/服务器模式,一个服务器进程会同时为多个客户进程服务。
以下是IP地址分类:
传输层向应用层提供了套接字Socket接口,Socket封装了下层数据传输细节,应用层程序通过Socket来建立与远程主机的连接,以及进行数据传输。Java中有3种套接字类:Socket、ServerSocket、DatagramSocket。Socket和ServerSocket建立在TCP协议上,DatagramSocket建立在UDP协议上。
(一)Socket用法详解
客户端/服务器通信模式中,客户端需要主动创建与服务器连接的Socket,服务端收到客户端的连接请求,也会创建与客户连接的Socket。 等待建立连接的超时时间,默认情况下会一直等待下去,通过不带参数的Socket构造方法可以设定等待时间,单位毫秒,为0表示永远不会超时。 设定服务器地址,InetAddress 设定客户端地址,Socket对象既包含服务器IP和端口也包括客户端地址和端口,对于拥有多个IP地址的客户端可指定使用哪个IP。
1.IP地址表示类InetAddress
Internet上的主机有两种表示地址的方式:域名和IP地址。有时候需要通过域名来查找它对应的IP地址,有时候又需要通过IP地址来查找主机名。这时候可以利用java.net包中的InetAddress类来完成任务。
InetAddress类是IP地址封装类,同时它也是一个比较奇怪的类——没有公共的构造方法,只能利用该类的一些静态方法来获取对象实例,然后再通过这些对象实例来对IP地址或主机名进行处理。该类常用的一些方法有如下。
pulic static InetAddress getByName(String hostname):根据给定的主机名创建一个InetAddress对象,可用来查找该主机的IP地址。
public static InetAddress getByAddress(byte[] addr):根据给定的IP地址创建一个InetAddress对象,可用来查找该IP对应的主机名。
public String getHostAddress():获取IP地址。
public String getHostName():获取主机名。
练习示例:返回从控制台输入的域名相对应的IP地址,若没有给出域名,则返回本地主机的IP地址。
代码如下:
- package com.OpenWealth.ZSY;
- import java.io.*;
- import java.net.InetAddress;
- public class InetAddressDemo {
- public static void main(String[] args ){
- //从键盘读取数据的标准写法
- BufferedReader inBuff=new BufferedReader(new InputStreamReader(System.in));
- InetAddress[] ips=null;
- try {
- String domainName=inBuff.readLine();
- ips=InetAddress.getAllByName(domainName);//一个域名对应的主机可能有多个ip地址
- System.out.println("域名:"+domainName+"对应的主机ip地址有:");
- for(InetAddress ip : ips){
- System.out.println("ip::"+ip.toString());
- }
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
运行结果:
2.Socket通信过程
客户连接服务端的异常:UnknownHostException(无法识别主机名或IP地址),ConnectionException(服务端没有监听指定端口或者拒绝连接),SocketTimeoutException(连接超时),BindException(无法把Socket对象与客户端IP或端口绑定)。
getInputStream和getOutputStream:如果Socket还没有连接或者已经关闭,或者已经调用shutdownInput(shutdownOutput)方法,则关闭输入输出流。
当通信结束,应及时通过close方法关闭Socket。强烈建议放在finally代码中确保总是执行。
Socket状态:
isClose(如果已连接到远程主机,且还未关闭,返回true);
isConnected(如果曾经连接到远程主机,返回true);
isBound(如果已经与一个本地端口绑定,则返回true)半关闭Socket。
结束通信的几种方式:
(1)字符流:约定特殊字符;
(2)约定数据长度;
(3)发送方发完数据后,关闭Socket,这时接收端调用输入流的read方法返回 值为-1; (4)关闭输入或输出流,最终还需要调用Socket的close方法关闭Socket连接isInputShutdown和isOutputShutdown用于检测输入输出流状态。
通信双方的终止方式:
(1)自然结束;
(2)提前终止服务器端:服务器端突然结束,客户端会仍然会发送完全部数据后自然结束,这是因为服务器端底层Socket并没有立即释放,操作系统探测到还有发送给该端口的数据,会继续占用一断时间;
(3)突然终止客户端:抛出Connection reset异常;
(4)关闭或半关闭:服务器端抛出Connection reset异常Socket选项:TCP_NODELAY:是否立即发送数据,Negale算法,发送大批量数据,并且接收端及时响应的情况下很有用,如果是发送少量数据并且接收端不及时响应则会严重降低性能,采用该算法将把发送数据缓冲起来到一定大小后一次性发出,等待接收端响应后再发送下一批数据。 默认false采用该算法。
SO_RESUSEADDR:调用Socket的close方法关闭连接后,不会立刻端口,这时其他进程绑定到该端口将失败,设置SO_RESUSEADDR为true将使得其他进程可以重用这个端口。必须在绑定到端口之前调用。一般是服务器端存在该问题。新进程不会收到之前的数据。 SO_TIMEOUT:接收数据的等待超时时间,单位毫秒,默认0表示永远等待。要在接收数据之前设置。
SO_LINGER:Socket连接是否立即关闭SO_SNFBUF:输出数据缓冲区大小,大批量数据建议大点,频繁传送小数据建议小点SO_RCVBUF:输入数据缓冲区大小。 SO_KEEPALIVE:为true时底层实现会监视连接有效性,客户端使用 OOBINLINE:为true时表示支持发送一个字节的紧急数据,一般不用。
服务类型:
IP规定了4中服务类型:1、低成本:发送成本低;2、高可靠性:保证鲍数据可靠的发送到目的地;3、高吞吐量:一次接受发送大量数据;4、最小延迟:传输数据快,最快发送到目的地设置服务类型:setTrafficClass(int trafficClass);底成本:0x02 高可靠:0x04 最高吞吐量:0x08最小延迟:0x10连接时间、延迟、带宽的相对重要性:setPerformancePreference。
(二)ServerSocket用法详解
服务器端创建ServerSocket监听特定端口,ServerSocket负责接收客户连接请求。 ServerSocket三个参数:port,backlog,bindAddr。
绑定端口port:绑定失败将抛出BindException,可能由于端口已被占用或端口不能被该用户使用(例如1~1023可能只能用root用户绑定)所致,参数0表示操作系统分配,称为匿名端口。
客户连接请求队列的长度:客户端连接请求由操作系统的先进先出队列管理,ServerSocket通过accept方法取出该队列的请求,backlog用于改变请求队列长度,但是如果该值大于操作系统限制或小于等于0则依然使用操作系统的值。
设定绑定的IP地址:bindAddr参数,多IP时可以指定绑定到哪个IP 不带参数的构造器:创建完ServerSocket对象后需要调用bind方法,bind之前可以设置一些参数,有些参数必须在bind之前调用。accept方法会阻塞,客户端连接上来后才返回。 ServerSocket不用close,程序结束时会释放,也可显示调用。ServerSocket的isClosed,isBound与Socket类似。
获取服务器绑定IP和端口:getInetAddress和getLocalPort FTP使用匿名端口 ServerSocket选项:
- SO_TIMEOUT:accept等待客户端连接超时的时间,0表示永远不超时,一直等 待客户端连接。
- SO_REUSEADDR:和Socket的类似
- SO_RCVBUF:接收数据的缓冲区的大小,与操作系统有关。连接时间、延迟和带宽的相对重要性:与Socket类似 多线程服务器(重点)前面的例子中的服务器都无法同时响应多个客户连接请求。
器与客户端之间的通信:
练习示例:客户端程序向服务端程序同时上传多张图片,服务端程序可以响应客户端并发访问,实现图片的多线程上传功能。
代码如下:
- package com.OpenWealth.ZSY;
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.io.IOException;
- import java.net.*;
- import java.util.ArrayList;
- import java.util.List;
- import java.io.*;
- public class TCPClientSocket {
- public static void main(String args[]) throws Exception{
- BufferedReader buff=new BufferedReader(new InputStreamReader(System.in));
- //同时上传多张图片
- while(true){
- String imgUrl=buff.readLine();
- new Thread(new MyUpLoadPicClient(imgUrl,"localhost",8866)).start();
- }
- }
- /**
- * 实现了同时上传多张图片的客户端;可以上传多种格式的图片
- * @author 支胜勇
- *
- */
- public static class MyUpLoadPicClient implements Runnable{
- private File imgFile;
- private String ip;
- private int port;
- private Socket soc;
- private List<String> extensList=null;
- private OutputStream out;
- private FileInputStream fis;
- /***
- * 初始化构造方法
- * @param imgUrl
- * @param ip:服务器IP地址
- * @param port:服务器端口
- * @throws Exception
- */
- public MyUpLoadPicClient(String imgUrl,String ip,int port) throws Exception{
- this.imgFile=new File(imgUrl);
- this.ip=ip;
- this.port=port;
- extensList=new ArrayList<String>();
- extensList.add(".jpg");
- extensList.add(".png");
- extensList.add(".gif");
- extensList.add(".jpeg");
- extensList.add(".bmp");
- }
- public boolean isImg(){
- if(!imgFile.exists()){//||!imgFile.isFile()
- System.out.println("图片不存在,请重新上传!");
- return false;
- }
- if(!imgFile.isFile()){//||!imgFile.isFile()
- System.out.println("不是图片文件,请重新上传!");
- return false;
- }
- String extenName=imgFile.getName().substring(imgFile.getName().lastIndexOf(".")).toLowerCase();
- if(!extensList.contains(extenName)){
- System.out.println("请上传jpg,png,gif,bmp,jpeg格式的图片");
- return false;
- }
- if(imgFile.length()>1024*1024*5){
- System.out.println("图片过大,请上传5m以内的图片!");
- return false;
- }
- try {
- fis=new FileInputStream(imgFile);
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return true;
- }
- /***
- * 执行向服务器上传图片的代码
- */
- @Override
- public void run() {
- // TODO Auto-generated method stub
- if(!isImg()){
- return;
- }else{
- try {
- this.soc=new Socket(this.ip,this.port);
- out=soc.getOutputStream();
- byte[] buff=new byte[1024];
- int len=0;
- while( (len=fis.read(buff)) != -1 ){
- out.write(buff,0,len);
- }
- soc.shutdownOutput();//告诉服务器上传结束
- InputStream in=soc.getInputStream();
- byte[] bufIn=new byte[1024];
- int num=in.read(bufIn);
- System.out.println(new String(bufIn,0,num));
- fis.close();
- out.close();
- soc.close();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
- }
- package com.OpenWealth.ZSY;
- import java.io.*;
- import java.net.*;
- public class TCPServer{
- @SuppressWarnings("resource")
- public static void main(String[] args) throws Exception{
- ServerSocket ss=null;
- ss=new ServerSocket(8866);
- while(true){
- try {
- Socket soc=ss.accept();
- new MyPicServerThread(soc).start();//开启服务端接受图片的线程
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- /***
- * 实现了多用户并发上传图片的服务端
- * @author 支胜勇
- *
- */
- public static class MyPicServerThread extends Thread{
- private Socket soc=null;
- public MyPicServerThread(Socket _soc){
- this.soc=_soc;
- }
- @Override
- public void run() {
- // TODO Auto-generated method stub
- String clientIp=soc.getInetAddress().getHostAddress();
- try {
- System.out.println(clientIp+"已连接!");
- InputStream in=soc.getInputStream();
- FileOutputStream fos=new FileOutputStream("E:\\TcpTest.png");
- byte[] buff=new byte[1024];
- int len=0;
- while((len=in.read(buff))!=-1){
- fos.write(buff,0,len);
- }
- OutputStream out=soc.getOutputStream();
- out.write("图片上传成功!".getBytes());
- fos.close();
- soc.close();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- throw new RuntimeException("客户端"+clientIp+"上传失败");
- }
- }
- }
- }
运行结果:
JAVA TCP网络编程学习笔记的更多相关文章
- JAVA UDP网络编程学习笔记
一.UDP网络编程概述 采用TCP协议通信时,客户端的Socket必须先与服务器建立连接,连接建立成功后,服务器端也会持有客户端连接的Socket,客户端的Socket与服务器端的Socket是对应的 ...
- Java Socket网络编程学习笔记(一)
0.前言 其实大概半年前就已经看过网络编程Socket的知识了(传统IO),但是因为长时间的不使用导致忘的一干二净,最近正好准备校招,又重新看了网络编程这一章, 是传统IO(BIO)相关的内容,故在此 ...
- 转 网络编程学习笔记一:Socket编程
题外话 前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公司使用的一些控件的开发,浏览器兼容性搞死人:但主要是因为这段时间一直在看html5的东西,看到web socket时觉得很有 ...
- Java - TCP网络编程
Java - TCP网络编程 Server 逻辑思路: 创建ServerSocket(port),然后服务器的socket就启动了 循环中调用accept(),此方法会堵塞程序,直到发现用户请求,返回 ...
- 网络编程学习笔记(二)基于TCP的Socket编程
1.Socket:英文意思插座.两个Java应用程序可以通过一个双向的网络通信连接实现数据交换,这个双向链路的一端称为一个Socket. 2.Socket通常用来实现client-server(客户端 ...
- Java网络编程学习笔记
Java网络编程,我们先来看下面这一张图: 由图可得:想要进行网络编程,首先是服务器端通过ServerSocket对某一个端口进行监听.通过accept来判断是否有客户端与其相连.若成功连上,则通过r ...
- Linux C网络编程学习笔记
Linux C网络编程总结报告 一.Linux C 网络编程知识介绍: 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端:(client) 在网络程序中, ...
- 网络编程学习笔记:Socket编程
文的主要内容如下: 1.网络中进程之间如何通信? 2.Socket是什么? 3.socket的基本操作 3.1.socket()函数 3.2.bind()函数 3.3.listen().connect ...
- 网络编程学习笔记一:Socket编程
“一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览 ...
随机推荐
- 【M16】谨记80-20法则
1.开始编写代码时,不要过多考虑效率,而应该首先考虑逻辑的清晰性和代码的可读性. 2.后期通过测试找到效率的瓶颈所在,而不是靠猜测.然后,针对性地去解决.也就是80%的时间去解决这20%的代码.
- MySQL安装详解(V5.5 For Windows)
前言 这几年一直在用MySQL,并且是Windows+.Net+MySQL的搭配,用MyISAM引擎支持过单表每天千万以上的数据递增,TB级的数据MySQL游刃有余.最近在做一个较大并发的项目,尝试了 ...
- Gym 100637F F. The Pool for Lucky Ones 暴力
F. The Pool for Lucky Ones Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/10 ...
- 使用C# 实现文件锁
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- Spring MVC Junit4 单元測试 JunitTest
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSmVyb21lX3M=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...
- DashClock
https://github.com/romannurik/dashclock/ https://github.com/nhaarman/DashPinkpop dashclock-master.zi ...
- Android 滑动效果基础篇(四)—— Gallery + GridView
Android系统自带一个GridView和Gallery两个控件,GridView网格显示,Gallery单个浏览,两者结合起来可以真正实现Gallery浏览图片效果. 本示例通过GridView和 ...
- oc-03-OC访问OC源文件、C源文件中的函数
show.h #ifndef __OCDay01__Show__ #define __OCDay01__Show__ #include <stdio.h> extern void test ...
- [ZZ]C++中,引用和指针的区别
(1) 引用总是指向一个对象,没有所谓的 null reference .所有当有可能指向一个对象也由可能不指向对象则必须使用 指针. 由于C++ 要求 reference 总是指向一个对象所以 re ...
- 分布式ActiveMQ集群--转载
原文地址:http://shensy.iteye.com/blog/1752529 回顾总结前一段时间学习的ActiveMQ分布式集群相关的知识,分享出来希望对看到的人有所帮助. 一.分布式Activ ...