基于Socket和Http的网络编程
一、基于Socket套接字的网络编程
网络应用程序的开发有很多的架构和模式,常见的有C/S、B/S等,而Socket主要就是用于进行传统的C/S模式应用的开发。C/S模式中主要需要开发两个端,服务端和客户端,工作原理如下:
如上图所示:
1、首先服务器端启动,监听指定的连接端口等待客户端的连接数据请求;
2、客户端请求成功,连接到服务器的指定端口等待交互;
3、服务器端接收到来自客户端的连接请求,建立连接等待交互;
4、服务端和客户端同时打开一个输入流和输出流,客户端的输入流对应着服务端返回的输出流,相反客户端的输出流对应着服务端的输入流;
5、客户端和服务端通过输入输出流进行双向的消息通信;
6、通信结束后,客户端和服务端各自关闭本次连接;
上面是基本的原理,是很简单吧!实际内部的通信机制是比较复杂的,这里只针对如何使用Socket进行网络编程,对于内部的通信实现不进行介绍,有兴趣的读者可以参考相关资料。
实例如下:
我们知道Android应用程序使用Java语言编写开发的,因此在Android平台下开发C/S模式程序较简单,直接使用Java中的Socket和ServerSocket类即可。这种方式与传统的网络套接字开发几乎没有区别,我想熟悉Java的游者应该很容易接受吧。
服务端:
首先我们需要新建一个Java项目作为服务器端,代码如下:
/*
* 服务器端代码实现
* */
public class SocketServer {
private static ServerSocket mServer;// 服务端口类引用
private static boolean flag = true;
public static void main(String[] args) {
try {
// 创建服务端及端口
mServer = new ServerSocket(8888); System.out.println("正在监听8888端口....");
while(flag) {// 创建Socket对象 监听服务端端口的访问Socket socket = mServer.accept();
DataInputStream dis=new DataInputStream(socket.getInputStream());// 双向流创建
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
System.out.println("客户端信息:"+ dis.readUTF());// 读取客户端请求
dos.writeUTF("成功连接到服务器!");
dis.close();// 关闭输入输出流及端口
dos.close();
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端启动效果如下:
客户端:
新建一个Android项目作为客户端请求服务器,代码如下:
布局文件main.xml如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:background="#898989"
tools:context=".SocketClient" >
<EditText
android:id="@+id/connect_input"
android:layout_width="200dp"
android:layout_height="20dp"
android:layout_centerHorizontal="true"
android:background="#ffffff"/>
<Button
android:id="@+id/connect_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="connect to the server"
android:layout_alignLeft="@id/connect_input"
android:layout_below="@id/connect_input"/>
<TextView
android:id="@+id/connect_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:layout_below="@id/connect_btn"
android:text="test"
android:layout_alignLeft="@id/connect_btn"/>
</RelativeLayout>
/*
* 客户端由手机充当
* */
public class SocketClient extends Activity {
EditText connectEdt;
Button connectBtn;
TextView connectText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initView();
}
private void initView() {
connectEdt = (EditText) findViewById(R.id.connect_input);
connectText = (TextView) findViewById(R.id.connect_value);
connectBtn = (Button) findViewById(R.id.connect_btn);
connectBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
connectToServer();// 连接到服务器方法
}
});
}
private void connectToServer() {
String serverIP = "192.168.1.6";// 本机的IP设定
try {
Socket socket = new Socket(serverIP,8888);// 创建Socket套接字 并发出连接请求
DataInputStream dis = new DataInputStream(socket.getInputStream());// 双向流创建
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
String strInput = connectEdt.getText().toString();// 获取发送到服务端的信息
dos.writeUTF(strInput);// 写信息到输出流
connectText.setText(dis.readUTF()); // 从输入流中获得信息
dis.close();
dos.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.socket_client, menu);
return true;
}
}
注意:
在创建Socket套接字的时候需要指明服务端的IP和端口号,这里的IP为我们计算机的真实IP而不是使用环回地址“127.0.0.1”,因此运行本程序需要根据自己的实际IP来修改服务器端的IP;另外,不要忘了在我们的AndroidManifest.xml中配置好网络访问权限。
客户端发送内容到服务器效果如下:
服务器端显示效果如下:
至此,我们就成功的完成了基于Socket的网络编程了,很简单吧!
二、基于Http协议的网络编程
我们知道Http协议(超文本传输协议)是基于TCP/IP协议的协议,是WEB浏览器和WEB服务器端的应用层协议,也是通用的,无状态的,面向对象的通信协议。我们还知道,WWW应用系统是以internet作为传输媒介的,而且这个系统上的传输单位是网页,同时它是基于B/S模式的,有WEB浏览器和WEB服务器构成的,他们之间使用Http超文本协议进行相互通信的。
一次HTTP操作称为一个事务,其工作过程可分为四步:
1)首先客户机与服务器需要建立连接。
2)建立连接后,客户机以Socket形式发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。
3)服务器接到请求后,进行相应的事物处理,然后给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。
4)客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接。
如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,在显示屏输出。对于用户来说,这些过程是由HTTP自己完成的,HTTP协议永远都是客户端发起请求,服务器回送响应。
实例如下:
URLConnection的使用:
下面我们就是用基于Http协议相关的简单应用,即通过URLConnectiton来获得谷歌或百度页面的内容。
代码如下:
/*
* 使用HttpURLConnection获取网络资源
* */
public class HttpURLAct extends Activity {
ScrollView scrollView = null;
TextView textView = null;
URLConnection ucon = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
scrollView = new ScrollView(this);
textView = new TextView(this);
BufferedInputStream bis = null;
ByteArrayBuffer bab = null;
String url_str = "http://www.google.cn/";// 资源标识符
try {
URL url = new URL(url_str);// 初始化URL
ucon = url.openConnection();// 建立与服务器的连接
bis = new BufferedInputStream(ucon.getInputStream());// 通过连接得到输入流
int c = 0;
bab = new ByteArrayBuffer(1000);
while((c=bis.read())!= -1) {
bab.append((char)c);// 将接收到的信息放入到字节缓冲数组中
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(bis != null) {
try {
bis.close();
bis = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
String result = EncodingUtils.getString(bab.toByteArray(), "UTF-8");
textView.setText(result);// 显示得到的信息
scrollView.addView(textView);
setContentView(scrollView);
}
}
注意:别忘了,应该现在AndroidManifest.xml中配置访问网络的权限,即添加<uses-permission andorid:name=”android.permission.INTERNET”/>
运行效果图如下(是一个网络传输的页面数据):
另外,我们一般从网络上获得的数据大多为XML或是JSON格式的数据,对于这两种的格式的数据的解析和创建,我会在相关章节更新。
HttpClient的简单使用:
在这里,我们使用Android自带的HttpClient来获得网站数据。即使用POST方式来获得网站的数据,而另一个使用GET方式来获得数据,然后用TextView显示出来由服务器返回的数据内容。
代码如下:
/*
* HttpClient简单使用
* */
public class HttpClientAct extends Activity {
private TextView text;
private Button get;
private Button post;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
text = (TextView) findViewById(R.id.text_show);
get = (Button) findViewById(R.id.get_content);
post = (Button) findViewById(R.id.post_content);
OnClickListener c = new OnClickListener(){
public void onClick(View v) {
if(v == get) {// Http Get方式联机
method_get();
}
else if(v == post) {// Http Post方式联机
method_post();
}
}
};
get.setOnClickListener(c);
post.setOnClickListener(c);
}
private void method_get() {
String uri = "http://www.xxxx.cc:8751/index.php?str=I+am+Get+String";
HttpGet httpRequest = new HttpGet(uri);
// 发出httpRequest请求并接受返回的结果
try {
HttpResponse httpResponse = new DefaultHttpClient().execute(httpRequest);
// 如果返回的状态码为200则访问成功
if(httpResponse.getStatusLine().getStatusCode() == 200) {
// 获得返回的字符串
String result = EntityUtils.toString(httpResponse.getEntity());
// 去除冗余字符串
result = str_replace("(\r\n|\r|\n|\n\r)","",result);
// 显示返回的内容
text.setText(result);
}
else {
text.setText("error response:"+ httpResponse.getStatusLine().toString());
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void method_post() {
String uri = "http://www.xxxx.cc:8751/Android/Test/API/Post/index.php";
HttpPost httpRequest = new HttpPost(uri);
// 当以Post方式发送请求时,必须使用NameValuePair数组
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("str","I am Post String"));
// 发送请求
try {
httpRequest.setEntity(new UrlEncodedFormEntity(params,HTTP.UTF_8));
HttpResponse httpResponse;
httpResponse = new DefaultHttpClient().execute(httpRequest);
// 如果返回的状态码为200则访问成功
if(httpResponse.getStatusLine().getStatusCode() == 200) {
// 获得返回的字符串
String result = EntityUtils.toString(httpResponse.getEntity());
// 去除冗余字符串
result = str_replace("(\r\n|\r|\n|\n\r)","",result);
// 显示返回的内容
text.setText(result);
}
else {
text.setText("error response:"+ httpResponse.getStatusLine().toString());
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private String str_replace(String from,String to,String target) {
String pattern = "(?i)" + from;
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(target);
if(m.find())
return target.replaceAll(from, to);
else
return target;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.http_client, menu);
return true;
}
注意:别忘了,应该现在AndroidManifest.xml中配置访问网络的权限,即添加<uses-permission andorid:name=”android.permission.INTERNET”/>
另外,上面的URL地址要是有效的才可以得到服务器端的正常返回。当然,这个是经过我验证的,没有问题的。
效果图如下:
上面简单的介绍了在Android中基于Http协议的网络编程的简单使用,在上面我们使用了URLConnection和HttpClient来与服务器端进行沟通的验证,许多其他的复杂的网络通信都是以上面的为基础进行的。
当然,还有很多其他的相关内容,会在空余时间继续更新。
技术讨论群:179914858