参考资料:

http://haohaoxuexi.iteye.com/blog/1979837
http://zhidao.baidu.com/link?url=OeOSa0YbOzSbMVPa8sgPXcwtyyHsWB1lPkh1XopETtNK_lVtbd9lL7NH3qlFxjC-4kNUmCkIXgcfRW7KJq9_FK
http://www.cnblogs.com/rond/p/3565113.html
http://www.cnblogs.com/mengdd/archive/2013/03/10/2952616.html

TCP/IP客户套接字

Java中有两种类型的TCP套接字,一种用于服务器,一种用于客户端。
ServerSocket类型设计成“监听器”,等待客户端连接的到来。因此,ServerSocket用于服务器。
Socket类用于客户端,它被设计成连接服务器套接字并且初始化协议的交换。

图片来自:http://www.cnblogs.com/rond/p/3565113.html

Socket

Socket可以使客户程序与服务器程序通信,使用Socket连接服务器的过程包含以下4个基本的步骤

(1)创建Socket
(2)打开连接到Socket的输入/输出流
(3)按照一定协议对Socket执行读写操作
(4)关闭Socket

 
构造函数

Socket()

Socket(InetAddress address, int port)throws UnknownHostException, IOException
Socket(InetAddress address, int port, InetAddress localAddress, int localPort)throws IOException
Socket(String host, int port)throws UnknownHostException, IOException
Socket(String host, int port, InetAddress localAddress, int localPort)throws IOException
 
除去第一种不带参数的之外,其它构造函数会尝试建立与服务器的连接。如果失败会抛出IOException错误。如果成功,则返回Socket对象。
InetAddress是一个用于记录主机的类,其静态getHostByName(String msg)可以返回一个实例,其静态方法getLocalHost()也可以获得当前主机的IP地址,并返回一个实例。Socket(String host, int port, InetAddress localAddress, int localPort)构造函数的参数分别为目标IP、目标端口、绑定本地IP、绑定本地端口。
 
Socket方法
getInetAddress();      远程服务端的IP地址
getPort();          远程服务端的端口
getLocalAddress()      本地客户端的IP地址
getLocalPort()        本地客户端的端口
getInputStream();     返回与调用的套接字相关联的输入流
getOutStream();      返回与调用的套接字相关联的输出流
值得注意的是,在这些方法里面,最重要的就是getInputStream()和getOutputStream()了。
 
Socket状态
isClosed();            //连接是否已关闭,若关闭,返回true;否则返回false
isConnect();      //如果曾经连接过,返回true;否则返回false
isBound();            //如果Socket已经与本地一个端口绑定,返回true;否则返回false
如果要确认Socket的状态是否处于连接中,下面语句是很好的判断方式。
boolean isConnection=socket.isConnected() && !socket.isClosed();   //判断当前是否处于连接

半关闭Socket

很多时候,我们并不知道在获得的输入流里面到底读多长才结束。下面是一些比较普遍的方法:
  • 自定义标识符(譬如下面的例子,当受到“bye”字符串的时候,关闭Socket)
  • 告知读取长度(有些自定义协议的,固定前几个字节表示读取的长度的)
  • 读完所有数据
  • 当Socket调用close的时候关闭的时候,关闭其输入输出流

例:

public class TestPort {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
String hostname = "www.baidu.com";
InetAddress addr = InetAddress.getByName(hostname);
Socket so = new Socket(addr,80);
System.out.println("远程连接地址:"+so.getInetAddress());
System.out.println("远程连接端口:"+so.getPort());
System.out.println("本地连接地址:"+so.getLocalAddress());
System.out.println("本地连接端口:"+so.getLocalPort());
InputStream is = so.getInputStream();
/*
* 这部分为客户端操作,需要一台远程服务器
int c;
while((c=is.read())!=-1){
System.out.println("gas");
}*/
is.close();
so.close();
}
}

运行结果:

