android 之TCP客户端编程

吸取教训!!!本来花了5个小时写完了,没想到,,,因为没点上面的自动保存查看一下,全没了,重新写呗

关于网络通信:每一台电脑都有自己的ip地址,每台电脑上的网络应用程序都有自己的通信端口,张三的电脑(ip:192.168.1.110)上有一个网络应用程序A(通信端口5000),李四的电脑(ip:192.168.1.220)上有一个网络应用程序B(通信端口8000),张三给李四发消息,首先你要知道李四的ip地址,向指定的ip(李四ip:192.168.1.220)发信息,信息就发到了李四的电脑。再指定一下发送的端口号(通信端口8000),信息就发到了李四电脑的网络应用程序B上。

TCP--一种网络通信方式而已。分为服务器(网络应用程序)和客户端(网络应用程序),TCP通信过程,首先打开服务器,监听自己的网络通信端口(假设为9000),打开客户端,设置好要连接的ip地址和服务器的网络通信端口(9000),这样服务器一旦监听到网络通信端口有连接,二者就建立了连接。

好一步一步写程序(最后有源码!!!!!!!)

怎样建立工程就不说了,本来写好了并贴了图,网络一有问题全没了。抱怨一下,博客传图片真麻烦。竟然不支持复制  粘贴。各位朋友有什么方便的方法请告知。

在布局文件里加入两个按钮(button),一个控制连接,一个控制发送消息;四个输入文本框(edittext),一个填写发送的信息内容,一个显示服务器发来的消息。一个填写要链接的ip地址,一个填写要链接的端口号

布局代码

<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"
tools:context="com.wifi123.MainActivity" >以上都不用管的,软件自动生成的,配置界面的
<!--显示的标题:目标IP地址-->
<TextView 
android:textSize="20dp"字体大小
android:id="@+id/IP_tv" id号
android:text="目标IP地址"  显示的内容
android:layout_width="wrap_content"  宽度随内容而定
android:layout_height="wrap_content"  高度度随内容而定
/>
<!--显示的标题:目标端口号-->
<TextView 
android:textSize="20dp"
android:id="@+id/Port_tv"
android:text="目标端口号"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/IP_tv"  在<!--显示的标题:目标IP地址-->的下面
android:layout_marginTop="30dp"  离它上面那个组件(<!--显示的标题:目标IP地址-->)的距离
/>

<!-- 用于填写ip地址的文本框-->
<EditText 
android:text="192.168.4.1"
android:id="@+id/ip_ET"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/IP_tv"  在<!--显示的标题:目标IP地址-->的右面
/>
<!-- 用于填写端口号的文本框-->
<EditText 
android:text="8080"
android:id="@+id/Port_ET"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/Port_tv"  还是在谁谁谁的右面
android:layout_alignBottom="@id/Port_tv"  本元素的下边缘和某元素的的下边缘对齐 
/>
<!-- 用于发送信息的文本框-->
<EditText 
android:id="@+id/Send_ET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/Port_tv"  在某元素的下方
/>
<!-- 用于连接的按钮-->
<Button 
android:text="连接"
android:id="@+id/Connect_Bt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="Connect_onClick"设置按钮的动作监听函数,其实有几种写法,就用最简单的一种
android:layout_below="@id/Send_ET"  在某元素的下方
/>
<!-- 用于发送信息的按钮-->
<Button 
android:text="发送"
android:id="@+id/Send_Bt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="Send_onClick"
android:layout_below="@id/Send_ET"
android:layout_alignParentRight="true"  贴紧父元素的右边缘,指的是整体的界面
/>
<!-- 用于接收信息的文本框-->
<EditText 
android:background="@android:color/darker_gray"
android:id="@+id/Receive_ET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/Connect_Bt"
android:layout_alignParentBottom="true"  贴紧父元素的下边缘 
/>
</RelativeLayout>

看看布局界面

接着开始编写功能程序

先做点击连接按钮就连接服务器

查看java的API文档,里面封装了专门用于TCP客户端通信的类,和方法

里面有一个类Socket (客服端),有一个它的构造方法

Socket(InetAddress address, int port)  创建一个流套接字并将其连接到指定 IP 地址的指定端口号。

