TCP是SOCKET通信中面向连接的协议
为此,我特地先写了PC端的TCP套接字通信程序
PC服务端
#include<stdio.h>
#include<winsock2.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
#define PORT 4567
#define ADDR "192.168.137.1"
int main()
{
WSADATA wsock;
SOCKET listensocket;
SOCKET newconnection;
SOCKADDR_IN serAddr;
SOCKADDR_IN cliAddr;
int cliAddrLen = sizeof(cliAddr);
int nRet = 0;
char buf[100];
cout << "服务端";
cout << ADDR << "-" << PORT << endl;
//下面初始化WinSock
cout << "初始化套接字..." << endl;
if (WSAStartup(MAKEWORD(2, 2), &wsock) != 0)
{
cout << "初始化失败" << endl;
return 0;
}
cout << "初始化成功" << endl;
//下面创建监听
cout << "创建TCP监听套接字..." << endl;
if ((listensocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
cout << "监听创建失败" << endl;
WSACleanup();
return 0;
}
cout << "TCP监听套接字创建成功" << endl;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(PORT);
serAddr.sin_addr.S_un.S_addr = inet_addr(ADDR);
//绑定监听套接字
cout << "绑定监听套接字..." << endl;
if (bind(listensocket, (SOCKADDR*)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
{
cout << "监听套接字绑定失败" << endl;
closesocket(listensocket);
WSACleanup();
return 0;
}
cout << "监听套接字绑定成功" << endl;
//创立监听
cout << "创建监听..." << endl;
if (listen(listensocket, 5) == SOCKET_ERROR)
{
cout << "监听创建失败" << endl;
closesocket(listensocket);
WSACleanup();
return 0;
}
cout << "监听创建成功" << endl;
//等待连接
cout << "等待连接..." << endl;
if ((newconnection = accept(listensocket, (SOCKADDR*)&cliAddr, &cliAddrLen)) == SOCKET_ERROR)
{
cout << "连接错误" << endl;
closesocket(listensocket);
WSACleanup();
return 0;
}
cout << "连接成功!" << endl;
cout << "连接信息:" << inet_ntoa(cliAddr.sin_addr)<<"-"<<ntohs(cliAddr.sin_port) << endl;
//这个程序只监听一个所以只监听第一个接收到连接的
closesocket(listensocket);
cout << "等待数据..." << endl;
for (int i = 0;;i++)
{
memset(buf, 0, sizeof(buf));
if ((nRet = recv(newconnection, buf, sizeof(buf), 0)) == SOCKET_ERROR)
{
cout << "数据接收错误" << endl;
closesocket(newconnection);
WSACleanup();
return 0;
}
cout << buf << endl;
//若发现exit退出
if (strcmp(buf, "exit") == 0)
{
cout << "正常退出" << endl;
break;
}
if ((nRet = send(newconnection, buf, strlen(buf), 0)) == SOCKET_ERROR)
{
cout << "发送失败" << endl;
}
}
closesocket(newconnection);
WSACleanup();
return 0;
}
PC客户端
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<winsock2.h>
#include<cstdio>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
#define PORT 4567
#define ADDR "192.168.137.1"
int main()
{
WSADATA wsock;
SOCKET sconnection;
SOCKADDR_IN serAddr;
int nRet = 0;
char buf[100];
cout << "客户端"<<endl;
//初始化套接字
cout << "初始化套接字..." << endl;
if (WSAStartup(MAKEWORD(2, 2), &wsock) != 0)
{
cout << "初始化失败" << endl;
return 0;
}
cout << "初始化成功" << endl;
//下面创建连接套接字
cout << "创建TCP连接套接字..." << endl;
if ((sconnection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
cout << "连接套接字创建失败" << endl;
WSACleanup();
return 0;
}
cout << "TCP连接套接字创建成功" << endl;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(PORT);
serAddr.sin_addr.S_un.S_addr = inet_addr(ADDR);
//下面链接服务器
cout << "连接服务器..." << inet_ntoa(serAddr.sin_addr) << "-" << htons(serAddr.sin_port) << endl;
if (connect(sconnection, (SOCKADDR*)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
{
cout << "连接失败" << endl;
closesocket(sconnection);
WSACleanup();
return 0;
}
cout << "连接成功!" << endl;
cout << "尝试发送数据" << endl;
for (int i = 0;;i++)
{
memset(buf, 0, sizeof(buf));
cout << "输入信息:" << endl;
cin >> buf;
cout << "发送中.." << endl;
if ((nRet = send(sconnection, buf, strlen(buf), 0)) == SOCKET_ERROR)
{
cout << "发送失败" << endl;
closesocket(sconnection);
WSACleanup();
return 0;
}
cout << "发送成功" << endl;
if (strcmp(buf, "exit") == 0)
{
cout << "正常退出" << endl;
break;
}
memset(buf, 0, sizeof(buf));
cout << "等待回应..." << endl;
if ((nRet = recv(sconnection, buf, sizeof(buf), 0)) == SOCKET_ERROR)
{
cout << "接收错误" << endl;
}
cout << "回应:";
cout << buf << endl;
}
closesocket(sconnection);
WSACleanup();
return 0;
}
好了,现在写Android的程序
跟之前一样,我只写客户端的程序
首先在AndroidManifest.xml上加权限
<uses-permission android:name="android.permission.INTERNET"/>
注意要写在
</application>
的外面
然后,是一个继承View的类,我的想法是每当点一下屏幕发送一串字符
import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.net.Socket; import java.net.UnknownHostException; /** * Created by xyt on 2015/10/9. */ public class MyView extends View{ int door=0; Socket socket; public MyView(Context context) { super(context); } public boolean onTouchEvent(MotionEvent event) { new Thread(new Runnable() { @Override public void run() { send(); door=1; } }).start(); return false; } protected void onDraw(Canvas canvas) { } public void send(){ try{ if(door==0){ socket=new Socket("192.168.137.1", 4567); socket.setSendBufferSize(100); } BufferedWriter out=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); out.write("lalala"); out.flush(); }catch(UnknownHostException e){ System.out.println(e); }catch(IOException e){ System.out.println(e); } } }
door记录是否是第一次点击,第一次点击就进行连接
因为执行
new Socket("192.168.138.1",4567)
的过程实际上是先发送给我的服务端一个表明地址的信息
因此这个信息只需要开始连接的时候发送就可以了,之后就不用
out.write直接将一个字符串写进发送缓存,
out.flush直接将缓存的东西全部发出,不然就是等到缓存满了再发出
当然,有一布非常必要,就是
socket.setSendBufferSize()
这个是用来调整缓存大小的
因为PC服务器的缓存大小为100byte,所以这里最好设置100byte
不要忘记,另开一个线程执执行网络任务
最后如果要关闭连接,直接out.close()就行
下面是最后步骤,在onCreate中加上
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); view=new MyView(this); setContentView(view); Log.i(Activity_ID, "onCreate!"); }
最后,我写了一个TCP通信的客户端类,包含接收和发送
package com.example.xyt.helloandroid; import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.net.Socket; import java.net.UnknownHostException; /** * Created by xyt on 2015/10/12. */ public class SocketClient { private boolean IsConnectedFlag=false; private Socket socket; private byte[] buf=new byte[100]; public String address="192.168.137.1"; public int port=4567; public void SetServer(String in_ip,int in_port){ address=in_ip; port=in_port; } //判断是否已经连接 public boolean IsConnected(){ return IsConnectedFlag; } //该方法是在数据通信前用的,用于连接服务器 private void ConnectServer(){ try{ socket=new Socket(address,port ); socket.setSendBufferSize(100); }catch(IOException e){ System.out.println(e); } } //该方法用cmd去换服务器的一个回应,保存在buf中,注意调用此方法前必须先连接 private void ChangeDataPack(String cmd){ try{ BufferedWriter out=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); out.write(cmd); out.flush(); InputStream inputStream = socket.getInputStream(); DataInputStream input = new DataInputStream(inputStream); int length=input.read(buf); String msg=new String(buf,0,length); System.out.println(msg); }catch(UnknownHostException e){ System.out.println(e); }catch(IOException e){ System.out.println(e); } } //另开线程交换数据,等待回复 public void ExchangeData(final String in_cmd){ new Thread(new Runnable() { @Override public void run() { ChangeDataPack(in_cmd); } }).start(); } //另开线程等待连接 public void Connect(){ new Thread(new Runnable() { @Override public void run() { ConnectServer(); IsConnectedFlag=true; } }).start(); } //关闭连接 public void CloseConnection(){ new Thread(new Runnable(){ @Override public void run() { try{ socket.close(); }catch (IOException e){ System.out.println(e); } IsConnectedFlag=false; } }).start(); } }