使用Socket进行通信
客户端通常可使用Socket的构造器来连接到指定服务器,Socket通常可使用如下两个构造器。
Socket(lnetAddress/String remoteAddress , int port):创建连接到指定远程主机、远程端口的Socket,该构造器没有指定本地地址、本地端口,默认使用本地主机的默认IP地址,默认使用系统动态指定的IP地址。
Socket(lnetAddress/String remoteAddress , int port , lnetAddress localAddr , int localPort):创建连接到指定远程主机、远程端口的Socket,并指定本地IP地址和本地端口号,适用于本地主机有多个IP地址的情形。
上面两个构造器中指定远程主机时既可使用lnetAddress来指定,也可直接使用String对象来指定,但程序通常使用String对象(如192.168.2.23)来指定远程IP。当本地主机只有一个IP地址时,使用第一个方法更为简单。如以下代码所示:
//创建连接到本机、30000端口的Socket
Socket s = new Socket("172.18.5.198", 30000);
//下面就可以使用Socket进行通信了
......
当程序执行上面代码时,该代码将会连接到指定服务器,让服务器的ServerSocket的accept()方法向下执行,于是服务器端和客户端就产生一对互相连接的Socket。
当客户端、服务器端产生了对应的Socket之后,程序无须再去分服务端、客户端,而是通过各自的Socket进行通信。Socket提供了如下两个方法来获取输入流和输出流:
InputStream getInputStream():返回该Socket对象对应的输入流,让程序通过该输入流从Socket中取出数据。
OnputStream getOnputStream():返回该Socket对象对应的输出流,让程序通过该输出流向Socket中输出数据。
通过这两个方法返回的InputStream和OutputStream,可以得出:不管底层的IO流是怎样的节点流、文件流也好,网络Socket产生的流也好,程序都可以将其包装成处理流,从而提供更多方便的处理。
下面以一个最简单的网络通信程序为例来介绍基于TCP协议的网络通信:
下面的服务器程序需要在PC上运行,它仅仅建立ServerSocket监听,并使用Socket获取输出流输出。
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class SimpleServer {
public static void main(String[] args) throws IOException {
//创建一个ServerSocket,用于监听客户端Socket的连接请求
ServerSocket ss = new ServerSocket(30000);
//采用循环不断接受来自客户端的请求
while(true){
//每当接收到客户端Socket的请求,服务器端也对应产生一个Socket
Socket s = ss.accept();
OutputStream os = s.getOutputStream();
os.write("你好!你收到了服务器的新年祝福:\n".getBytes("utf-8"));
//关闭输出流,关闭Socket
os.close();
s.close();
}
}
}
上面的程序中建立了一个ServerSocket对象,该ServerSocket在30000端口监听,该ServerSocket将会一直监听,等待客户端程序的连接,程序接下来的代码用于打开Socket对应输出流,并向输出流中写入一段字符串数据。
上面的程序并未把OutputStream流包装成PrintStream,然后使用PrintStream直接输出整个字符串,这是因为该服务器端程序运行于Windows主机上,当直接使用PrintStream输出字符串时默认使用系统平台的字符串(即GBK)进行编码;但该程序的客户端是Android应用,运行于Linux平台(Android是Linux内核的),因此当客户端读取网络数据时默认使用UTF-8字符集进行解码,这样势必引起乱码,为了保证客户端能正常解析到数据,此处手动控制字符串的编码、强行指定使用UTF-8字符集进行编码,这样就可避免乱码问题了。
接下来的客户端也仅仅使用Socket建立与指定IP、指定端口的连接,并使用Socket获取输入流读取数据,该客户端程序是一个Android应用,因此还是需要先建立Android项目,该程序的界面中包含一个文本框,用于显示从服务器端读取的字符串数据。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.Socket;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.EditText;
public class SimpleClient extends Activity {
EditText show;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple_client);
show = (EditText) findViewById(R.id.show);
//关闭输入流、socket
try {
Socket socket = new Socket("172.18.5.88", 30000);
//将Socket对应的输入流包装成BufferReader
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
//进行普通IO操作
String line = br.readLine();
show.setText("来自服务器的数据"+line);
br.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面的程序中使用ServerSocket和Socket建立网络连接后,接下来通过Socket获取输入流、输出流进行通信。不难看出,一旦使用ServerSocket、Socket建立网络连接,程序通过网络通信与普通IO并没有太大的区别。
该Android应用需要访问互联网,因此还需要为该应用赋予访问互联网的权限,也就是在AndroidManifest.xml文件中增加如下配置片段:
<!-- 授权访问互联网 -->
<uses-permission android:name="android.permission.INTERNET"/>
先运行上面程序中的SimpleServer类,将看到服务器一直处于等待状态,因为服务器使用了死循环来接收来自客户端的请求;再运行客户端AndroidClient类,将看到程序输出:“来自服务器的数据:你好,你收到了服务器的新年祝福!”这表明客户端和服务端通信成功。
实际应用中,程序可能不想让执行网络连接、读取服务器数据的进程一直阻塞,而是希望当网络连接、读取操作超过合理时间之后,系统自动认为该操作失败,这个合理时间就是超时时间。Socket对象提供了一个setSoTimeout(int timeout)来设置超时时长,如下面的代码:
Socket s = new Socket("127.0.0.1" , 30000);
//设置10秒之后即认为超时
s.setSoTimeout(10000);
为Socket对象指定了超时时长之后,如果使用Socket进行读、写操作完成之前已经超出了该时间限制,那么这些方法就会抛出SocketTimeoutException异常,程序可以对该异常进行捕捉,并进行适当处理。如以下代码:
try {
//使用Scanner来读取网络输入流中的数据
Scanner scan = new Scanner(s.getInputStream());
//读取一行字符
String line = scan.nextLine();
}
//捕捉SocketTimeoutException异常
catch (SocketTimeoutException e) {
// 对异常进行处理
......
}
假设程序需要为Socket连接服务器时指定超时时长:即经过指定时间后,如果该Socket还未连接到远程服务器,则系统认为该Socket连接超时。但Socket的所有构造器里都没有提供指定超时时长的参数,所以程序应该先创建一个无连接的Socket,再调用Socket的connect()方法来连接远程服务器,而connect方法就可以接受一个超时时长参数,如以下代码:
//创建一个无连接的Socket
Socket s = new Socket();
//让该Socket连接到远程服务器,如果经过10秒还没有连接到,则认为连接超时
s.connect(new InetAddress(host,port), 1000);
使用Socket进行通信的更多相关文章
- java socket报文通信(一)socket的建立
java socket报文通信(一) socket的建立 今天来和大家分享一下java中如何使用socket进行通信.先来啰嗦两句,看看Tcp/ip和udp: TCP是Transfer Contro ...
- java socket线程通信
关于socket线程通信的一些知识整理 一般我们需要要让两台机子进行通信,需要创建一个Server 类,一个Client类,还需要创建一个线程类 server public class Server ...
- Dubbo底层采用Socket进行通信详解
由于Dubbo底层采用Socket进行通信,自己对通信理理论也不是很清楚,所以顺便把通信的知识也学习一下. n 通信理论 计算机与外界的信息交换称为通信.基本的通信方法有并行通信和串行通信两种. 1 ...
- 《java入门第一季》之Socket编程通信和TCP协议通信图解
Socket编程通信图解原理: TCP协议通信图解
- socket 编程通信实例
socket 编程通信实例:TCPserver: , ServerThread, ; WSADATA wsaData; ,), ; } ; } } ; g ...
- 【Android实战】Socket消息通信
这篇博客的内容是基于http://my.oschina.net/fengcunhan/blog/178155进行改造的.所以须要先看完这篇博客,然后再来看以下的内容. 1.须要完毕的功能是直播间的so ...
- Android native进程间通信实例-socket本地通信篇之——基本通信功能
导读: 网上看了很多篇有关socket本地通信的示例,很多都是调通服务端和客户端通信功能后就没有下文了,不太实用,真正开发中遇到的问题以及程序稳定性部分没有涉及,代码健壮性不够,本系列(socket本 ...
- Python网络编程03 /缓存区、基于TCP的socket循环通信、执行远程命令、socketserver通信
Python网络编程03 /缓存区.基于TCP的socket循环通信.执行远程命令.socketserver通信 目录 Python网络编程03 /缓存区.基于TCP的socket循环通信.执行远程命 ...
- 基于.NET Socket API 通信的综合应用
闲谈一下,最近和客户进行对接Scoket 本地的程序作为请求方以及接受方,对接Scoket 的难度实在比较大,因为涉及到响应方返回的报文的不一致性,对于返回的报文的格式我需要做反序列化的难度增大了不少 ...
随机推荐
- c 字符串常用函数
#include <iostream> #include <stdio.h> #include <vector> #include "string.h&q ...
- Div里面载入另一个页面的实现(取代框架)(AJax)(转)
随着框架越来越不火了,HTML5就不对框架支持了,iframe也只有url了,Div就担当了此大任 DIV+CSS在页面部局确实也很让人满意,使用也更方便 今天突然遇到一个问题,那就是需要导入另一个页 ...
- Ajax异步调用使用
//验证通知号重复 function checkinformcodeagage() { var informcode = $("#txtinformcode").val(); if ...
- Linux配置全局环境变量的方法
总结3种方法: 1.修改/etc/profile 1.1 :首先在此文件中设置环境变量; 1.2:export 设置好的环境变量. #view /etc/profile export ...
- 转:C++语言的15个晦涩特性
转自 http://blog.jobbole.com/54140/ 操作符重载和检查顺序 重载,(逗号),||或者&&操作符会引起混乱,因为它打破了正常的检查规则.通常情况下,逗号操作 ...
- mysql使用笔记(四)
一.选择合适的数据类型 1. CHAR vs VCHAR char是固定长度的字符类型,而varchar是可变长度的字符类型.char(M)的数据列中,每个值都占用M个字节,如果某个长度小于M ...
- about opencl
Platform:LG G3,Adreno 330 1. 8M(3264x2448) memmap方式读入时间24ms,读出时间12ms,时间与内存大小基本成线性关系.使用memmap 与 memco ...
- python 键值对的树实现
#coding:utf-8 __author__ = 'similarface' class KeyedBinaryTree: def __init__(self):self.tree=EmptyNo ...
- 教你开启红米的USB大容量存储选项,全网首发哦
教你开启红米的USB大容量存储选项,全网首发哦 http://bbs.7to.cn/thread-10732-1-1.html 发表于 2014-4-29 110643 红米note入手也有两天了.各 ...
- linux笔记:linux常用命令-目录和文件处理命令
命令格式: 命令 [-选项] [参数] 例: ls -la /etc 注意:个别命令的使用不遵循此格式. 目录处理命令:ls(列目录) 更多选项: -h 以kb等利于人阅读的方式取代字节显示文件大小 ...