意思是Socket socket = new Socket(InetAddress address, int port) ;//创建连接地址和端口,就去连接指定的ip和端口号去了,address填ip地址,port填端口号

只不过InetAddress是一个类,我们打开看一下

那么

InetAddress ipAddress = InetAddress.getByName("192.168.4.1"); socket = new Socket(ipAddress, 8080);//创建连接地址和端口--------------就完了,客户端就去连接了

但是ip地址和端口被我们定死了,,,,可不好玩,我们就设置成获取ip文本框中的ip,端口号文本框中的端口号

InetAddress ipAddress = InetAddress.getByName(IPEditText.getText().toString()); int port =Integer.valueOf(PortText.getText().toString());//获取端口号 socket = new Socket(ipAddress, port);//创建连接地址和端口-------------------这样就好多了

但是由于在android几开始,不允许在主线程里连接服务器,所以只好让按钮点击后启动一个线程里面写上面的东西

package com.wifi123;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {
Button ConnectButton;//定义连接按钮
Button SendButton;//定义发送按钮
EditText IPEditText;//定义ip输入框
EditText PortText;//定义端口输入框
EditText MsgText;//定义信息输出框
EditText RrceiveText;//定义信息输入框
Socket socket = null;//定义socket
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

ConnectButton = (Button) findViewById(R.id.Connect_Bt);//获得按钮对象
SendButton = (Button) findViewById(R.id.Send_Bt);//获得按钮对象
IPEditText = (EditText) findViewById(R.id.ip_ET);//获得ip文本框对象
PortText = (EditText) findViewById(R.id.Port_ET);//获得端口文本框按钮对象
}

