相对于C和C++来说,Java中的socket编程是比较简单的,比较多的细节都已经被封装好了,每次创建socket连接只需要知道地址和端口即可。

在了解socket编程之前,我们先来了解一下读写数据的数据流类中一些需要注意的东西。

BufferedReader与DataInputStream的区别:

通常我们常用到的字节输入输出流有BufferedReaderPrintWriterDataInputStreamDataOutputStream这两对。这些类都属于java.io包。

那么两者之间有什么区别呢?

区别就是前者有个缓冲区,假如我们人为设置为100k(不设置亦可,有默认值),当这个缓冲区存储的内容达到100k的时候,类对象才会进行读入或写入操作。

而Stream的两个对象是没有缓冲区的,它们是收到什么数据就即刻进行读出和写入。

所以在进行socket编程的时候,这两对最好不要交替使用,因为当有数据存到前面提到的缓存里的时候,stream对象没有办法读到缓存里的东西,所以会造成数据的丢失。

在这里我们另外说一说PrintWriter类,先看看比较常用的两个构造方法:

在第二个构造方法中,参数2指明该对象是否自动将缓冲区里的数据流自动刷出,一般来说我们可以采用第二种构造方法,将参数2设为true。

否则,在每次用PrintWriter对象调用printXXX方法的时候,后面就要紧接着使用flush方法。

比如:

PrintWriter pw = new PrintWriter(socket.getOutputStream);

pw.println(“写出数据”);

pw.flush();      

如果你不这么做的话,pw对象可能会因为你要写出的数据并未到达缓冲区指定大小而不作任何操作。这个时候你的线程就会阻塞!!所以关于这一点务必小心。

Java里的Socket工作模式:

在socket编程中我们基本上需要用到这些类:

SocketServer、Socket、BufferedReader与PrintWriter(或者DataInputStream与DataOutputStream)。

在服务器中,首先新建一个服务器socket对象:

ServerSocket srvSocket = new ServerSocket(nPort);

一旦接收到请求,则生成一个socket对象:

Socket socket = srvSocket.accept();

然后创建流对象:

BufferedReader bf = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);

客户端里直接进行连接然后创建流对象即可:

Socket socket = new Socket(hostAddr, nPort);

Socket编程基础Java实现:

其实在实现过程中遇到挺多很细节但是又很让人蛋疼的问题,比如前面提到的PrintWriter对象要么初始化的时候就设定为自动刷出缓存区内容,要么就每次写操作后面调用flush方法。下面给出实现方法:

客户端实现:

客户端实现的功能是这样的,输入一些特定的字符串,比如:DATE,BYE,DOY,DOM,DOW什么的,然后让服务器判断输入的是什么命令,然后服务器调用Calendar类返回对应的日期和时间信息。

我希望能从控制台读取用户输入的信息,所以设计了如下代码:

BufferedReader inSys = new BufferedReader(new InputStreamReader(System.in));
while((string = inSys.readLine()) != null && string.length() != 0)
{
System.out.println("客户端这边输入的命令是"+string);
pw.println(string);
System.out.println("服务器返还的数据是"+bf.readLine());
//ctrl+z or Enter to terminate the loop
}

这样的话,但凡是用户按了ctrl+z或者是Enter键,则结束循环。

下面给出客户端完整实现代码:

import java.io.*;
import java.net.*;
import java.util.*;
public class NeroSocketClient {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
System.out.println("新客户端开启");
//三个变量要放在try-catch块前声明,不然的话在finall块中,try里面声明和定义的内容是不可见的,属于不同的作用域,生命周期不同
Socket socket = null;
BufferedReader bf = null;
PrintWriter pw = null;
try {
socket = new Socket("127.0.0.1", 8888);
bf = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream(),true); //参数2:自动flush缓冲区内容
BufferedReader inSys = new BufferedReader(new InputStreamReader(System.in));
String string;
System.out.println("支持的命令如下:");
System.out.println("BYE:结束连接");
System.out.println("DATE:日期和时间");
System.out.println("DOW:day_of_week");
System.out.println("DOM:day_of_month");
System.out.println("DOY:day_of_year");
System.out.println("PAUSE:暂停");
System.out.println("举个例子:客户端这边输入的是DATE");
pw.println("DATE");
pw.flush();
System.out.println ("服务器返还的数据是"+bf.readLine ());
System.out.println("请输入:");
while((string = inSys.readLine()) != null && string.length() != 0)
{
System.out.println("客户端这边输入的命令是"+string);
pw.println(string);
System.out.println("服务器返还的数据是"+bf.readLine());
//ctrl+z or Enter to terminate the loop
}
} catch (IOException e) {
// TODO: handle exception
System.out.println (e.toString ());
}finally{
//关闭连接
try {
if (bf != null) {
bf.close();
}
if (pw != null) {
pw.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e2) {
// TODO: handle exception
}
}
}
}

