记录一下利用tcp使得服务器与android客户端相连

云服务器

租一个简单的服务器,

服务器

防火墙

首先将我们需要的端口打开

1
2
firewall-cmd --zone=public --add-port=6666/tcp --permanent   # 开放6666端口
firewall-cmd --reload # 配置立即生效

firewall-cmd --zone=public --list-ports 查看一下是否开启

有时阿里云服务器的端口也得打开,这个自行百度即可。

seriver.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <stdlib.h>

#define SERV_PORT 6666

char rbuf[1024];

int main() {
int sockfd,clientfd, size, ret, on = 1;
struct sockaddr_in saddr, raddr;

// 设置地址信息,ip信息
size = sizeof(struct sockaddr_in);
bzero(&saddr, size);
saddr.sin_family = AF_INET;
saddr.sin_port = htons(SERV_PORT);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);

// 创建UDP套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) {
perror("Socket failed.");
exit(1);
}

//设置端口复用
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

// 绑定地址信息,IP信息
ret = bind(sockfd, (struct sockaddr*)&saddr, sizeof(struct sockaddr));
if(ret < 0) {
perror("sbind failed.");
exit(2);
}

listen(sockfd, 128);

socklen_t val = sizeof(struct sockaddr);
puts("waiting connect...");
clientfd = accept(sockfd, (struct sockaddr*)&raddr, &val);
puts("get!");

// 循环接受客户端发来的信息
while(1) {
puts("waiting data....");
read(clientfd, rbuf, sizeof(rbuf));
printf("客户端传来数据: %s\n", rbuf);
strncpy(rbuf, "hello world too.\n", sizeof("hello world too.\n"));
write(clientfd, rbuf, sizeof(rbuf));
printf("发送给客户端数据: %s", rbuf);
bzero(rbuf, 50);

}

close(sockfd);
close(clientfd);
return 0;
}

Android 客户端

NDK

应为tcp的服务端是用c语言写的,所以为了android能够连接,我使用了ndk,ndk具体的配置可以自行百度。

网络权限

因为要进行网络连接,所以我们必须在mainfests文件中申请网络权限。如果没有权限创建socket会直接失败。

在application上面加上一句即可

1
<uses-permission android:name="android.permission.INTERNET"/>

layout文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<Button
android:id="@+id/send_link"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送连接"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.374" />

<TextView
android:text="TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/send_link"
android:layout_alignStart="@+id/send_link"
android:layout_marginTop="69dp"
android:id="@+id/textView"
tools:ignore="MissingConstraints" />


</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.example.test2;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}

public EditText editText;

public static native String linkTest();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// Example of a call to a native method
Button button = findViewById(R.id.send_link);
editText = findViewById(R.id.editText);
button.setOnClickListener(new View.OnClickListener() {
@SuppressLint("WrongConstant")
@Override
public void onClick(View v) {
new Thread(new Runnable() { //网络连接必须在子线程中
@Override
public void run() {
String ret = linkTest();
Log.d("s", ret);
//Toast.makeText(getApplicationContext(), linkTest(), 1).show();
}
}).start();
}
});
}

}

cpp文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <jni.h>
#include <string>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <android/log.h>

#define LOG_TAG "mysocket"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)

#define SERV_IP "123.56.252.111"
#define SERV_PORT 6666

extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_test2_MainActivity_linkTest(JNIEnv *env, jclass clazz) {
int cfd;
struct sockaddr_in serv_addr;
socklen_t serv_addr_len;

cfd = socket(AF_INET, SOCK_STREAM, 0);
if(cfd < 0) {
return env->NewStringUTF("socket failed.");
}
LOGI("socket successful");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr);
LOGI("connecting!");
int ret = connect(cfd, (sockaddr *)&serv_addr, sizeof(serv_addr));
if(ret < 0) {
LOGI("connect failed.");
return env->NewStringUTF("连接失败");
}
LOGI("connect successful");
char buf[1024] = "hello world\n";
write(cfd, buf, sizeof(buf));
bzero(buf, 1024);
read(cfd, buf, sizeof(buf));

return env->NewStringUTF(buf);
}

连接

我们点击模拟器中的连接按钮,可以在android日志中发现连接成功,并成功相互发送数据

---------Thanks for your attention---------