public void Connect_onClick(View v) {
//启动连接线程
Connect_Thread connect_Thread = new Connect_Thread();
connect_Thread.start();
}
class Connect_Thread extends Thread//继承Thread
{
public void run()//重写run方法
{
try 
{
if (socket == null) //如果已经连接上了,就不再执行连接程序
{
//用InetAddress方法获取ip地址
InetAddress ipAddress = InetAddress.getByName(IPEditText.getText().toString());
int port =Integer.valueOf(PortText.getText().toString());//获取端口号 
socket = new Socket(ipAddress, port);//创建连接地址和端口-------------------这样就好多了
}

} 
catch (Exception e) 
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

对了需要添加两个权限,一个是wifi权限,一个是internet

然后下载到手机因为我的电脑的ip为192.168.1.101,所以我把192.168.4.1改了,192.168.4.1是为了做与wifi模块EPS8266通信使得

然后打开网络调试助手,点击连接(可以关闭电脑防火墙),然后点击手机上的连接

好接着,连接按钮按一下连接,再按一下断开连接,并且,连接后按钮上显示断开,断开后按钮上显示连接

按钮事件改为

public void Connect_onClick(View v) {
if (isConnect == true) //标志位 = true表示连接
{
isConnect = false;//置为false
ConnectButton.setText("断开");//按钮上显示--断开
//启动连接线程
Connect_Thread connect_Thread = new Connect_Thread();
connect_Thread.start();
}
else //标志位 = false表示退出连接
{
isConnect = true;//置为true
ConnectButton.setText("连接");//按钮上显示连接
try 
{

socket.close();//关闭连接
socket=null;
} 
catch (IOException e) 
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

你可以试一试了

接着写点击发送按钮把发送信息文本框的内容发送出去

先贴代码

public void Send_onClick(View v) {
try 
{
//获取输出流
outputStream = socket.getOutputStream();
//发送数据
outputStream.write(MsgEditText.getText().toString().getBytes());
//outputStream.write("0".getBytes());
} 
catch (Exception e) 
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}

看看javaAPI

所以才有了

//获取输出流 OutputStream outputStream = socket.getOutputStream(); //发送数据 outputStream.write(MsgEditText.getText().toString().getBytes());

接收数据并在信息框显示出来

创建一个接收线程,在连接线程成功建立连接后启动接收线程

//接收线程
class Receive_Thread extends Thread
{
public void run()//重写run方法
{
try 
{
while (true) 
{
final byte[] buffer = new byte[1024];//创建接收缓冲区
inputStream = socket.getInputStream();
final int len = inputStream.read(buffer);//数据读出来,并且返回数据的长度
runOnUiThread(new Runnable()//不允许其他线程直接操作组件,用提供的此方法可以
{
public void run() 
{
// TODO Auto-generated method stub
RrceiveEditText.setText(new String(buffer,0,len));
}
});
}
} 
catch (IOException e) 
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
 //连接线程
    class Connect_Thread extends Thread//继承Thread
{
public void run()//重写run方法
{
try 
{
if (socket == null) 
{
//用InetAddress方法获取ip地址
InetAddress ipAddress = InetAddress.getByName(IPEditText.getText().toString());
int port =Integer.valueOf(PortText.getText().toString());//获取端口号 
socket = new Socket(ipAddress, port);//创建连接地址和端口-------------------这样就好多了
//在创建完连接后启动接收线程
Receive_Thread receive_Thread = new Receive_Thread();
     receive_Thread.start();
}
} 
catch (Exception e) 
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

下面贴全部源码

activity_mian.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"
    tools:context="com.wifi123.MainActivity" >
    <!--显示的标题:目标IP地址-->
	<TextView 
	    android:textSize="20dp"
	    android:id="@+id/IP_tv"
	    android:text="目标IP地址"
	    android:layout_width="wrap_content"
        android:layout_height="wrap_content"
	    />
	<!--显示的标题:目标端口号-->
	<TextView 
	    android:textSize="20dp"
	    android:id="@+id/Port_tv"
	    android:text="目标端口号"
	    android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/IP_tv"
        android:layout_marginTop="30dp"
	    />
 
    <!-- 用于填写ip地址的文本框-->
    <EditText 
        android:text="192.168.1.101"
        android:id="@+id/ip_ET"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/IP_tv"
        />
    <!-- 用于填写端口号的文本框-->
    <EditText 
        android:text="8080"
        android:id="@+id/Port_ET"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/Port_tv"
        android:layout_alignBottom="@id/Port_tv"
        />
     <!-- 用于发送信息的文本框-->
    <EditText 
        android:id="@+id/Send_ET"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/Port_tv"
        />
    <!-- 用于连接的按钮-->
    <Button 
        android:text="连接"
        android:id="@+id/Connect_Bt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="Connect_onClick"
        android:layout_below="@id/Send_ET"
        />
    <!-- 用于发送信息的按钮-->
    <Button 
        android:text="发送"
        android:id="@+id/Send_Bt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="Send_onClick"
        android:layout_below="@id/Send_ET"
        android:layout_alignParentRight="true"
        />
	 <!-- 用于接收信息的文本框-->
    <EditText 
        android:background="@android:color/darker_gray"
        android:id="@+id/Receive_ET"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/Connect_Bt"
        android:layout_alignParentBottom="true"
        />
</RelativeLayout>
MainActivity.java源码
package com.wifi123;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {
	boolean isConnect=true;//连接还是断开
	Button ConnectButton;//定义连接按钮
	Button SendButton;//定义发送按钮
	EditText IPEditText;//定义ip输入框
	EditText PortText;//定义端口输入框
	EditText MsgEditText;//定义信息输出框
	EditText RrceiveEditText;//定义信息输入框
	Socket socket = null;//定义socket
	private OutputStream outputStream=null;//定义输出流
	private InputStream inputStream=null;//定义输入流
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        ConnectButton = (Button) findViewById(R.id.Connect_Bt);//获得连接按钮对象
        SendButton = (Button) findViewById(R.id.Send_Bt);//获得发送按钮对象
        IPEditText = (EditText) findViewById(R.id.ip_ET);//获得ip文本框对象
        PortText = (EditText) findViewById(R.id.Port_ET);//获得端口文本框按钮对象
        MsgEditText = (EditText) findViewById(R.id.Send_ET);//获得发送消息文本框对象
        RrceiveEditText = (EditText) findViewById(R.id.Receive_ET);//获得接收消息文本框对象
    }
 
    public void Connect_onClick(View v) {
    	if (isConnect == true) //标志位 = true表示连接
		{
			isConnect = false;//置为false
			ConnectButton.setText("断开");//按钮上显示--断开
			//启动连接线程
	    	Connect_Thread connect_Thread = new Connect_Thread();
	    	connect_Thread.start();
		}
		else //标志位 = false表示退出连接
		{
			isConnect = true;//置为true
			ConnectButton.setText("连接");//按钮上显示连接
			try 
			{
 
				socket.close();//关闭连接
				socket=null;
			} 
			catch (IOException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
 
    public void Send_onClick(View v) {
    	try 
		{
			//获取输出流
			outputStream = socket.getOutputStream();
			//发送数据
			outputStream.write(MsgEditText.getText().toString().getBytes());
			//outputStream.write("0".getBytes());
		} 
		catch (Exception e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
    //连接线程
    class Connect_Thread extends Thread//继承Thread
	{
		public void run()//重写run方法
		{
			try 
			{
				if (socket == null) 
				{
					//用InetAddress方法获取ip地址
					InetAddress ipAddress = InetAddress.getByName(IPEditText.getText().toString());
					int port =Integer.valueOf(PortText.getText().toString());//获取端口号 
					socket = new Socket(ipAddress, port);//创建连接地址和端口-------------------这样就好多了
					//在创建完连接后启动接收线程
					Receive_Thread receive_Thread = new Receive_Thread();
			    	receive_Thread.start();
				}
 
			} 
			catch (Exception e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	//接收线程
	class Receive_Thread extends Thread
	{
		public void run()//重写run方法
		{
			try 
			{
				while (true) 
				{
					final byte[] buffer = new byte[1024];//创建接收缓冲区
					inputStream = socket.getInputStream();
					final int len = inputStream.read(buffer);//数据读出来,并且返回数据的长度
					runOnUiThread(new Runnable()//不允许其他线程直接操作组件,用提供的此方法可以
						{
							public void run() 
							{	
								// TODO Auto-generated method stub
								RrceiveEditText.setText(new String(buffer,0,len));	
							}						
						});
				}
			} 
			catch (IOException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
 
		}
	}
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏三流程序员的挣扎

Navigation 详解一

Navigation 是 JetPack 中的一个组件,用于方便的实现页面的导航,所以抽象出了一个 destination 的概念,大部分情况一个 destin...

23710
来自专栏向治洪

Android Remote Views

听名字就可以看出,remote views是一种远程view,感觉有点像远程service,其实remote views是view的一个结构,他可以在其他的进程...

20670
来自专栏Vamei实验室

安卓第四夜 概念漫游(下)

在安卓第三夜 概念漫游(上)中,我介绍了安卓最基本的功能单元和Intent的连接方式。在这个骨架之上,我们可以进一步增加一些与开发密切相关的重要概念。 Cont...

219100
来自专栏世界第一语言是java

Android全能开源项目xUtils3开发教程、简单封装

25040
来自专栏向治洪

android插件开发机制

插件机制实质上就是由主体程序定义接口,然后由插件去实现这些接口,以达到功能模块化。Android系统是基于Linux内核的,其安全机制也继承了Linux的特性...

23570
来自专栏向治洪

Universal Image Loader for Android 使用实例

<span style="white-space:pre">      </span>// 1.获取ImageLoader实例         ImageLo...

254100
来自专栏非著名程序员

Android自定义ViewGroup神器-ViewDragHelper

? 投稿作者:zhuhf 原文链接:http://www.jianshu.com/p/111a7bc76a0e 特别声明:本文为zhuhf原创并授权发布,未经...

35450
来自专栏wOw的Android小站

[Android][Framework] 从一个小问题了解STK加载内容的方式

这个界面从哪来的? 实际上,我们插入SIM卡,手机就会显示SimToolKit,打开就能看到一些和运营商相关的菜单。换了不同的卡菜单也会变。所以大概可以猜到,S...

27910
来自专栏软件开发 -- 分享 互助 成长

WIFI环境下Android手机和电脑通信

前面已经写过一篇java实现最基础的socket网络通信,这篇和之前那篇大同小异,只是将客户端代码移植到手机中,然后获取本机IP的方法略有不同。 先讲一下本篇中...

40950
来自专栏向治洪

android orm持久层框架

android数据库开发   Android中内置了sqlite,但是常用的开发语言java是面向对象的,而数据库是关系型的,二者之间的转化每次都很麻烦(主...

19450

扫码关注云+社区

领取腾讯云代金券