服务器实现:

设计服务器的时候,对进行数据读写操作的类应用Runnable接口,这样即可实现多线程,因为服务器没可能只对一个客户端提供服务的,所以写练习程序的时候直接写多线程的即可,从最基本的练起没必要,进度太慢。

在服务器的主方法里面,我们通过一个无限循环来不断地接受新发现的连接请求:

ServerSocket srvSocket = new ServerSocket(8888);
while(true) //服务器是需要一直运行的,这样可以不断地监听和接收新的socket连接
{
Socket socket = srvSocket.accept(); //收到新的请求
System.out.println("收到新的socket连接请求");
ServerThread sThread = new ServerThread(socket);
Thread thread = new Thread(sThread);
thread.start();
//上面的三行代码,不妨直接写成:
//new Thread(new ServerThread(socket)).start();
}

这样的话,服务器即可一直运行。

具体操作数据的方法写在从接口继承来的run方法即可,这个方法是必须被重载的。

下面给出服务器实现代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.*;
import java.util.*; public class NeroSocketServer { public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
System.out.println("新服务器开启");
ServerSocket srvSocket = new ServerSocket(8888);
while(true) //服务器是需要一直运行的,这样可以不断地监听和接收新的socket连接
{
Socket socket = srvSocket.accept(); //收到新的请求
System.out.println("收到新的socket连接请求");
ServerThread sThread = new ServerThread(socket);
Thread thread = new Thread(sThread);
thread.start();
//上面的三行代码,不妨直接写成:
//new Thread(new ServerThread(socket)).start();
}
} } //应用这个接口,在run方法里面定义具体操作
class ServerThread implements Runnable
{
private Socket socket;
//构造函数
public ServerThread(Socket s)
{
this.socket = s;
} @Override
public void run() {
// TODO Auto-generated method stub
System.out.println("线程开始");
BufferedReader bf = null;
PrintWriter pw = null; try {
bf = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream(),true); //如果参数2不设为true,则每次都需要执行flush操作
//程序实现功能:通过客户端请求,返回日期、时间等信息
//静态方法,直接调用
Calendar calendar = Calendar.getInstance();
System.out.println("准备进入循环");
while(true) //一直循环,直到用户请求完毕
{
String s_request = bf.readLine();
//将所有命令转换为大写
s_request = s_request.toUpperCase();
System.out.println("当前接受到的命令是"+s_request);
if (s_request.startsWith("BYE")) { //结束
break;
}
if (s_request.startsWith("DATE") || s_request.startsWith("TIME")) {
System.out.println("输出日期和时间");
pw.println(calendar.getTime().toString());
System.out.println("输出完毕");
}
if (s_request.startsWith("DOM")) {
pw.println(""+calendar.get(Calendar.DAY_OF_MONTH)); //以字符形式写入
}
if (s_request.startsWith("DOW")) {
switch (calendar.get(Calendar.DAY_OF_WEEK)) {
case Calendar.SUNDAY:
pw.println("SUNDAY");
break;
case Calendar.MONDAY:
pw.println("MONDAY");
break;
case Calendar.TUESDAY:
pw.println("TUESDAY");
break;
case Calendar.WEDNESDAY:
pw.println("WEDNESDAY");
break;
case Calendar.THURSDAY:
pw.println("THURSDAY");
break;
case Calendar.FRIDAY:
pw.println("FRIDAY");
break;
case Calendar.SATURDAY:
pw.println("SATURDAY");
break;
default:
break;
}
}
if (s_request.startsWith("DOY")) {
pw.println(""+calendar.get(Calendar.DAY_OF_YEAR));
}
if (s_request.startsWith("PAUSE")) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO: handle exception
System.out.println(e.toString());
}
}
}
}
catch (Exception e) {
// TODO: handle exception
System.out.println(e.toString());
}
finally{ //关闭连接
System.out.println("当前客户端断开连接");
try {
if (bf != null) {
bf.close();
}
if (pw != null) {
pw.close();
}
if (socket != null) {
socket.close();
}
} catch (Exception e2) {
// TODO: handle exception
}
}
}
}

最后我们看看客户端和服务器的运行情况,先运行服务器,然后运行客户端1,在客户端1输入一些测试命令以后,我们运行客户端2。

因为开启两个客户端是在同一个eclipse中开启,所以测试有点不准确,不过也懒得去开多个编译器了,就是那么一回事。对于这个问题呢,我们可以开多几个eclipse来运行(工作空间必须是不同的)。

也可以直接在控制台(cmd)里进行编译“javac 主类名.java”,生成.class字节码文件以后,用“java 类名”的方式运行客户端,在此不作演示。

客户端(左边)的截图只记录了第二个客户端开启以后输入的信息。

