第一次学习 Socket 编程
来源:互联网 发布:中国企业数据 编辑:程序博客网 时间:2024/05/22 03:02
服务器端:
import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.PrintWriter;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;public class Server { // 绑定地址 private static String ADDRESS = "127.0.0.1"; // 监听端口号 private static final int PORT = 8080; public static void main(String[] args) throws IOException { // 创建 server 对象 Server server = new Server(); // 调用 service() 方法,等待请求 server.service(); } public void service() { // 初始化 ServerSocket server = null; try { // 创建 InetAddress 对象简单的方法是调用其静态方法 getByName() InetAddress adr = InetAddress.getByName(ADDRESS); // 创建 ServerSocket 对象 // 参数说明:1.监听端口号 2.请求最大队列长度 3.绑定地址 server = new ServerSocket(PORT, 5, adr); System.out.println("服务启动成功"); } catch (IOException e) { System.out.println("服务启动失败"); e.printStackTrace(); } // 循环等待请求 while (true) { // 初始化参数 Socket socket = null; try { // accept 方法会从连接请求队列中取出一个连接请求,然后创建与客户端连接的 Socket 对象,并将它返回 // 如果队列中没有连接请求,accept() 方法就会一直等待,直到接收到了连接请求才返回 socket = server.accept(); // 打印请求地址和端口 System.out.println("连接来自:" + socket.getLocalSocketAddress()); // 解析客户端发来的请求 String request = parse(socket.getInputStream()); // 对请求进行处理 String response = process(request); // 将请求结果发送给客户端 send(socket.getOutputStream(), response); // 关闭当前的 socket socket.close(); } catch (Exception e) { e.printStackTrace(); // 出错时重新开始新的循环 continue; } } } // 返回值可以认为是一个 Request 的对象 protected String parse(InputStream input) { int len; String charset = "UTF-8"; byte[] buffer = new byte[2048]; try { // 一次读取全部的请求信息(因为请求一般不会太长) len = input.read(buffer); // 解析处理(暂无) // TODO // 解析结果返回 return new String(buffer, 0, len, charset); } catch (IOException e) { e.printStackTrace(); return "解析请求时发生异常!"; } } // 对请求进行处理,并返回处理结果 protected String process(String request) { // 这里只做了简单的处理 return "来自服务端的消息:\r\n" + request; } // 这里的第二个参数,可以理解为 Response 对象 protected void send(OutputStream output, String response) { // 创建 PrintWriter 对象,第二个参数为 true,则 println、printf 或 format 方法将刷新输出缓冲区 PrintWriter out = new PrintWriter(output, true); // 将 response 发送给客户端 out.println(response); }}
客户端:
import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.Socket;public class Client { public static void main(String[] args) { // 连接到的主机地址 String host = "127.0.0.1"; // 连接的端口号 int port = 8080; try { // 创建连接服务器的 Socket 对象 Socket socket = new Socket(host, port); // 要从连接的另一端接受字节流,需要调用 Socket 类的 getInputStream 方法获取 InputStream 对象, // 之后可以将其作为参数创建需要的 BufferedReader 对象 BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 需要发送字节流时,需要调用 Socket 类的 getOutputStream 方法获取 OutputStream 对象, // 之后可以将其用来创建需要的 PrintWriter 对象 PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // 向服务器端发送请求 out.println("Hello World!"); // 接受服务器端发送回来的信息 while (true) { // ready() 方法判断此数据流是否已准备好被读取,这里主要是判断缓冲区是否为空 if (in.ready()) { // 缓冲区不为空时,按行读取流中数据 String line; while ((line = in.readLine()) != null) { System.out.println(line); } // 读取结束后跳出循环 break; } // 缓冲区为空,则线程等待 50 毫秒(等待服务器发送数据) Thread.sleep(50); } // 关闭 socket socket.close(); } catch (Exception e) { e.printStackTrace(); } }}
知识点补充:
1. 构造 ServerSocket
ServerSocket 构造方法有以下四种形式:
- ServerSocket()throws IOException
- ServerSocket(int port) throws IOException
- ServerSocket(int port, int backlog) throws IOException
- ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException
在以上构造方法中,参数 port 指定服务器要绑定的端口(监听端口),参数 backlog 指定客户端连接请求队列的长度,参数 bindAddr 指定服务端要绑定的 IP 地址。
1.1 绑定端口
除了第一个不带参数的构造方法之外,其它构造方法都会使服务器和特定端口绑定,端口由参数 port 指定,当运行时无法绑定到某端口时,会抛出 IOException, 更确切的说是 BindException,它是 IOException 的子类。BindException 一般有以下几个原因造成:
a)端口已经被其它服务器进程占用
b)在某些操作系统中,如果没有以超级用户的身份来运行服务器程序,那么操作系统不允许服务器绑定到 1~1023 端口
如果把参数 port 设为 0,表示由操作系统分配任意一个可用的端口。由服务器分配的端口也称之为匿名端口。对于多数服务器,会使用明确的端口,因为客户程序需要事先知道服务器端口,才能方便的访问服务器。在某些场合才会使用到匿名端口,比如 FTP(文件传输)协议等。
1.2 设定客户连接请求队列的长度
当服务器进程运行时,可能会同时监听到多个客户的连接请求。管理这些请求的任务是由操作系统来完成的,操作系统会把这些连接请求存储在一个先进先出的队列中。很多系统限定了队列的最大长度为 50。当队列中的请求达到了队列的最大容量时,服务器进程所在的主机就会拒绝新的连接请求。只有当服务器进程通过 ServerSocket 的accep() 方法从队列中取出连接请求,使队列腾出空位时,队列才能继续加入新的连接请求。
对于客户端进程,如果它发出的连接请求被加入到服务器的队列中,就意味着客户和服务器的连接建立成功,客户进程从 Socket 构造方法中正常返回。
ServerSocket 构造方法的 backlog 参数用来显示设置连接请求队列的长度,它将覆盖系统设定的最大长度,但在以下情况,仍然会采用系统限定的队列的最大长度:
a)backlog 参数的值大于系统限定的队列的最大长度
b)backlog 参数的值小于或等于 0
c)在 ServerSocket 构造函数中没有设置 backlog 参数
1.3 设定绑定的 IP 地址
如果主机只有一个 IP 地址,那么默认情况下,服务器就会与该 IP 地址绑定。ServerSocket 4个构造方法中只有第四个显示指定服务器要绑定的 IP 地址,该构造方法适用于具有多个 IP 地址的主机。
1.4 默认构造方法的作用
ServerSocket 有一个不带任何参数的默认构造方法,通过该构造方法创建的 ServerSocket 不和任何端口绑定,但是之后需要通过 bind() 方法与特定端口绑定。这么做的目的是,允许服务器在绑定到特定端口之前,先设置 ServerSocket 的一些选项。因为一点服务器与特定端口绑定,有些选项就不能在改变了。
例如:
ServerSocket serverSocket=new ServerSocket();serverSocket.setReuseAddress(true); // 设置 ServerSocket 选项serverSocket.bind(new InetSocketAddress(8080)); // 与8080端口绑定如果把 2、3 句代码互换,那么设置 ServerSocket 选项就完全不起作用了。
ServerSocket 有以下 3 个选项:
- SO_TIMEOUT:表示等待客户连接的超时时间
- SO_REUSEADDR:表示是否允许重用服务器所绑定的地址
- SO_RCVBUF:表示接收数据的缓冲区的大小
- 第一次学习 Socket 编程
- 第一次学习编程
- linux socket编程学习
- Android学习--Socket编程
- linux socket编程学习
- socket编程学习笔记
- socket 编程学习
- Python socket编程学习
- socket编程学习总结
- linux学习:socket编程
- socket编程学习
- socket 编程学习笔记
- socket编程学习笔记
- Socket编程学习记录
- 学习-Socket编程讲解
- JAVA Socket 编程学习
- socket编程学习1----socket编程框架
- LINUX C下socket编程第一次发送数据出错
- 软中断/tasklet/工作队列
- 程序猿10级
- 安卓环境搭建(图文版包括最新安卓4.0)
- VS源文件提取工具vsjuicer 下载及使用方法
- 信息网站
- 第一次学习 Socket 编程
- 蝴蝶兰与鳞托菊
- 面试训练顺时针打印矩阵
- C# VS2010发布问题---<compilation debug="true" targetFramework="4.0">
- 在XP SP3 IIS5.1 部署MVC运行环境
- 七夕怀念
- 从数据库sqlite3读取数据
- linux slab精细结构
- freeswitch 注册用户