Java套接字socket编程笔记
相对于C和C++来说,Java中的socket编程是比较简单的,比较多的细节都已经被封装好了,每次创建socket连接只需要知道地址和端口即可。
在了解socket编程之前,我们先来了解一下读写数据的数据流类中一些需要注意的东西。
BufferedReader与DataInputStream的区别:
通常我们常用到的字节输入输出流有BufferedReader与PrintWriter,DataInputStream和DataOutputStream这两对。这些类都属于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编程笔记的更多相关文章
- [置顶] Java套接字Socket编程
1)概念 网络编程基本模型就客户端到服务器的模型,也就是我们常见的C/S模型.简单的说就是两个进程间相互通信的过程.即通信双方一方作为服务器等待客户端提出请求并给以回应,另一方作为客户端向服务器提出请 ...
- Java套接字Socket编程--TCP参数
在Java的Socket中,主要包含了以下可设置的TCP参数. 属性 说明 默认值 SO_TIMEOUT 对ServerSocket来说表示等待连接的最长空等待时间; 对Socket来说表示读数据最长 ...
- Node.js开发入门—套接字(socket)编程
Node.js的net模块提供了socket编程接口,方便我们利用较为底层的套接字接口来实现应用协议.这次我们看一个简单的回显服务器示例,包括服务端和客户端的代码. 代码 分服务器和客户端两部分来说吧 ...
- Java网络编程--套接字Socket
一.套接字Socket IP地址标志Internet上的计算机,端口号标志正在计算机上运行的进程(程序). 端口号被规定为一个16位的0--65535之间的整数,其中,0--1023被预先定义的服务通 ...
- 套接字编程,创建套接字socket
1.套接字地址结构: struct sockaddr { sa_family_t sa_family; char sa_data[14]; }; 其中,成员sa_family表示套接字的协议族类型,对 ...
- 网络编程 套接字socket TCP UDP
网络编程与套接字 网络编程 网络编程是什么: 网络通常指的是计算机中的互联网,是由多台计算机通过网线或其他媒介相互链接组成的 编写基于网络的应用程序的过程序称之为网络编程. 网络编程最主要的工 ...
- pythonl练习笔记——PythonNet 套接字socket
1 套接字socket 1.1 套接字概述 套接字,一种网络通讯工具:用于进行网络间的通信,是一种特殊文件类型, 套接字,是一个通信链的句柄,用于描述IP地址和端口,实现向网络发出请求或应答网络请求. ...
- 网络编程(二)--TCP协议、基于tcp协议的套接字socket
一.TCP协议(Transmission Control Protocol 传输控制协议) 1.可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会 ...
- 网络编程(二)——TCP协议、基于tcp协议的套接字socket
TCP协议与基于tcp协议的套接字socket 一.TCP协议(流式协议) 1.可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的 ...
随机推荐
- PAXOS: libevent_paxos
PAXOS实现 -- libevent_paxos 该文章是项目的一部分.主要讲PAXOS算法的实现. ...
- Bouncy Castle Crypto API c# port
Bouncy Castle 是一种用于 Java 平台的开放源码的轻量级密码术包.它支持大量的密码术算法,并提供 JCE 1.2.1 的实现.现在有了C#的版本.下面是网站上的介绍 This port ...
- script跨域之360搜索
思考: 布局: 1,flex元素上下左右居中,内部元素横向排列: div{ /* 100vh = viewport height*/ display: flex; justify-content: c ...
- 《从零开始学Swift》学习笔记(Day 61)——Core Foundation框架之内存管理
原创文章,欢迎转载.转载请注明:关东升的博客 在Swift原生数据类型.Foundation框架数据类型和Core Foundation框架数据类型之间转换过程中,虽然是大部分是可以零开销桥接,零开销 ...
- 170208、用Navicat自动备份mysql数据库
数据库备份很重要,很多服务器经常遭到黑客的恶意攻击,造成数据丢失,如果没有及时备份的话,后果不堪设想. 一:备份的目的: 做灾难恢复:对损坏的数据进行恢复和还原 需求改变:因需求改变而需要把数据还原到 ...
- less-!important关键字
//!important关键字 使用!important关键字混入调用之后,以标记它继承的所有属性!important,example: .test{ background:red; font-siz ...
- [刷题]ACM ICPC 2016北京赛站网络赛 D - Pick Your Players
Description You are the manager of a small soccer team. After seeing the shameless behavior of your ...
- POJ 3020 Antenna Placement【二分匹配——最小路径覆盖】
链接: http://poj.org/problem?id=3020 http://acm.hust.edu.cn/vjudge/contest/view.action?cid=22010#probl ...
- 高性能网站服务器的架设优化-Nginx优化
一:对于高性能网站 ,请求量大,如何支撑?思路 在网站架构设计中,大家一定对 LNMP (Linux Nginx Mysql Php) 不陌生.LNMP 确实是一个非常优秀的架构,秉承着自由,开放,高 ...
- delphi中String 和 动态静态数组
默认string类型为ansiString:有编译开关控制 shortString: strShort : shortString; strShort 大小256字节,可根据sizeof()计算出,s ...