Java套接字socket编程笔记的更多相关文章

  1. [置顶] Java套接字Socket编程

    1)概念 网络编程基本模型就客户端到服务器的模型,也就是我们常见的C/S模型.简单的说就是两个进程间相互通信的过程.即通信双方一方作为服务器等待客户端提出请求并给以回应,另一方作为客户端向服务器提出请 ...

  2. Java套接字Socket编程--TCP参数

    在Java的Socket中,主要包含了以下可设置的TCP参数. 属性 说明 默认值 SO_TIMEOUT 对ServerSocket来说表示等待连接的最长空等待时间; 对Socket来说表示读数据最长 ...

  3. Node.js开发入门—套接字(socket)编程

    Node.js的net模块提供了socket编程接口,方便我们利用较为底层的套接字接口来实现应用协议.这次我们看一个简单的回显服务器示例,包括服务端和客户端的代码. 代码 分服务器和客户端两部分来说吧 ...

  4. Java网络编程--套接字Socket

    一.套接字Socket IP地址标志Internet上的计算机,端口号标志正在计算机上运行的进程(程序). 端口号被规定为一个16位的0--65535之间的整数,其中,0--1023被预先定义的服务通 ...

  5. 套接字编程,创建套接字socket

    1.套接字地址结构: struct sockaddr { sa_family_t sa_family; char sa_data[14]; }; 其中,成员sa_family表示套接字的协议族类型,对 ...

  6. 网络编程 套接字socket TCP UDP

    网络编程与套接字 网络编程 网络编程是什么: ​ 网络通常指的是计算机中的互联网,是由多台计算机通过网线或其他媒介相互链接组成的 ​ 编写基于网络的应用程序的过程序称之为网络编程. 网络编程最主要的工 ...

  7. pythonl练习笔记——PythonNet 套接字socket

    1 套接字socket 1.1 套接字概述 套接字,一种网络通讯工具:用于进行网络间的通信,是一种特殊文件类型, 套接字,是一个通信链的句柄,用于描述IP地址和端口,实现向网络发出请求或应答网络请求. ...

  8. 网络编程(二)--TCP协议、基于tcp协议的套接字socket

    一.TCP协议(Transmission Control Protocol 传输控制协议) 1.可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会 ...

  9. 网络编程(二)——TCP协议、基于tcp协议的套接字socket

    TCP协议与基于tcp协议的套接字socket 一.TCP协议(流式协议) 1.可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的 ...

随机推荐

  1. BlockingQueue(阻塞队列)分析

    如果读者还有一点印象,我们在实现线程池时,用了队列这种数据结构来存储接收到的任务,在多线程环境中阻塞队列是一种非常有用的队列,在介绍BlockingQueue之前,我们先解释一下Queue接口. Qu ...

  2. flask渲染模板

    Flask自身使用了jinja2模板,可以使用render_template()方法来渲染模板,只需要将模板名和关键字的参数传入. 该渲染模板的模块(views.py)会在 templates 文件夹 ...

  3. Duilib教程-自动布局3-分隔条

    先看一个常用的图,如下: 左边是导航栏,右边是信息区. 中间可以自由拉伸. XML如下: <?xml version="1.0" encoding="utf-8&q ...

  4. 1、Android自己的下拉刷新SwipeRefreshLayout

    <android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/ ...

  5. mvn命令上传jar

    开发过程中涉及到下载第三SDK包,而本身项目是基于gradle的,所以为了项目中使用sdk包,需要将包加入到自己的仓库 1.利用nexus创建自己的第三方库thirdparty 类型hosted 2. ...

  6. python框架Scrapy中crawlSpider的使用

    一.创建Scrapy工程 #scrapy startproject 工程名 scrapy startproject demo3 二.进入工程目录,根据爬虫模板生成爬虫文件 #scrapy genspi ...

  7. Exchange 2016 系统要求

    Exchange 2016 和早期版本的 Exchange Server 共存方案 Exchange 2016支持混合部署方案 Exchange 2016 的网络和目录服务器要求 目录服务体系结构: ...

  8. C# 矩阵乘法实现

    矩阵乘法是一种高效的算法可以把一些一维递推优化到log( n ),还可以求路径方案等,所以更是是一种应用性极强的算法.矩阵,是线性代数中的基本概念之一.一个m×n的矩阵就是m×n个数排成m行n列的一个 ...

  9. Java 之继承和 final 关键字

    继承的概述 继承的特点 super 关键字 函数覆盖 子类的实例化过程 final 关键字 1. 继承的概述 继承是类与类之间的关系. 继承的好处: 提高了代码的复用性 让类与类之间产生了关系, 给第 ...

  10. DKLang Translation Editor

    https://yktoo.com/en/software/dklang-traned Features Translation using a dictionary (so-called Trans ...