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数据包的 ...
随机推荐
- android发送邮件
众所周知,在Android中调用其他程序进行相关处理,几乎都是使用的Intent,所以,Email也不例外. 在Android中,调用Email有三种类型的Intent: Intent.ACT ...
- c++11小计
[capture] (parameters) mutable -> return-type { statement } " (parameters)" 和 "-&g ...
- VMThread占CPU高基本上是JVM在频繁GC导致,原因基本上是冰法下短时间内创建了大量对象堆积造成频繁GC。
今天线上一个java进程cpu负载100%.按以下步骤查出原因. 1.执行top -c命令,找到cpu最高的进程的id 2.执行top -H -p pid,这个命令就能显示刚刚找到的进程的所有线程的资 ...
- ICE新手入门版
1.ICE是什么? 网络通信引擎ICE(Internet Communications Engine)是Zero C公司的分布式系统开发专家实现的一种新的高性能的面向对象中间件平台.从根本上说, I ...
- delphi连接sql server数据库,并根据sql语句查询出数据显示--初级
需要用到四个组件,分别为: 1.ADOConnection1 设置Connectionstring属性(连接串),loginPrompt属性控制是否连接记住了密码: 2.ADOQuery1 设置Con ...
- Mac下安装apk(命令形式)
1 连接上设备 2 卸载原有程序 3 进入adb程序目录/Applications/adt-bundle-mac-x86_64-20131030/sdk/platform-tools 4 键入命令 . ...
- “绝对”妹纸~position
CSS:布局之fixed,relative,absolute 记住abs是跟随 relative的,没有看到position:relative;之前他会一直向上查找. 直到执着的找到relative! ...
- window下搭建python开发环境
搭建一个python开发环境比较简单,所以就稍微记录一下. 1.下载python然后安装 2.配置环境变量 3.在eclipse添加PyDev插件 1.下载python 官网:https://www. ...
- 并发编程8 线程的创建&验证线程之间数据共享&守护线程&线程进程效率对比&锁(死锁/递归锁)
1.线程理论以及线程的两种创建方法 2.线程之间是数据共享的与join方法 3.多线程和多进程的效率对比 4.数据共享的补充线程开启太快 5.线程锁 互斥锁 同步锁 6.死锁现象和递归锁 7.守护线程 ...
- datagridview数据导出到excel
/// <summary> /// 导出Excel /// </summary> /// <param name="mydgv">控件 Data ...