远程连接地址:www.baidu.com/119.75.218.70
远程连接端口:80
本地连接地址:/192.168.1.160
本地连接端口:4338
 ServerSocket
在客户端/服务器端的通信模式中,服务器端用来监听特定端口上客户端的连接。并且可以发送信息,通过ServerSocket类实现,而客户端通过上面的Socket类实现。
 
构造函数
ServerSocket()throws IOException
ServerSocket(int port)throws IOException
ServerSocket(int port, int backlog)throws IOException
ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOException
 
注意点:
1. port服务端要监听的端口;backlog客户端连接请求的队列长度;bindAddr服务端绑定IP
2. 如果端口被占用或者没有权限使用某些端口会抛出BindException错误。譬如1~1023的端口需要管理员才拥有权限绑定。
3. 如果设置端口为0,则系统会自动为其分配一个端口;
4. bindAddr用于绑定服务器IP,为什么会有这样的设置呢,譬如有些机器有多个网卡。
5. ServerSocket一旦绑定了监听端口,就无法更改。ServerSocket()可以实现在绑定端口前设置其他的参数。
 
ServerSocket类可以通过getInetAddress方法返回此服务器套接字的本地地址,该方法要绑定一个本地的InetAddress,可以在构造方法 
ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOException 中通过参数bindAddr传递。
getLocalPort方法用来返回此套接字在其上侦听的接口。
 
:返回服务器端信息
public class GetServerIP {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
InetAddress addr =InetAddress.getByName("localhost");
ServerSocket se = new ServerSocket(10000,10,addr);
System.out.println(se.getInetAddress());
System.out.println(se.getLocalPort());
}
}

运行结果

localhost/127.0.0.1
10000

附:完整例子

发送端 socket

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket; public class SocketSender {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket("127.0.0.1", 27401);
// 向服务端发送信息
OutputStream outer = socket.getOutputStream();
byte[] b = "客户端:向服务端发送文字,\"这是一行测试....\"".getBytes();
outer.write(b);
outer.flush();
System.out.println("发送完毕!"); // 接收服务端的返回值
InputStream inner = socket.getInputStream();
int count = 0;
while (count == 0) {
count = inner.available();
}
byte[] recv = new byte[count];
inner.read(recv); String str_recv = new String(recv);
System.out.println("客户端:接收到服务端的文字:" + str_recv);
} catch (IOException e) {
System.out.println("发送端出现异常");
} finally {
if (socket != null)
try {
socket.close();
} catch (IOException e) {
System.out.println("发送端 finally 出现异常");
}
}
}
}

接收端:ServerSocket

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket; public class SocketReceiver {
public static void main(String[] args) {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(27401);
while (true) {
Socket socket = serverSocket.accept(); // 接收客户端的信息
InputStream in = socket.getInputStream();
int count = 0;
while (count == 0) {
count = in.available();
}
byte[] b = new byte[count];
in.read(b);
String str = new String(b);
System.out.println(str); // 向客户端发送确认消息
OutputStream outer = socket.getOutputStream();
byte[] b_out = "已经收到,返回消息码200".getBytes();
outer.write(b_out);
outer.flush(); // 关闭socket
socket.close();
}
} catch (IOException e) {
System.out.println("接收端出现异常");
} finally {
if (serverSocket != null)
try {
serverSocket.close();
} catch (IOException e) {
System.out.println("接收端 finally 出现异常");
}
}
}
}

接收端:方法2,利用多线程,每一个发送端对应接收端的一个线程

