一、前言
小生不才,学习安卓socke通信打了很多滚,遇到过很多问题,也翻过前辈们关于socket的讲解,很杂各有见解,案例也不是很完整,于是特地将此安卓Socket与pc端c#服务器的通信代码讲解与socket的资料整理一遍加深理解,希望能有所帮助,小生没写过几次博客,欢迎各位大佬提出宝贵意见。安卓客户端项目源代码与pc端C#服务器与客户端的资源已经上传:安卓Socket与pc端c#服务器的通信完整代码(包含C#服务器与安卓项目工程)-优快云下载 https://download.youkuaiyun.com/download/zengpengqing1204/10374110
二、Socket的基本概念
1、套接字(socket)概念
Socket(套接字)用于描述IP地址和端口,是通信链的句柄,应用程序可以通过Socket向网络发出请求或者应答网络请求。
Socket是支持TCP/IP协议的网络通信的基本操作单元,是对网络通信过程中端点的抽象表示,包含了进行网络通信所必需的5种信息:连接所使用的协议、本地主机的IP地址、本地进程的协议端口、远地主机的IP地址以及远地进程的协议端口。
套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应 用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
2 、建立socket连接
建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。
套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发 给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
ps:其他一些几次握手的事情我就不弄出来了,没什么太大的营养,自己了解即可,重在实战
三、Socket的使用
1、socket的构造方法
Java在包java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的Socket客户端和服务器端。
Socket的构造方法如下:
(1)Socket(InetAddress address, int port);
(2)Socket(InetAddress address, int port, boolean stream);
(3)Socket(String host, int port);
(4)Socket(String host, int port, boolean stream);
(5)Socket(SocketImpl impl);
(6)Socket(String host, int port, InetAddress localAddr, int localPort);
(7)Socket(InetAddress address, int port, InetAddrss localAddr, int localPort);
ServerSocket的构造方法如下:
(1)ServerSocket(int port);
(2)ServerSocket(int port, int backlog);
(3)ServerSocket(int port, int backlog, InetAddress bindAddr);
其中,参数address、host和port分别是双向连接中另一方的IP地址、主机名和端口号;参数stream表示Socket是流Socket还是数据报Socket;参数localAddr和localPort表示本地主机的IP地址和端口号;SocketImpl是Socket的父类,既可以用来创建ServerSocket,也可以用来创建Socket。
2、输入流和输出流
Socket提供了方法getInputStream()和getOutPutStream()来获得对应的输入流和输出流,以便对Socket进行读写操作,这两个方法的返回值分别是InputStream和OutPutStream对象。
为了便于读写数据,我们可以在返回的输入输出流对象上建立过滤流,如PrintStream、InputStreamReader和OutputStreamWriter等。
3、关闭Socket
可以通过调用Socket的close()方法来关闭Socket。在关闭Socket之前,应该先关闭与Socket有关的所有输入输出流,然后再关闭Socket。
四、安卓与PC端的socket通讯
1.主程序代码附首页链接
-
package com.example.android_socket_pc_zpq3;
-
import java.io.BufferedReader;
-
import java.io.IOException;
-
import java.io.InputStreamReader;
-
import java.io.OutputStream;
-
import java.io.UnsupportedEncodingException;
-
import java.net.Socket;
-
import java.net.UnknownHostException;
-
import android.app.Activity;
-
import android.os.Bundle;
-
import android.os.Handler;
-
import android.os.Message;
-
import android.view.View;
-
import android.view.View.OnClickListener;
-
import android.widget.EditText;
-
import android.widget.TextView;
-
import android.widget.Toast;
-
public class MainActivity extends Activity {
-
private EditText ipEditText,portEditText,sendEditText;//声明输入IP编辑框,端口号编辑框,发送信息编辑框
-
private TextView chatTextView;//声明聊天记录显示文本
-
private Thread connectThread;//声明连接服务器线程
-
private Handler messageHandler;//声明信息处理机制函数
-
Socket socket;//声明socket对象
-
BufferedReader bufferedReader;//声明输入流对象
-
OutputStream outputStream;//声明输出流对象
-
Boolean isconnectBoolean = false;//初始化socket连接值为未连接
-
@Override
-
protected void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_main);
-
init();//初始化控件
-
}
-
//连接服务器线程:连接服务器并接收数据 本人觉得可以放一起写
-
Runnable connectRunnable = new Runnable() {
-
@Override
-
public void run() {
-
// TODO Auto-generated method stub
-
try {
-
//新建socket对象,填入编辑框IP,端口号
-
socket=new Socket(ipEditText.getText().toString(),
-
Integer.valueOf(portEditText.getText().toString()));
-
//取得输入流、输出流
-
bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
-
outputStream=socket.getOutputStream();
-
//接收数据可用子线程也可直接在此线程操作
-
char[] buffer=new char[256];//定义数组接收输入流数据
-
String bufferString="";//定义一个字符接收数组数据
-
int conut =0;//初始化buffer数组长度为0
-
int tag=0;//初识写入数组的位置
-
//死循环重复接收输入流数据并进行处理
-
while (true) {
-
//当输入流写入buffer数组的长度大于0时 即 接收到数据时
-
while ((conut=bufferedReader.read(buffer))>0) {
-
//将buffer数组的数据全部写入bufferString字符类型
-
while ( tag<buffer.length) {
-
bufferString=bufferString+buffer[tag];
-
tag++;
-
}
-
//将数据给messageHandler刷新UI界面
-
Message msgMessage =new Message();
-
msgMessage.obj=bufferString;
-
msgMessage.what=1;
-
messageHandler.sendMessage(msgMessage);
-
//初始化数据,以便处理下一条输入流信息
-
tag=0;
-
bufferString="";
-
}
-
}
-
} catch (NumberFormatException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
} catch (UnknownHostException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
} catch (IOException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
}
-
};
-
//初始化所有控件
-
private void init() {
-
ipEditText=(EditText) findViewById(R.id.IpeditText);
-
portEditText=(EditText)findViewById(R.id.PorteditText);
-
sendEditText=(EditText)findViewById(R.id.sendeditText);
-
chatTextView=(TextView)findViewById(R.id.chattextView);
-
//连接服务器按钮事件
-
findViewById(R.id.connectbutton).setOnClickListener(new OnClickListener() {
-
@Override
-
public void onClick(View arg0) {
-
//逻辑判断socket是否连接上,再做操作
-
if (isconnectBoolean==false) {
-
connectThread=new Thread(connectRunnable);
-
connectThread.start();
-
Toast.makeText(MainActivity.this, "连接服务器成功", 2).show();
-
isconnectBoolean=true;
-
}
-
}
-
});
-
//断开服务器按钮事件
-
findViewById(R.id.breakbutton).setOnClickListener(new OnClickListener() {
-
@Override
-
public void onClick(View arg0) {
-
//逻辑判断socket是否连接上,再做操作
-
if (isconnectBoolean==true) {
-
if (socket !=null) {
-
try {
-
//先关闭socket连接,再关闭输入流、输出流,
-
socket.close();
-
socket = null;
-
bufferedReader.close();
-
outputStream.close();
-
} catch (IOException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
}
-
//中断连接线程
-
connectThread.interrupt();
-
Toast.makeText(MainActivity.this, "断开连接!", 2).show();
-
isconnectBoolean=false;
-
}
-
}
-
});
-
//发送信息给服务器按钮事件
-
findViewById(R.id.sendbutton).setOnClickListener(new OnClickListener() {
-
@Override
-
public void onClick(View arg0) {
-
//这里可以用线程也可以不用线程
-
try {
-
//输出流写入发送编辑框的信息并指定类型UTF-8,注意要加换行
-
outputStream.write((sendEditText.getText().toString()+"\n").
-
getBytes("utf-8"));
-
//输出流发送至服务器
-
outputStream.flush();
-
//handlerMessage处理发送信息刷新UI界面
-
Message msgMessage =new Message();
-
msgMessage.obj=sendEditText.getText().toString();
-
msgMessage.what=0;
-
messageHandler.sendMessage(msgMessage);
-
} catch (UnsupportedEncodingException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
} catch (IOException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
}
-
});
-
//处理接收服务器信息以及发送至服务器信息呈现聊天室效果
-
messageHandler = new Handler(){
-
public void handleMessage(android.os.Message msgMessage) {
-
String sendString="";
-
String receiveString="";
-
switch (msgMessage.what) {
-
case 0:
-
sendString="曾鹏晴说:"+msgMessage.obj.toString()+"\n";
-
chatTextView.append(sendString);
-
break;
-
case 1:
-
receiveString="小权权说:"+msgMessage.obj.toString()+"\n";
-
chatTextView.append(receiveString);
-
break;
-
default:
-
break;
-
}
-
}
-
};
-
}
-
}
2、布局文件
-
<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=".MainActivity" >
-
<EditText
-
android:id="@+id/IpeditText"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:layout_alignParentLeft="true"
-
android:layout_alignParentTop="true"
-
android:layout_marginLeft="36dp"
-
android:ems="7"
-
android:text="192.168.1.101" >
-
<requestFocus />
-
</EditText>
-
<TextView
-
android:id="@+id/textView1"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:layout_alignBaseline="@+id/IpeditText"
-
android:layout_alignBottom="@+id/IpeditText"
-
android:layout_alignParentLeft="true"
-
android:text="IP:" />
-
<TextView
-
android:id="@+id/textView2"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:layout_alignBaseline="@+id/IpeditText"
-
android:layout_alignBottom="@+id/IpeditText"
-
android:layout_toRightOf="@+id/IpeditText"
-
android:text="Port:" />
-
<EditText
-
android:id="@+id/PorteditText"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:layout_alignBaseline="@+id/textView2"
-
android:layout_alignBottom="@+id/textView2"
-
android:layout_alignParentRight="true"
-
android:ems="4"
-
android:text="8488" />
-
<Button
-
android:id="@+id/connectbutton"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:layout_alignLeft="@+id/textView1"
-
android:layout_below="@+id/IpeditText"
-
android:layout_marginTop="14dp"
-
android:text="连接服务器" />
-
<Button
-
android:id="@+id/breakbutton"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:layout_alignBaseline="@+id/connectbutton"
-
android:layout_alignBottom="@+id/connectbutton"
-
android:layout_marginLeft="41dp"
-
android:layout_toRightOf="@+id/connectbutton"
-
android:text="断开服务器" />
-
<EditText
-
android:id="@+id/sendeditText"
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content"
-
android:layout_alignRight="@+id/PorteditText"
-
android:layout_below="@+id/connectbutton"
-
android:layout_marginTop="16dp"
-
android:ems="10"
-
android:text="hello 小权权" />
-
<Button
-
android:id="@+id/sendbutton"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:layout_alignLeft="@+id/sendeditText"
-
android:layout_alignRight="@+id/sendeditText"
-
android:layout_below="@+id/sendeditText"
-
android:layout_marginTop="14dp"
-
android:text="发送信息至服务器" />
-
<TextView
-
android:id="@+id/textView3"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:layout_alignLeft="@+id/sendbutton"
-
android:layout_below="@+id/sendbutton"
-
android:layout_marginTop="14dp"
-
android:text="聊天记录:" />
-
<TextView
-
android:id="@+id/chattextView"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:layout_below="@+id/textView3"
-
android:layout_toRightOf="@+id/textView1"
-
android:text="" />
-
</RelativeLayout>
3、PC端c#服务器代码附首页链接
-
using System;
-
using System.Collections.Generic;
-
using System.Linq;
-
using System.Text;
-
using System.Threading.Tasks;
-
using System.Windows;
-
using System.Windows.Controls;
-
using System.Windows.Data;
-
using System.Windows.Documents;
-
using System.Windows.Input;
-
using System.Windows.Media;
-
using System.Windows.Media.Imaging;
-
using System.Windows.Navigation;
-
using System.Windows.Shapes;
-
using System.Net.Sockets;
-
using System.Net;
-
using System.Threading;
-
namespace socket
-
{
-
/// <summary>
-
/// MainWindow.xaml 的交互逻辑
-
/// </summary>
-
public partial class MainWindow : Window
-
{
-
public MainWindow()
-
{
-
InitializeComponent();
-
}
-
Socket ServerSocket = null;
-
private void btnStart_Click(object sender, RoutedEventArgs e)
-
{
-
IPEndPoint IPE = new IPEndPoint(IPAddress.Parse(tboxIP.Text), Int32.Parse(tboxPort.Text));
-
ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
-
ServerSocket.Bind(IPE);
-
ServerSocket.Listen(10);
-
showmsg("服务器已启动,监听中...");
-
Thread thread = new Thread(ListenClientConnect);
-
thread.IsBackground=true;
-
thread.Start();
-
}
-
Dictionary<string, Socket> dic = new Dictionary<string, Socket>();
-
private void ListenClientConnect(object obj)
-
{
-
while (true)
-
{
-
Socket socketClient = ServerSocket.Accept() ;
-
string RemoteIP = socketClient.RemoteEndPoint.ToString();
-
dic.Add(RemoteIP, socketClient);
-
Dispatcher.Invoke(()=>lstboxIP.Items.Add(RemoteIP));
-
showmsg(RemoteIP + "已连接");
-
Thread recieveThread = new Thread(recievemsg);
-
recieveThread.IsBackground = true;
-
recieveThread.Start(socketClient);
-
}
-
}
-
private void recievemsg(object soc)
-
{
-
Socket socketClient = (Socket)soc;
-
while (true)
-
{
-
byte[] buffer = new byte[1024];
-
int n = socketClient.Receive(buffer);
-
//string msg = Encoding.Default.GetString(buffer, 0, n);
-
string msg = Encoding.UTF8.GetString(buffer, 0, n);
-
//可在这里指定接受数据格式
-
showmsg(socketClient.RemoteEndPoint.ToString()+":"+msg);
-
}
-
}
-
private void showmsg(string p)
-
{
-
Dispatcher.BeginInvoke(new Action(() =>
-
{
-
rtbx.AppendText(p + "\r\n");
-
}));
-
}
-
private void btnStop_Click(object sender, RoutedEventArgs e)
-
{
-
ServerSocket.Close();
-
}
-
private void btnSend_Click(object sender, RoutedEventArgs e)
-
{
-
showmsg(tboxMsg.Text);
-
string ip = lstboxIP.SelectedValue.ToString();
-
//byte[] by = Encoding.UTF8.GetBytes(tboxMsg.Text);
-
byte[] by = Encoding.Default.GetBytes(tboxMsg.Text);
-
dic[ip].Send(by,0);
-
tboxMsg.Text = "";
-
}
-
}
-
}
4、布局文件
-
<Window x:Class="socket.MainWindow"
-
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-
Title="MainWindow" Height="350" Width="525">
-
<Grid>
-
<Label x:Name="label" Content="IP地址:" HorizontalAlignment="Left" Margin="24,15,0,0" VerticalAlignment="Top"/>
-
<TextBox x:Name="tboxIP" HorizontalAlignment="Left" Height="23" Margin="86,17,0,0" TextWrapping="Wrap" Text="192.168.1.101" VerticalAlignment="Top" Width="92"/>
-
<Label x:Name="label1" Content="端口:" HorizontalAlignment="Left" Margin="183,16,0,0" VerticalAlignment="Top"/>
-
<TextBox x:Name="tboxPort" HorizontalAlignment="Left" Height="23" Margin="228,17,0,0" TextWrapping="Wrap" Text="8488" VerticalAlignment="Top" Width="43"/>
-
<Button x:Name="btnStart" Content="服务器启动" HorizontalAlignment="Left" Margin="287,19,0,0" VerticalAlignment="Top" Width="75" Click="btnStart_Click"/>
-
<Button x:Name="btnStop" Content="服务器停止" HorizontalAlignment="Left" Margin="377,19,0,0" VerticalAlignment="Top" Width="75" Click="btnStop_Click"/>
-
<ListBox x:Name="lstboxIP" HorizontalAlignment="Left" Height="180" Margin="377,65,0,0" VerticalAlignment="Top" Width="116"/>
-
<Button x:Name="btnSend" Content="发送信息" HorizontalAlignment="Left" Margin="377,271,0,0" VerticalAlignment="Top" Width="75" Click="btnSend_Click"/>
-
<TextBox x:Name="tboxMsg" HorizontalAlignment="Left" Height="23" Margin="24,270,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="320"/>
-
<RichTextBox x:Name="rtbx" HorizontalAlignment="Left" Height="180" Margin="24,65,0,0" VerticalAlignment="Top" Width="320">
-
<FlowDocument>
-
<Paragraph>
-
</Paragraph>
-
</FlowDocument>
-
</RichTextBox>
-
</Grid>
-
</Window>
5、聊天室效果图
上一篇eclipse及myeclipse快捷键及各种设置 ——Eclipse的联想功能
想对作者说点什么? 我来说一句
Zengpengqing12042018-08-02 11:31:28#4楼
[java] view plain copy- try {
- //新建socket对象,填入编辑框IP,端口号
- socket=new Socket(ipEditText.getText().toString(),
- Integer.valueOf(portEditText.getText().toString()));
- //取得输入流、输出流
- bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
- outputStream=socket.getOutputStream();
- //接收数据可用子线程也可直接在此线程操作
- char[] buffer=new char[1024];//定义数组接收输入流数据
- //int len = bufferedReader.read(buffer);//将网络输入流的数据写入buffer数组
- int conut =0;//初始化buffer数组长度为0
- //死循环重复接收输入流数据并进行处理
- while (true) {
- //当输入流写入buffer数组的长度大于0时 即 接收到数据时
- while ((conut=bufferedReader.read(buffer))>0) {
- //将buffer数组的数据全部写入bufferString字符类型
- String bufferString = new String(buffer, 0, conut);
- //将数据给messageHandler刷新UI界面
- Message msgMessage =new Message();
- msgMessage.obj=bufferString;
- msgMessage.what=1;
- messageHandler.sendMessage(msgMessage);
- //初始化数据,以便处理下一条输入流信息
- conut=0;
- }
- }
Zengpengqing12042018-08-02 11:30:59#3楼
改了最后一个朋友反馈的问题,输入流信息的处理显示
Zengpengqing12042018-06-03 22:06:59#2楼
源代码在关闭socket时有个小修改 应该是先关闭socket,再依次关闭输入流输出流
qq_371793652018-05-27 21:48:42#1楼
博主有两把刷子哦,用上喽