JAVA网络编程之——TCP通信
2015-06-26 12:19
567 查看
JAVA网络编程之——TCP通信
TCP是面向连接的,所以是C/S架构,服务器端ServerSocket首先要创建启动,然后监听某一个端口,等待Client的Socket连接,如果有连接发过来,就创建一个专门的Socket_New连接用于和这个Client的Socket进行通信。
那么问题来了:
1. Client的Socket连接有自已的IP和Port,那服务器与它通信的这个Socket_New的端口号是不是ServerSocket监听的端口号呢?建立连接——>数据通信,这个具体流程怎样?
2. 服务器端 多线程解决多Client的方法。
首先,看一下Client和Server的交互过程:
![](http://img.blog.csdn.net/20150626121348277?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmFud2Vuamllb2s=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
Socket一些常用方法 :
getInetAddress(); 远程服务端的IP地址
getPort(); 远程服务端的端口
getLocalAddress() 本地客户端的IP地址
getLocalPort() 本地客户端的端口
getInputStream(); 获得输入流
getOutStream(); 获得输出流
值得注意的是,在这些方法里面,最重要的就是getInputStream()和getOutputStream()了。一般不直接使用,因为网络上的流是字节流,JAVA中一般转成字符流,再包装一下进行更方便的处理。
下面看一下最基本的TCP Server的编写步骤,客户端可以用Windows自带的telnet来测试。
服务器要响应多个Client的连接就需要用到多线程,每当有一个Client发出连接请求,都创建一个新的线程和这个Client进行专线通信。
下面是最基本的一个多线程处理Client请求的例子,先看主程序:
上面代码中, new Server(s)是一个实现了Runnable接口的多线程类的对像,把ss.accept()返回来的socket连接给这个多线程对像,它就知道自已要干什么了(和哪个Client的socket进行通信)。
Server代码如下:
上面代码没有进行异常处理,这样编译是不通过的,但是为了简洁直观,这里只贴出了关键内容。
运行服务器程序,进行测试如下:
![](http://img.blog.csdn.net/20150626123106159?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmFud2Vuamllb2s=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
经测试,达到了预期的效果,每个Client可以和Server单独通信。
accept函数主要用于服务器端,默认会阻塞进程,直到有一个客户请求连接,建立好连接后,它返回的一个新的套接字
Socket_New(),此后,服务器端即可使用这个新的套接字Socket_New()与该客户端进行通信,而ServerSocket
则继续用于监听其他客户端的连接请求。并且新的Socket_New是否为阻塞和非阻塞属性,与监听的Socket一样,监听的Socket为阻塞,则新的Socket_New也为阻塞,反之一样。
至此,我的困惑产生了,这个新的套接字
Socket_New与监听套接字ServerSocket是什么关系?它所代表的socket对象包含了哪些信息?Socket_New是否占用了新的端口与客户端通信?
设想一下,由于网站的服务器也是一种TCP服务器,使用的是80端口,并不会因客户端的连接而产生新的端口给客户端服务,该客户端依然是向服务器端的80端口发送数据,其他客户端依然向80端口申请连接。因此,可以判断,Socket_New并没有占用新的端口与客户端通信,依然使用的是与监听套接字socketfd_new一样的端口号。再说了,在一台机器中,端口的使用数量是65535,是有限的,不可能无限制的占用新端口来使用,那这么说,难道一个端口可以被两个socket对象绑定?当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢?
个人理解如下:
首先,一个端口肯定只能绑定一个socket。我认为,服务器端的端口在new ServerSocket(int port)的时候已经绑定到了监听套接字ss所描述的对象上,accept函数新创建的Socket_New对象其实并没有进行端口的占有,而是复制了ServerSocket ss的本地IP和端口号,并且记录了连接过来的客户端的IP和端口号。
那么,当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢?
客户端发送过来的数据可以分为2种,一种是连接请求,一种是已经建立好连接后的数据传输。由于TCP/IP协议栈是维护着一个接收和发送缓冲区的。在接收到来自客户端的数据包后,服务器端的TCP/IP协议栈应该会做如下处理: 1. 如果收到的是请求连接的数据包,则传给监听着连接请求端口的ServerSocket 套接字,进行accept处理;
2. 如果是已经建立过连接后的客户端数据包,则将数据放入接收缓冲区。
这样,当服务器端需要读取指定客户端的数据时,则可以利用socketfd_new 套接字通过recv或者read函数到缓冲区里面去取指定的数据(因为socketfd_new代表的socket对象记录了客户端IP和端口,因此可以鉴别)。
TCP是面向连接的,所以是C/S架构,服务器端ServerSocket首先要创建启动,然后监听某一个端口,等待Client的Socket连接,如果有连接发过来,就创建一个专门的Socket_New连接用于和这个Client的Socket进行通信。
那么问题来了:
1. Client的Socket连接有自已的IP和Port,那服务器与它通信的这个Socket_New的端口号是不是ServerSocket监听的端口号呢?建立连接——>数据通信,这个具体流程怎样?
2. 服务器端 多线程解决多Client的方法。
首先,看一下Client和Server的交互过程:
Socket一些常用方法 :
getInetAddress(); 远程服务端的IP地址
getPort(); 远程服务端的端口
getLocalAddress() 本地客户端的IP地址
getLocalPort() 本地客户端的端口
getInputStream(); 获得输入流
getOutStream(); 获得输出流
值得注意的是,在这些方法里面,最重要的就是getInputStream()和getOutputStream()了。一般不直接使用,因为网络上的流是字节流,JAVA中一般转成字符流,再包装一下进行更方便的处理。
下面看一下最基本的TCP Server的编写步骤,客户端可以用Windows自带的telnet来测试。
//1.创建服务器端Socket ServerSocket ss = new ServerSocket(10001); System.out.println("等待客户端连接....."); //2.阻塞等待客户端连接 Socket s = ss.accept(); System.out.println("连接成功!"); //3.程序运行到此处表明连接成功,获取Socket的输入输出流 OutputStream ops = s.getOutputStream(); InputStream ins = s.getInputStream(); //4.服务器返回给客户端一个信息 ops.write("Hello,欢迎访问TCP服务器".getBytes()); //5.把网络字节流转换为字符流再包装成Buffer流 BufferedReader br = new BufferedReader(new InputStreamReader(ins)); //6.利用BufferReader的"行读"功能,读取客户端输入的一行数据 // 此处会阻塞读入,直到客户端输入一行数据(telnet输入完成回册) System.out.println(br.readLine()); //7.关闭包装类会自动关闭包装类中所有的底层类 br.close(); s.close(); ss.close();
服务器要响应多个Client的连接就需要用到多线程,每当有一个Client发出连接请求,都创建一个新的线程和这个Client进行专线通信。
下面是最基本的一个多线程处理Client请求的例子,先看主程序:
public class TCPServerTest { public static void main(String [] args) throws IOException { //1.创建TCP Server,监听10001号端口 ServerSocket ss = new ServerSocket(10001); while(true) { //2.等待客户端连接 System.out.println("等待客户端连接..."); Socket s = ss.accept(); System.out.println("连接成功!"); //3.创建一个新线程和上面的客户端进行专线连接 new Thread(new Server(s)).start(); //4.打印连接信息,然后等待下一个连接 System.out.println("新线程创建成功,客户端信息如下:"); System.out.println(" 客户端IP: " + s.getInetAddress().getHostAddress()); System.out.println(" 客户端Port: " + s.getPort()); System.out.println(); } } }
上面代码中, new Server(s)是一个实现了Runnable接口的多线程类的对像,把ss.accept()返回来的socket连接给这个多线程对像,它就知道自已要干什么了(和哪个Client的socket进行通信)。
Server代码如下:
//服务器线程 public class Server implements Runnable { private Socket s; //构造器,主线程传过来个socket,子线程才知道和哪个Socket连接 public Server(Socket s) { this.s = s; } public void run() { //程序执行到此说明服务器响应Client并新建Thread成功,获取输入输出流 InputStream ips = s.getInputStream(); OutputStream ops = s.getOutputStream(); //将网络字节流转换为字符流,再包装成Buffer流 BufferedReader br = new BufferedReader(new InputStreamReader(ips)); String recvStr = null; while(true) { //阻塞等待客户端输入一行数据 recvStr = br.readLine(); //如果客户端输入quit则退出循环,关闭连接 if(recvStr.equalsIgnoreCase("quit")) { break; } //把收到的数据回打给客户端 String str = "服务器已收到,内容为:" + recvStr; ops.write(str.getBytes()); } br.close(); s.close(); }
上面代码没有进行异常处理,这样编译是不通过的,但是为了简洁直观,这里只贴出了关键内容。
运行服务器程序,进行测试如下:
经测试,达到了预期的效果,每个Client可以和Server单独通信。
accept函数主要用于服务器端,默认会阻塞进程,直到有一个客户请求连接,建立好连接后,它返回的一个新的套接字
Socket_New(),此后,服务器端即可使用这个新的套接字Socket_New()与该客户端进行通信,而ServerSocket
则继续用于监听其他客户端的连接请求。并且新的Socket_New是否为阻塞和非阻塞属性,与监听的Socket一样,监听的Socket为阻塞,则新的Socket_New也为阻塞,反之一样。
至此,我的困惑产生了,这个新的套接字
Socket_New与监听套接字ServerSocket是什么关系?它所代表的socket对象包含了哪些信息?Socket_New是否占用了新的端口与客户端通信?
设想一下,由于网站的服务器也是一种TCP服务器,使用的是80端口,并不会因客户端的连接而产生新的端口给客户端服务,该客户端依然是向服务器端的80端口发送数据,其他客户端依然向80端口申请连接。因此,可以判断,Socket_New并没有占用新的端口与客户端通信,依然使用的是与监听套接字socketfd_new一样的端口号。再说了,在一台机器中,端口的使用数量是65535,是有限的,不可能无限制的占用新端口来使用,那这么说,难道一个端口可以被两个socket对象绑定?当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢?
个人理解如下:
首先,一个端口肯定只能绑定一个socket。我认为,服务器端的端口在new ServerSocket(int port)的时候已经绑定到了监听套接字ss所描述的对象上,accept函数新创建的Socket_New对象其实并没有进行端口的占有,而是复制了ServerSocket ss的本地IP和端口号,并且记录了连接过来的客户端的IP和端口号。
那么,当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢?
客户端发送过来的数据可以分为2种,一种是连接请求,一种是已经建立好连接后的数据传输。由于TCP/IP协议栈是维护着一个接收和发送缓冲区的。在接收到来自客户端的数据包后,服务器端的TCP/IP协议栈应该会做如下处理: 1. 如果收到的是请求连接的数据包,则传给监听着连接请求端口的ServerSocket 套接字,进行accept处理;
2. 如果是已经建立过连接后的客户端数据包,则将数据放入接收缓冲区。
这样,当服务器端需要读取指定客户端的数据时,则可以利用socketfd_new 套接字通过recv或者read函数到缓冲区里面去取指定的数据(因为socketfd_new代表的socket对象记录了客户端IP和端口,因此可以鉴别)。
相关文章推荐
- 黑马程序员——Java基础---网络编程、反射
- VMware10中安装centos7没有可用的网络设备
- 转 maven 教程一 入门 (http://wentao365.iteye.com/blog/903396)
- 跨平台高性能TCP服务器框架 &boost
- 网络编程API-下 (I/O复用函数)
- HTTP服务器头部信息
- JAX-RS入门 八: HTTP响应
- TCP与UDP区别
- 【分析总结框架记录】基于ZMQ的游戏网络层基础架构
- Linux tcpdump命令详解
- tcprstat工具安装与使用
- http错误代码解析
- http长链接问题
- JS使用http协议实现ping功能
- HttpURLConnection的流式输出的缺陷和解决方法
- 构建简易网络与网络设备的简单配置(Cisco Packet Tracer)第二弹:静态路由协议配置
- TCP/IP协议分层详解
- httpclient4.3简单封装,模拟登录
- TCP三次握手 与 四次断开 的详细过程剖析
- android 开发 常用到的一些网络通信包