协议:tcp/ip 协议
架构:c/s 架构
数据库:mysql
服务器端接口语言 :python
客户端语言:安卓
注册:服务器端创建套接字,获取客户端发来的json数据,解析完后存入数据库
首先安装服务器端mysql 数据库 ,这里我直接安装了 phpstudy
然后 进入cmd,mysql - u root - p 输入密码登录数据库
然后进入 test 数据库
创建如下的数据表
mysql> create table users (
-> `id` int not null auto_increment ,
-> `account` char(10) not null,
-> `password` char(10) not null,
-> primary key (`id`),
-> unique key( `account`)
-> );
即创建了一个 主键为id 拥有两个栏目,account (账户)与password(密码)的表用于储存用户账户密码,并且设置各栏目非空,主键自增,account(账户)键值唯一,即用户名唯一
然后开始编写服务器端代码
#coding:utf-8
import MySQLdb ,socket,threading ,time,json
from json import *
class myThread (threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
print "Starting " + self.name
try:
data=conn.recv(1024)
print data
dict=json.loads(data)
dict = eval(dict)
threadLock.acquire()
cur=conne.cursor()
account=dict['account']
password = dict['password']
tuple = (account,password)
sql = " INSERT INTO users (account,password) VALUES(%s,%s)";
cur.execute(sql,tuple)
conn.send("写入成功")
cur.close()
print("succeed in writing")
threadLock.release()
finally:
conn.close()
conne=MySQLdb.connect(host='127.0.0.1',user='root',passwd='ONS2015',db='test',port=3306)
threadLock = threading.Lock()
HOST='127.0.0.1'
PORT=2333
s= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('127.0.0.1',2333))
s.listen(5)
while (1):
conn,addr = s.accept()
print'Connected by',addr
# 接受客户端端的连接后,就开启一个线程处理客户端的数据
thread = myThread()
thread.start()
其中
dict=json.loads(data)
dict = eval(dict)
是将json数据流转化为string,再转化为dict ,从而接下来对其进行操作
而conne=MySQLdb.connect(host=’127.0.0.1’,user=’root’,passwd=’ONS2015’,db=’test’,port=3306)
是数据库连接操作
sql = ” INSERT INTO users (account,password) VALUES(%s,%s)”;
cur.execute(sql,tuple)
则是向数据库中插入数据的操作
客户端先使用 python 进行简单测试,代码如下:
import MySQLdb ,socket,threading ,time,json
from json import *
HOST='127.0.0.1'
PORT=2333
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('127.0.0.1',2333))
dict="{'account':'doge','password':'123'}"
d=json.dumps(dict)
s.sendall(d)
data=s.recv(1024)
print data
主要使用了d=json.dumps(dict)将dict转化为json字符串
服务器端输出如下:
C:\Python27\python.exe E:/代码文件/python/client_test.py
Connected by ('127.0.0.1', 59171)
Starting Thread-1
"{'account':'doge','password':'123'}"
succeed in writing
客户端如下:
C:\Python27\python.exe E:/代码文件/python/client.py
写入成功
Process finished with exit code 0
接下来开始写安卓端的界面,这是第一次做出来的效果图
其中UI,即xml文件上遇到的问题如下:
1.密码输入只以 * 显示
android:inputType="textPassword"
2.设置一个隐藏的提示功能的文本
android:hint="请输入账户"
3.限制输入只能为字母数字下划线
以及长度限制最高为10
android:digits="1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_"
android:maxLength="10"
4.设置隐藏的提示框,即设置一个字体颜色,内容为空的textview ,用于在接下来提示输入
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f4f4f3"
android:orientation="vertical">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:contentDescription="dp"
android:src="@mipmap/dp"
android:layout_weight="0.15"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="0.05"
android:orientation="horizontal"
>
<TextView
android:layout_width="0sp"
android:layout_height="match_parent"
android:text="账户"
android:id="@+id/register_tv1"
android:layout_weight="0.02"
android:gravity="center"
android:textSize="20sp"
/>
<EditText
android:layout_width="0sp"
android:layout_height="match_parent"
android:hint="请输入账户"
android:id="@+id/register_account"
android:layout_weight="0.07"
android:digits="1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_"
android:maxLength="10"
/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/register_warm1"
android:text=""
android:layout_weight="0.0005"
android:gravity="center"
android:textSize="20sp"
android:textColor="#fc4984"
android:visibility="visible"
/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="0.05"
android:orientation="horizontal"
>
<TextView
android:layout_width="0sp"
android:layout_height="match_parent"
android:text="密码"
android:id="@+id/register_tv2"
android:layout_weight="0.02"
android:gravity="center"
android:textSize="20sp"
/>
<EditText
android:layout_width="0sp"
android:layout_height="match_parent"
android:hint="请输入密码"
android:id="@+id/register_password1"
android:layout_weight="0.07"
android:inputType="textPassword"
android:digits="1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_"
android:maxLength="10"
/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=""
android:id="@+id/register_warm2"
android:layout_weight="0.0005"
android:gravity="center"
android:textSize="20sp"
android:textColor="#fc4984"
android:visibility="visible"
/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="0.05"
android:orientation="horizontal"
>
<TextView
android:layout_width="0sp"
android:layout_height="match_parent"
android:text="密码"
android:id="@+id/register_tv3"
android:layout_weight="0.02"
android:gravity="center"
android:textSize="20sp"
/>
<EditText
android:layout_width="0sp"
android:layout_height="match_parent"
android:hint="请再次输入密码"
android:id="@+id/register_password2"
android:layout_weight="0.07"
android:inputType="textPassword"
android:digits="1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_"
android:maxLength="10"
/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=""
android:id="@+id/register_warm3"
android:layout_weight="0.0005"
android:gravity="center"
android:textSize="20sp"
android:textColor="#fc4984"
android:visibility="visible"
/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="0.05"
android:orientation="horizontal"
>
<TextView
android:layout_width="0sp"
android:layout_height="wrap_content"
android:text="性别"
android:textSize="20sp"
android:gravity="center"
android:layout_weight="0.29"/>
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.7">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="男"
android:layout_weight="0.5"/>
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="女"
android:layout_weight="0.5"/>
</LinearLayout>
</RadioGroup>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="0.1"
android:orientation="horizontal"
>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="完成注册"
android:id="@+id/register_bt"
android:layout_weight="0.005"
android:background="#1fb2f6"
android:textColor="#fefefe"
android:textSize="20sp"
/>
</LinearLayout>
</LinearLayout>
从中获得了很多学习经验,
如:控件可以嵌套LinearLayout,从而形成各种风格的布局
字体大小要设置为sp单位
可以设置RadioGroup 管理RadioButton
这是最终的客户端代码
package doge.dp.test;
import android.os.Bundle;
import android.os.Looper;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.design.widget.Snackbar;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
SetListener();
}
@Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_camera) {
// Handle the camera action
} else if (id == R.id.nav_gallery) {
} else if (id == R.id.nav_slideshow) {
} else if (id == R.id.nav_manage) {
} else if (id == R.id.nav_share) {
} else if (id == R.id.nav_send) {
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
//注册按钮响应事件
public void SetListener()
{
//定义控件变量
final EditText register_account = (EditText) findViewById(R.id.register_account);
final EditText register_password1 = (EditText) findViewById(R.id.register_password1);
final EditText register_password2 = (EditText) findViewById(R.id.register_password2);
final TextView register_warm1 = (TextView) findViewById(R.id.register_warm1);
final TextView register_warm2 = (TextView) findViewById(R.id.register_warm2);
final TextView register_warm3 = (TextView) findViewById(R.id.register_warm3);
//设置按钮点击监听
Button register_bt = (Button) findViewById(R.id.register_bt);
TextWatcher textWatcher1 = new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void afterTextChanged(Editable s) {
String register_account_text = register_account.getText().toString();
if(s.length()<6||s.length()>10)
{
register_warm1.setText("请将账号长度控制在6-10位之间");
}
else
register_warm1.setText("");
}
};
register_account.addTextChangedListener(textWatcher1);
TextWatcher textWatcher2 = new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void afterTextChanged(Editable s) {
String register_password1_text = register_password1.getText().toString();
String register_password2_text = register_password2.getText().toString();
if(register_password1_text.length()<6||register_password1_text.length()>10)
{
register_warm2.setText("请将密码长度控制在6-10位之间");
}
else
register_warm2.setText("");
if(!register_password2_text.equals(register_password1_text))
register_warm3.setText("请输入一样的密码");
else
register_warm3.setText("");
}
};
register_password1.addTextChangedListener(textWatcher2);
TextWatcher textWatcher3 = new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void afterTextChanged(Editable s) {
String register_password2_text = register_password2.getText().toString();
String register_password1_text = register_password1.getText().toString();
if(!register_password2_text.equals(register_password1_text))
{
register_warm3.setText("请输入一样的密码");
}
else
register_warm3.setText("");
}
};
register_password2.addTextChangedListener(textWatcher3);
Button reister_bt = (Button) findViewById(R.id.register_bt);
reister_bt.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view) {
String register_password1_text = register_password1.getText().toString();
String register_password2_text = register_password2.getText().toString();
String register_account_text = register_account.getText().toString();
String register_warm1_text = register_warm1.getText().toString();
String register_warm2_text = register_warm2.getText().toString();
String register_warm3_text = register_warm3.getText().toString();
if ( register_password1_text.equals("")||register_password2_text.equals("")||register_account_text.equals("") )
Toast.makeText( getApplicationContext(),"请输入完整注册信息",Toast.LENGTH_SHORT).show();
else if (!register_warm1_text.equals("")||!register_warm2_text.equals("")||!register_warm3_text.equals(""))
Toast.makeText( getApplicationContext(),"请输入正确的注册信息",Toast.LENGTH_SHORT).show();
else
{
Toast.makeText(getApplicationContext(),"flag",Toast.LENGTH_LONG);
ExecutorService exec = Executors.newCachedThreadPool();
MyTask task = new MyTask();
Future<Boolean> future = exec.submit(task);
try {
future.get(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Toast.makeText(getApplicationContext(),"请求超时,请检查网络",Toast.LENGTH_LONG).show();
} catch (ExecutionException e) {
Toast.makeText(getApplicationContext(),"请求超时,请检查网络",Toast.LENGTH_LONG).show();
} catch (TimeoutException e) {
Toast.makeText(getApplicationContext(),"请求超时,请检查网络",Toast.LENGTH_LONG).show();
}
finally {
exec.shutdownNow();
}
}
}
});
}
//继承了callable接口
class MyTask implements Callable<Boolean> {
String flag="0";
@Override
public Boolean call() throws Exception {
try
{
getMainLooper().prepare();
EditText register_account = (EditText) findViewById(R.id.register_account);
EditText register_password1 = (EditText) findViewById(R.id.register_password1);
EditText register_password2 = (EditText) findViewById(R.id.register_password2);
String account = register_account.getText().toString();
String password = register_password1.getText().toString();
Socket socket = new Socket("10.0.2.2", 2333);
OutputStream outputStream = socket.getOutputStream();
String jsonStr = "{\"account\": \""+ account +"\"," + " \"password\": \""+password+"\"}";
BufferedWriter BW = new BufferedWriter(new OutputStreamWriter(outputStream));
BW.write(jsonStr);
BW.flush();
InputStream is=socket.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is));
int data =br.read();
System.out.println(data);
if(data==49)
Toast.makeText(getApplicationContext(),"注册成功",Toast.LENGTH_LONG).show();
else
Toast.makeText(getApplicationContext(),"已存在同名账户,请更换账户名",Toast.LENGTH_LONG).show();
socket.close();
flag="1";
getMainLooper().loop();
}
catch (java.net.SocketTimeoutException e)
{
Toast.makeText(getApplicationContext(),"连接失败,请检查网络",Toast.LENGTH_LONG).show();
}
catch (IOException e) {
Toast.makeText(getApplicationContext(), "连接失败,请检查网络", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
return true;
}
}
}
这是最终的修改过后的服务器端代码
#coding:utf-8
import MySQLdb ,socket,threading ,time,json
from json import *
class myThread (threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
#线程的主体代码
def run(self):
print "Starting " + self.name
try:
data=conn.recv(1024) #接受数据
dict = eval(data) #强转为dict
account=dict['account']
password = dict['password']
tuple = (account,password)
sql = " INSERT INTO users (account,password) VALUES(%s,%s)";
threadLock.acquire() #获取线程锁
#接下来执行数据库操作
cur=conne.cursor()
cur.execute(sql,tuple)
#执行完毕后向客户端发1表示成功
conn.send("1")
print("succeed in writing")
except Exception,ex:
conn.send("0")
print("failed in writing")
finally:
cur.close()
threadLock.release()
conn.close()
#连接数据库
conne=MySQLdb.connect(host='127.0.0.1',user='root',passwd='ONS2015',db='test',port=3306)
threadLock = threading.Lock()
HOST='127.0.0.1'
PORT=2333
#创建socket
s= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('127.0.0.1',2333))
#设置最高同时监听数
s.listen(1000)
while (1):
conn,addr = s.accept()
# 接受客户端端的连接后,就开启一个线程处理客户端的数据
thread = myThread()
thread.start()
这是最终效果图:
1.未填完整信息
2.输入信息不正确
3.网络连接超时
4存在相同用户名.
5,注册成功