public class SocketReceiverWithThread {
public static void main(String[] args) {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(27401);
while (true) {
Socket socket = serverSocket.accept();
new Thread(new Handler(socket)).start();
//socket.close(); 注意 关闭socket不能在这里,而应该写在线程内,否则可能线程没结束就关闭了socket
} } catch (IOException e) {
System.out.println("接收端出现异常");
} finally {
if (serverSocket != null)
try {
serverSocket.close();
} catch (IOException e) {
System.out.println("接收端 finally 出现异常");
}
}
}
} class Handler implements Runnable {
private Socket socket;
public Handler(Socket socket) {
this.socket = socket;
}
public void run() {
try {
// 接收客户端的信息
InputStream in = socket.getInputStream();
int count = 0;
while (count == 0) {
count = in.available();
}
byte[] b = new byte[count];
in.read(b);
String str = new String(b);
System.out.println(str); // 向客户端发送确认消息
OutputStream outer = socket.getOutputStream();
byte[] b_out = "已经收到,返回消息码200".getBytes();
outer.write(b_out);
outer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭socket
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

附2:使用socket进行文件传送

客户端 ClienPart.java

package com.client;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.Socket; public class ClientPart {
private Socket socket=null;
private String ip ;// 设置成服务器IP
private int port ; public ClientPart(String ip,int port) {
this.ip=ip;
this.port=port; }
private boolean createConnection() {
try {
socket = new Socket(ip, port);
System.out.print("连接服务器成功!" + "\n");
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
//向服务器发送消息
private void sentMessage(String msg){
if (socket == null)
return;
Writer writer;
try {
writer = new OutputStreamWriter(socket.getOutputStream());
writer.write(msg);
writer.flush(); //写完后要记得flush
writer.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void getFile() {
if (socket == null)
return;
DataInputStream inputStream = null;
try {
inputStream = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
} catch (Exception e) {
System.out.print("接收消息缓存错误\n");
return;
}
try {
//本地保存路径,文件名会自动从服务器端继承而来。
String savePath = "F:\\client\\";
int bufferSize = 8192;
byte[] buf = new byte[bufferSize];
long len=0; savePath += inputStream.readUTF(); //读取文件名
DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new BufferedOutputStream(new FileOutputStream(savePath))));
len = inputStream.readLong(); //读取文件长度 System.out.println("文件的长度为:" + len);
System.out.println("开始接收文件!"); while (true) {
int read = 0;
if (inputStream != null) {
read = inputStream.read(buf);
}
if (read == -1) {
break;
}
//客户端读出文件
fileOut.write(buf, 0, read);
} System.out.println("接收完成,文件存为" + savePath + "\n");
fileOut.close();
socket.close();
} catch (Exception e) {
System.out.println("接收消息错误" + "\n");
return;
}
} private void putFile(String filename){
String filePath = filename;
File client_file = new File(filePath); //要传输的文件路径
long filelen = client_file.length();
System.out.println("要上传的文件长度:" + (int)filelen);
try{
DataInputStream in_stream = new DataInputStream(new BufferedInputStream(new FileInputStream(filePath)));
DataOutputStream out_stream = new DataOutputStream(socket.getOutputStream());
out_stream.writeUTF(client_file.getName());
out_stream.flush();
out_stream.writeLong((long) client_file.length());
out_stream.flush(); int bufferSize = 8192;
byte[] buf = new byte[bufferSize]; while (true) {
int read = 0;
if (in_stream != null) {
read = in_stream.read(buf);
}
if (read == -1) {
break;
}
out_stream.write(buf, 0, read);
}
out_stream.flush();
// 注意关闭socket链接,不然客户端会等待server的数据过来,
// 直到socket超时,会导致数据不完整。
in_stream.close();
socket.close();
System.out.println("文件传输完成");
}catch(Exception e){
e.printStackTrace();
} } public static void main(String arg[]) {
ClientPart client = new ClientPart("127.0.0.1",7005);
client.createConnection();
client.sentMessage("read");//向服务器发送信息,说明需要读取文件
client.createConnection();
client.getFile(); //获取服务器发送的文件 client.createConnection();
client.sentMessage("write");//向服务器发送信息,说明需要上传文件
client.createConnection();
client.putFile("F:\\client\\1.txt");//向服务器发送文件 }
}

服务端 ServerPart.java

package com.server;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.ServerSocket;
import java.net.Socket; public class ServerPart {
int port = 7005;
public void serverStart() {
try {
ServerSocket ssocket = new ServerSocket(port);
System.out.println("服务器准备完毕!");
while (true) {
String msg = checkIO(ssocket); //获取来自客户端的请求是读/写
if(msg.compareTo("read")==0){
sendFile(ssocket); //向客户端发送文件
}
else if(msg.compareTo("write")==0){
saveFile(ssocket); //存储来自客户端的文件
}
}
} catch (Exception e) {
e.printStackTrace();
}
} //服务器检查来自客户端的请求是读/写
public String checkIO(ServerSocket ssocket) throws IOException{
Socket socket=ssocket.accept();
Reader reader = new InputStreamReader(socket.getInputStream());
char chars[] = new char[10];
int len;
StringBuilder sb = new StringBuilder();
while ((len=reader.read(chars)) != -1) {
sb.append(new String(chars, 0, len));
}
reader.close();
socket.close();
return sb.toString();
} public void sendFile(ServerSocket ssocket) throws IOException{
Socket soc = null;
// 选择进行传输的文件
String filePath = "F:\\server\\info.bin";
File myfile = new File(filePath);
System.out.println("服务器文件长度:" + (int) myfile.length());
soc = ssocket.accept();
System.out.println("建立socket链接"); DataInputStream in_stream = new DataInputStream(new BufferedInputStream(new FileInputStream(filePath)));
DataOutputStream out_stream = new DataOutputStream(soc.getOutputStream());
//将文件名及长度传给客户端。这里要真正适用所有平台,例如中文名的处理,还需要加工,具体可以参见Think In Java 4th里有现成的代码。
out_stream.writeUTF(myfile.getName());
out_stream.flush();
out_stream.writeLong((long) myfile.length());
out_stream.flush(); int bufferSize = 8192;
byte[] buf = new byte[bufferSize]; while (true) {
int read = 0;
if (in_stream != null) {
read = in_stream.read(buf);
}
if (read == -1) {
break;
}
out_stream.write(buf, 0, read);
}
out_stream.flush();
// 注意关闭socket链接哦,不然客户端会等待server的数据过来,
// 直到socket超时,导致数据不完整。
out_stream.close();
in_stream.close();
soc.close();
System.out.println("服务器文件传输完成");
} //服务器保存文件的方法
public void saveFile(ServerSocket ssocket) throws IOException{
Socket soc = ssocket.accept();
if (soc == null)
return;
DataInputStream inputStream = null;
try {
inputStream = new DataInputStream(new BufferedInputStream(soc.getInputStream()));
} catch (Exception e) {
System.out.print("服务器接收消息缓存错误\n");
return;
} try {
//服务器文件的保存路径
String savePath = "f:\\server\\";
int bufferSize = 8192;
byte[] buf = new byte[bufferSize];
long len=0; savePath += inputStream.readUTF(); //读取文件名
DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new BufferedOutputStream(new FileOutputStream(savePath))));
len = inputStream.readLong(); //读取文件长度
//控制台输出
System.out.println("文件的长度为:" + len);
System.out.println("开始接收文件!"); while (true) {
int read = 0;
if (inputStream != null) {
read = inputStream.read(buf);
}
if (read == -1) {
break;
}
//客户端读出文件
fileOut.write(buf, 0, read);
}
System.out.println("接收完成,文件存为" + savePath + "\n");
fileOut.close();
soc.close();
} catch (Exception e) {
System.out.println("接收消息错误" + "\n");
e.printStackTrace();
return;
}
} public static void main(String arg[]) {
//启动服务器
new ServerPart().serverStart();
}
}
 

Java学习笔记--Socket和ServerSocket的更多相关文章

  1. Java学习笔记——Socket实现文件传输

    我越是逃离,却越是靠近你. 我越是背过脸,却越是看见你. 我从你开始, 我在你结束. 需求:实现局域网下socket传输文件. 客户端步骤: 1.建立与服务器的连接 2.创建client输出流 3.创 ...

  2. Java学习笔记4

    Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...

  3. 20155234 2016-2017-2第十周《Java学习笔记》学习总结

    20155234第十周<Java学习笔记>学习总结 教材学习内容总结 网络编程 在两个或两个以上的设备(例如计算机)之间传输数据.程序员所作的事情就是把数据发送到指定的位置,或者接收到指定 ...

  4. 20145330第十周《Java学习笔记》

    20145330第十周<Java学习笔记> 网络编程 网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据.程序员所作的事情就是把数据发送到指定的位置,或者接收到指定的数据,这个就 ...

  5. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  6. 0035 Java学习笔记-注解

    什么是注解 注解可以看作类的第6大要素(成员变量.构造器.方法.代码块.内部类) 注解有点像修饰符,可以修饰一些程序要素:类.接口.变量.方法.局部变量等等 注解要和对应的配套工具(APT:Annot ...

  7. Java学习笔记(04)

    Java学习笔记(04) 如有不对或不足的地方,请给出建议,谢谢! 一.对象 面向对象的核心:找合适的对象做合适的事情 面向对象的编程思想:尽可能的用计算机语言来描述现实生活中的事物 面向对象:侧重于 ...

  8. 0032 Java学习笔记-类加载机制-初步

    JVM虚拟机 Java虚拟机有自己完善的硬件架构(处理器.堆栈.寄存器等)和指令系统 Java虚拟机是一种能运行Java bytecode的虚拟机 JVM并非专属于Java语言,只要生成的编译文件能匹 ...

  9. 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用

    垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...

随机推荐

  1. 使用SQLiteDatabase进行数据库操作的步骤

    1.获取SQLiteDatabase对象,它代表了与数据库的连接.2.调用SQLiteDatabase的方法来执行SQL语句.3.操作SQL语句的执行结果,比如用SimpleCursorAdapter ...

  2. 如何打一手好Log(转)

    如果项目上过线的话,那你一定知道Log是多么重要. 为什么说Log重要呢?因为上线项目不允许你调试,你只能通过Log来分析问题.这时打一手好Log的重要性绝不亚于写一手好代码.项目出问题时,你要能拿出 ...

  3. C++Memset误区

    Memset的原型是void *memset(void *s, char ch, size_t n); Memset是按字节赋值的,对char以外的类型赋0(00000000) -1(11111111 ...

  4. sicily 1035. DNA matching

    题意:判断基因链是否匹配,匹配的双链数加1,并要标记,下次比较不能重用! 解法: 打擂台法 #include<iostream> #include<string> #inclu ...

  5. Codeforces 460 DE 两道题

    D Little Victor and Set 题目链接 构造的好题.表示是看了题解才会做的. 假如[l,r]长度不超过4,直接暴力就行了. 假如[l,r]长度大于等于5,那么如果k = 1,显然答案 ...

  6. js中的函数,Date对象,Math对象和数组对象

    函数就是完成某个功能的一组语句,js中的函数由关键字 function + 函数名 + 一组参数定义;函数在定义后可以被重复调用,通常将常用的功能写成一个函数,利用函数可以使代码的组织结构更多清晰. ...

  7. c++应用程序文件的编译过程

    这里讲下C++文件的编译过程及其中模板的编译过程: 一:一般的C++应用程序的编译过程.     一般说来,C++应用程序的编译过程分为三个阶段.模板也是一样的. 在cpp文件中展开include文件 ...

  8. tomcat与resin的比较

    Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache.Sun 和其他一些公司及个人共同开发而成.由于有了 ...

  9. Swift中实现点击、双击、捏、旋转、拖动、划动、长按手势的类和方法介绍

    1.UITapGestureRecognizer 点击/双击手势 代码如下: var tapGesture = UITapGestureRecognizer(target: self, action: ...

  10. JavaScripts学习日记——DOM

    DOM Document Object Model 文档对象模型  整合js和html css.控制html文档行为.DOM就是把页面当中所有内容全部封装成对象.HTML文档中万物皆对象.1.对象的分 ...