在这里,我们将给大家分享关于Python--socket实现服务器之间的通信的知识,让您更了解pythonsocket服务端的本质,同时也会涉及到如何更有效地2.socket结构体——表示socket
在这里,我们将给大家分享关于Python -- socket 实现服务器之间的通信的知识,让您更了解python socket 服务端的本质,同时也会涉及到如何更有效地2. socket 结构体 —— 表示 socket 地址、Android NDK Socket(POSIX Socket Api)编程、boost::asio 序列16: tcp::socket 和 udp::socket & socket_basic、C# Socket 系列一 简单的创建 socket 的监听的内容。
本文目录一览:- Python -- socket 实现服务器之间的通信(python socket 服务端)
- 2. socket 结构体 —— 表示 socket 地址
- Android NDK Socket(POSIX Socket Api)编程
- boost::asio 序列16: tcp::socket 和 udp::socket & socket_basic
- C# Socket 系列一 简单的创建 socket 的监听
Python -- socket 实现服务器之间的通信(python socket 服务端)
现在需要做一个分布式课程设计(简单小游戏),三个人小组合作完成。
我需要设计一个登录注册服务器,接收来自网关服务器(消息中间件)的用户登录注册消息请求,然后生成访问数据库服务器的消息,发送给数据库服务器,接收并处理其返回信息,发送登录注册结果给网关服务器。(很简单的功能)
我的想法是:登录注册服务器主线程一直运行,监控是否有来自网关服务器的连接请求。每当接收到一次连接请求时,开辟一个新的子线程,处理来自网关服务器的消息请求,并生成访问数据库的请求消息,发送给数据库服务器,随即接收返回的数据库操作信息,子线程处理后发送登录注册结果给网关服务器。
编程语言:Python
网络通信方式:tcp(具体使用socket)
数据交换格式:json
流程图:
代码如下:
1 Server.py
2
3 # -*- coding: UTF-8 -*-
4 import socket
5 import datetime
6 import time
7 import json
8
9 class Server(object):
10 """Server Side"""
11
12 def __init__(self):
13 self.host = ''219.224.167.162''
14 self.port = 6999
15 Arr=(self.host,self.port)
16 self.s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
17 self.s.bind(Arr)
18 self.s.listen(5) #操作系统可以挂起的最大连接数量
19
20 def ReceiveMSG(self, threadname, sk_conn):
21 try:
22 msg_conn = sk_conn.recv(1024).decode()
23 data_conn= json.loads(msg_conn)
24 print(data_conn)
25 #访问数据库服务器
26 sk_db = socket.socket() # 创建 socket 对象
27 host = ''219.224.167.250'' # 获取数据库服务器主机名
28 port = 9999 # 设置端口号
29 sk_db.connect((host, port))
30 #分析客户端 登录or注册
31 if data_conn[0][''OP'']==''login'':
32 print('' -【登录】:'',data_conn)
33 data=[{''OP'':''login'',''username'':''%s''%(data_conn[0][''username''])}]
34 msg=json.dumps(data)
35 sk_db.send(msg.encode())
36 print('' -数据库服务器返回:'')
37 while True:
38 msg=sk_db.recv(1024).decode()
39 print('' '',msg)
40 if msg==''Close!'':
41 sk_db.close()
42 break
43 if msg==''Welcome!'':
44 continue
45 else:
46 data=json.loads(msg)
47 sk_db.send(b''exit'')
48 if data[0][''password'']==''None'':
49 data=[{''code'':1,''info'':''Failed for error username!''}]
50 elif data[0][''password'']!=data_conn[0][''password'']:
51 data=[{''code'':2,''info'':''Failed for error password!''}]
52 else:
53 data=[{''code'':3,''info'':''success!''}]
54
55 ##返回信息到客户端
56 #data=[{''code'':3,''info'':''success!''}]#
57 msg=json.dumps(data)
58 print('' 返回客户端信息: '',msg)
59 sk_conn.send(msg.encode())
60 sk_conn.close()
61 else:
62 print('' -【注册】:'',data_conn)
63 data=data_conn
64 msg=json.dumps(data)
65 sk_db.send(msg.encode())
66 print('' -数据库服务器返回:'')
67 while True:
68 msg=sk_db.recv(1024).decode()
69 print('' '',msg)
70 if msg==''Close!'':
71 sk_db.close()
72 break
73 if msg==''Welcome!'':
74 continue
75 else:
76 data=json.loads(msg)
77 sk_db.send(b''exit'')
78 if data[0][''msg'']==''success'':
79 data=[{''code'':4,''info'':''success''}]
80 else:
81 data=[{''code'':5,''info'':''fail''}]
82 #data=[{''code'':4,''info'':''success''}]#
83 #返回信息到客户端
84 msg=json.dumps(data)
85 print('' 返回客户端信息: '',msg)
86 sk_conn.send(msg.encode())
87 sk_conn.close()
88 except BaseException:
89 print(''An unknow error occurred.'')
90
91 def __delattr__(self):
92 self.sock.close()
93 self.s.close()
1 LoginRegisterServer.py
2 主线程
3
4 # -*- coding: UTF-8 -*-
5 import _thread
6 import time
7 import json
8 from Server import Server
9 from Client import Client
10
11 print(''服务器已启动,开始提供 【登录 注册】 服务...\n'')
12 server=Server()
13
14 while True:
15 sk_conn,addr = server.s.accept()
16 print(''\n请求链接用户信息:'', addr)
17
18 try:
19 _thread.start_new_thread( server.ReceiveMSG, ("Thread: deal with request.", sk_conn) )
20 except:
21 print("Error: unable to start thread")
原文出处:https://www.cnblogs.com/chen9510/p/10692523.html
2. socket 结构体 —— 表示 socket 地址
一、两种通用 socket 结构体
1. sockaddr
struct sockaddr {
sa_family_t sa_family; // 地址族
char sa_data[14]; // 存放socket地址值
};
补:由于不同的协议栈的地址值具有不同的含义和长度(如 PF_INET6 的地址值占用 26 字节,更不必说 PF_UNIX 的地址值最大可达到 108 字节),所以 14 字节的 sa_data 根本无法容纳多数协议族的地址值。
2. sockaddr_storage
struct sockaddr_storage {
sa_family_t sa_family; // 地址族
unsigned long int __ss_align; // 用于内存对齐
char __ss_padding[128-sizeof(__ss_align)]; // 提供足够大的空间用于存放地址值
};
这两个通用 socket 地址结构体显然很不好用,比如设置与获取 IP 地址和端口号就需要执行繁琐的位操作。所以,Linux 为各个协议族提供了专门的 socket 地址结构体。
二、专用 socket 结构体
1. sockaddr_in
struct sockaddr_in {
sa_family_t sin_family; // 地址族
u_int16_t sin_port; // 端口号
struct in_addr sin_addr; // IPv4地址结构体
};
struct in_addr {
u_int32_t s_addr; // IPv4地址
};
2. sockaddr_in6
struct sockaddr_in6 {
sa_family_t sin6_family; // 地址族
u_int16_t sin6_port; // 端口号
struct in6_addr sin6_addr; // IPv6地址结构体
u_int32_t sin6_flowinfo; // 流信息
u_int32_t sin6_scope_id; // scope ID
};
struct in6_addr {
unsigned char sa_addr[16]; // IPv6地址
};
3. sockaddr_un
struct sockaddr_un {
sa_family_t sin_family; // 地址族
char sun_path[108]; // 文件路径名
};
所有专用 socket 地址(以及 sockaddr_storage)类型的变量在实际使用时都需要转化为通用 socket 地址类型 sockaddr,因为所有 socket 编程接口使用的地址参数的类型都是 sockaddr。
三、余音绕梁
1. socket 地址结构体大小的数据类型为:socklen_t
typedef uint32_t socklen_t;
2. 为何 socket 编程接口使用的地址参数的类型是 sockaddr?
因为 socket 编程接口需要处理 sockaddr_in、sockaddr_in6、sockaddr_un 等 socket 地址结构,为了满足这些需求,于是 socket 编程接口中的指针参数便指向通用 socket 地址结构。言下之意就是,我们只需知道 sockaddr 存在的唯一用途就是对指向特定于协议的 socket 地址结构体的指针执行强制类型转换。
3. sockaddr_storage 结构体往往用于事先不知道地址族的类型这一情况
/* 获取socket的地址族 */
int getFamily(int fd)
{
struct sockaddr_storage ss;
socklen_t len = sizeof(ss);
getsockname(fd, (struct sockaddr*)&ss, &len);
return ss.sa_family;
}
补:既然事先不知道要分配的 socket 地址结构体的类型,我们只能采用 sockaddr_storage 这个通用 socket 地址结构体,因为它能够承载系统支持的任何 socket 地址结构体。也就是说,它能够容纳下 sockaddr_in6 结构体的 26 字节的地址值,也能够容纳下 sockaddr_un 结构体的 108 字节的文件路径名。
Android NDK Socket(POSIX Socket Api)编程
socket简介
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
tcpsocket和udpsocket的具体实现
讲了这么久,终于要开始讲socket的具体实现了,iOS提供了Socket网络编程的接口CFSocket,不过这里使用BSD Socket。
tcp和udp的socket是有区别的,这里给出这两种的设计框架
基本TCP客户—服务器程序设计基本框架
基本UDP客户—服务器程序设计基本框架流程图
这里我们利用Linux C POSIX Socket API进行NDK Socket编程
AbstractEchoActivity
package com.apress.echo;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
/**
* 客户端和服务端的抽象父类 共同有一个启动按钮,显示日志的TextView,端口设置EditText
*
*/
public abstract class AbstractEchoActivity extends Activity implements
OnClickListener {
protected static final int TCP = 1;
protected static final int UDP = 2;
protected EditText editPort;// Port number
protected Button btnStart;// server button
protected ScrollView scrollLog;//
protected TextView tvLog;// log view
private final int layoutID;
public AbstractEchoActivity(int layoutID) {
this.layoutID = layoutID;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(layoutID);
editPort = (EditText) findViewById(R.id.port_edit);
btnStart = (Button) findViewById(R.id.start_button);
scrollLog = (ScrollView) findViewById(R.id.scroll_view);
tvLog = (TextView) findViewById(R.id.log_view);
btnStart.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v == btnStart) {
onStartButtonClicked();
} else {
Log.v("onClick", "onClick no done.");
}
}
/**
* 获取端口
*
* @return
*/
protected Integer getPort() {
Integer port;
try {
port = Integer.valueOf(editPort.getText().toString());
} catch (Exception e) {
e.printStackTrace();
port = null;
}
return port;
}
protected void logMessage(final String message) {
runOnUiThread(new Runnable() {
@Override
public void run() {
logMessageDirect(message);
}
});
}
protected void logMessageDirect(final String message) {
tvLog.append(message);
tvLog.append("\n");
scrollLog.fullScroll(View.FOCUS_DOWN);
}
protected abstract void onStartButtonClicked();
/**
* 这个thread抽象出onBackground()方法作为线程的执行方法,在启动前先设置控件状态为不可用,同时清空日志。执行完毕后设置控件可用。
*
*/
protected abstract class AbstractEchoTask extends Thread {
private final Handler handler;
public AbstractEchoTask() {
handler = new Handler();
}
protected void onPreExecute() {
btnStart.setEnabled(false);
// 清空日志
tvLog.setText("");
}
/*
*
*/
@Override
public synchronized void start() {
// 这里start是由主线程来调用的。调用之前先设置控件状态。
onPreExecute();
super.start();
}
@Override
public void run() {
// run是在新线程中运行的
onBackground();
// 用handler来修改控件
handler.post(new Runnable() {
@Override
public void run() {
onPostExecute();
}
});
}
/**
* 线程的执行体
*/
protected abstract void onBackground();
/**
*
*/
protected void onPostExecute() {
btnStart.setEnabled(true);
}
}
static {
System.loadLibrary("Echo");
}
}
客户端 EchoClientActivity
package com.apress.echo;
import android.os.Bundle;
import android.widget.EditText;
public class EchoClientActivity extends AbstractEchoActivity {
private EditText editIp;
private EditText editMessage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
editIp = (EditText) findViewById(R.id.ip_edit);
editMessage = (EditText) findViewById(R.id.message_edit);
}
public EchoClientActivity() {
super(R.layout.activity_echo_client);
}
@Override
protected void onStartButtonClicked() {
String ip = editIp.getText().toString();
Integer port = getPort();
String message = editMessage.getText().toString();
if (0 != ip.length() && port != null && (0 != message.length())) {
new ClientTask(ip, port, message).start();
}
}
private native void nativeStartTcpClient(String ip, int port, String message)
throws Exception;
private class ClientTask extends AbstractEchoTask {
private final String ip;
private final int port;
private final String message;
public ClientTask(String ip, int port, String message) {
this.ip = ip;
this.port = port;
this.message = message;
}
@Override
protected void onBackground() {
logMessage("Starting client");
try {
nativeStartTcpClient(ip, port, message);
} catch (Exception e) {
logMessage(e.getMessage());
}
logMessage("Client terminated.");
}
}
}
服务端SocketServer
EchoServerActivity
package com.apress.echo;
public class EchoServerActivity extends AbstractEchoActivity {
public EchoServerActivity() {
super(R.layout.activity_echo_server);
}
@Override
protected void onStartButtonClicked() {
Integer port = getPort();
if (port != null) {
new ServerTask(port, TCP).start();
} else {
logMessage("port error");
}
}
/**
* 启动tcp服务
*
* @param port
* @throws Exception
*/
private native void nativeStartTcpServer(int port) throws Exception;
/**
* 启动udp服务
*
* @param port
* @throws Exception
*/
private native void nativeStartUdpServer(int port) throws Exception;
private class ServerTask extends AbstractEchoTask {
private final int port;
private final int protocol;
/**
* @param port端口
* @param protocol
* 使用的协议
*/
public ServerTask(int port, int protocol) {
this.port = port;
this.protocol = protocol;
}
@Override
protected void onBackground() {
logMessage("Starting server.");
logMessage("server ip:" + Commons.getIpAddress());
try {
if (protocol == TCP) {
nativeStartTcpServer(port);
} else if (protocol == UDP) {
nativeStartUdpServer(port);
} else {
logMessage("protocol error.");
}
} catch (Exception e) {
logMessage(e.getMessage());
}
logMessage("Server terminated.");
}
}
}
清单文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.apress.echo"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<!-- 服务端app -->
<!-- <activity -->
<!-- android:name=".EchoServerActivity" -->
<!-- android:label="@string/title_activity_echo_server" -->
<!-- android:launchMode="singleTop" > -->
<!-- <intent-filter> -->
<!-- <action android:name="android.intent.action.MAIN" /> -->
<!-- <category android:name="android.intent.category.LAUNCHER" /> -->
<!-- </intent-filter> -->
<!-- </activity> -->
<!-- 客户端app -->
<activity
android:name=".EchoClientActivity"
android:label="@string/title_activity_echo_client"
android:launchMode="singleTop" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
实现NDK编程
native接口文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_apress_echo_EchoServerActivity */
#ifndef _Included_com_apress_echo_EchoServerActivity
#define _Included_com_apress_echo_EchoServerActivity
#ifdef __cplusplus
extern "C" {
#endif
#undef com_apress_echo_EchoServerActivity_TCP
#define com_apress_echo_EchoServerActivity_TCP 1L
#undef com_apress_echo_EchoServerActivity_UDP
#define com_apress_echo_EchoServerActivity_UDP 2L
/*
* Class: com_apress_echo_EchoServerActivity
* Method: nativeStartTcpServer
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_apress_echo_EchoServerActivity_nativeStartTcpServer
(JNIEnv *, jobject, jint);
/*
* Class: com_apress_echo_EchoServerActivity
* Method: nativeStartUdpServer
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_apress_echo_EchoServerActivity_nativeStartUdpServer
(JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
实现native Socket
SocketTool.cpp
#include <stdio.h>
#include <stdarg.h>
//errno
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
//sockaddr_un
#include <sys/un.h>
//htons,sockaddr_in
#include <netinet/in.h>
//inet_ntop
#include <arpa/inet.h>
//close,unlink
#include <unistd.h>
//offsetof
#include <stddef.h>
#ifndef __SOCKET_UTILS__
#define __SOCKET_UTILS_
//MAX log message length
#define MAX_LOG_MESSAGE_LENGTH 256
//MAX data buffer size
#define MAX_BUFFER_SIZE 80
//打印日志到java环境中
static void LogMessage(JNIEnv* env, jobject obj, const char* format, ...) {
//cache log method ID
static jmethodID methodID = NULL;
if (methodID == NULL) {
jclass clazz = env->GetObjectClass(obj);
methodID = env->GetMethodID(clazz, "logMessage",
"(Ljava/lang/String;)V");
env->DeleteLocalRef(clazz);
}
if (methodID != NULL) {
char buffer[MAX_BUFFER_SIZE];
//将可变参数输出到字符数组中
va_list ap;
va_start(ap, format);
vsnprintf(buffer, MAX_LOG_MESSAGE_LENGTH, format, ap);
va_end(ap);
//转换成java字符串
jstring message = env->NewStringUTF(buffer);
if (message != NULL) {
env->CallVoidMethod(obj, methodID, message);
env->DeleteLocalRef(message);
}
}
}
//通过异常类和异常信息抛出异常
static void ThrowException(JNIEnv* env, const char* className,
const char* message) {
jclass clazz = env->FindClass(className);
if (clazz != NULL) {
env->ThrowNew(clazz, message);
env->DeleteLocalRef(clazz);
}
}
//通过异常类和错误号抛出异常
static void ThrowErrnoException(JNIEnv* env, const char* className,
int errnum) {
char buffer[MAX_LOG_MESSAGE_LENGTH];
//通过错误号获得错误消息
if (-1 == strerror_r(errnum, buffer, MAX_LOG_MESSAGE_LENGTH)) {
strerror_r(errno, buffer, MAX_LOG_MESSAGE_LENGTH);
}
ThrowException(env, className, buffer);
}
//sock用到的一些公用方法
//创建一个socket:socket()
static int NewTcpSocket(JNIEnv* env, jobject obj) {
LogMessage(env, obj, "Constructing a new TCP socket...");
int tcpSocket = socket(PF_INET, SOCK_STREAM, 0);
if (-1 == tcpSocket) {
ThrowErrnoException(env, "java/io/IOException", errno);
}
return tcpSocket;
}
//绑定 bind()
static void BindSocketToPort(JNIEnv* env, jobject obj, int sd,
unsigned short port) {
struct sockaddr_in address;
//清空结构体
memset(&address, 0, sizeof(address));
address.sin_family = PF_INET;
//Bind to all address
address.sin_addr.s_addr = htonl(INADDR_ANY);
//Convert port to network byte order
address.sin_port = htons(port);
//Bind socket
LogMessage(env, obj, "Binding to port %hu.", port);
//sockaddr方便函数传递, sockaddr_in方便用户设定, 所以需要的时候在这2者之间进行转换
if (-1 == bind(sd, (struct sockaddr*) &address, sizeof(address))) {
ThrowErrnoException(env, "java/io/IOException", errno);
}
}
//返回当前socket绑定的端口
static unsigned short GetSocketPort(JNIEnv* env, jobject obj, int sd) {
unsigned short port = 0;
struct sockaddr_in address;
socklen_t addressLength = sizeof(address);
if (-1 == getsockname(sd, (struct sockaddr*) &address, &addressLength)) {
ThrowErrnoException(env, "java/io/IOException", errno);
} else {
port = ntohs(address.sin_port);
LogMessage(env, obj, "Binding to the random port %hu.", port);
}
return port;
}
//监听 listen()
static void ListenOnSocket(JNIEnv*env, jobject obj, int sd, int backlog) {
LogMessage(env, obj,
"Listening on socket with a baklog of %d pending connections.",
backlog);
//listen()用来等待参数s 的socket 连线. 参数backlog 指定同时能处理的最大连接要求,
//如果连接数目达此上限则client 端将收到ECONNREFUSED 的错误.
//Listen()并未开始接收连线, 只是设置socket 为listen 模式, 真正接收client 端连线的是accept().
//通常listen()会在socket(), bind()之后调用, 接着才调用accept().
if (-1 == listen(sd, backlog)) {
ThrowErrnoException(env, "java/io/IOException", errno);
}
}
//根据地址打印IP和端口
static void LogAddress(JNIEnv* env, jobject obj, const char* message,
const struct sockaddr_in* address) {
char ip[INET_ADDRSTRLEN];
if (NULL == inet_ntop(PF_INET, &(address->sin_addr), ip, INET_ADDRSTRLEN)) {
ThrowErrnoException(env, "java/io/IOException", errno);
} else {
unsigned short port = ntohs(address->sin_port);
LogMessage(env, obj, "%s %s:%hu", message, ip, port);
}
}
//accept()
static int AcceptOnSocket(JNIEnv* env, jobject obj, int sd) {
struct sockaddr_in address;
socklen_t addressLength = sizeof(address);
LogMessage(env, obj, "Waiting for a client connection...");
int clientSocket = accept(sd, (struct sockaddr*) &address, &addressLength);
if (-1 == clientSocket) {
ThrowErrnoException(env, "java/io/IOException", errno);
} else {
LogAddress(env, obj, "Client connection from ", &address);
}
return clientSocket;
}
//接收 recv()
static ssize_t ReceiveFromSocket(JNIEnv* env, jobject obj, int sd, char* buffer,
size_t bufferSize) {
LogMessage(env, obj, "Receiving from the socket... ");
ssize_t recvSize = recv(sd, buffer, bufferSize - 1, 0);
if (-1 == recvSize) {
ThrowErrnoException(env, "java/io/IOException", errno);
} else {
//字符串截断
buffer[recvSize] = NULL;
if (recvSize > 0) {
//接收成功,打印
LogMessage(env, obj, "Received %d bytes:%s", bufferSize, buffer);
} else {
LogMessage(env, obj, "Client disconnected.");
}
}
return recvSize;
}
//发送消息:send()
static ssize_t SendToSocket(JNIEnv *env, jobject obj, int sd,
const char* buffer, size_t bufferSize) {
LogMessage(env, obj, "Sending to the socket... ");
ssize_t sentSize = send(sd, buffer, bufferSize, 0);
if (-1 == sentSize) {
ThrowErrnoException(env, "java/io/IOException", errno);
} else {
if (sentSize > 0) {
LogMessage(env, obj, "Send %d bytes: %s", sentSize, buffer);
} else {
LogMessage(env, obj, "Client disconnected.");
}
}
return sentSize;
}
//链接到服务器 connect()
static void ConnectToAddress(JNIEnv*env, jobject obj, int sd, const char*ip,
unsigned short port) {
LogMessage(env, obj, "Connecting to %s:%hu...", ip, port);
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_family = PF_INET;
//转换ip
if (0 == inet_aton(ip, &(address.sin_addr))) {
ThrowErrnoException(env, "java/io/IOException", errno);
} else {
address.sin_port = htons(port);
}
if (-1 == connect(sd, (const sockaddr*) &address, sizeof(address))) {
ThrowErrnoException(env, "java/io/IOException", errno);
} else {
LogMessage(env, obj, "Connected.");
}
}
//----------------udp
//创建udp socket
static int NewUdpSocket(JNIEnv* env, jobject obj) {
LogMessage(env, obj, "Constructing a new UDP socket...");
int udpSocket = socket(PF_INET, SOCK_DGRAM, 0);
if (-1 == udpSocket) {
ThrowErrnoException(env, "java/io/IOException", errno);
}
return udpSocket;
}
#endif __SOCKET_UTILS_
实现Native 接口
#include <jni.h>
#include "com_apress_echo_EchoServerActivity.h"
#include "com_apress_echo_EchoClientActivity.h"
#include "SocketTool.cpp"
//服务端:启动监听
//流程:socket()->listen()->accept()->recv()->send()_close()
void JNICALL Java_com_apress_echo_EchoServerActivity_nativeStartTcpServer(
JNIEnv *env, jobject obj, jint port) {
int serverSocket = NewTcpSocket(env, obj);
if (NULL == env->ExceptionOccurred()) {
//绑定
BindSocketToPort(env, obj, serverSocket, (unsigned short) port);
if (NULL != env->ExceptionOccurred()) {
goto exit;
}
//如果端口是0,打印出当前随机分配的端口
if (0 == port) {
GetSocketPort(env, obj, serverSocket);
if (NULL != env->ExceptionOccurred()) {
goto exit;
}
}
//监听 链接4
ListenOnSocket(env, obj, serverSocket, 4);
if (NULL != env->ExceptionOccurred()) {
goto exit;
}
//
int clientSocket = AcceptOnSocket(env, obj, serverSocket);
if (NULL != env->ExceptionOccurred()) {
goto exit;
}
char buffer[MAX_BUFFER_SIZE];
ssize_t recvSize;
ssize_t sentSize;
while (1) {
//接收
recvSize = ReceiveFromSocket(env, obj, clientSocket, buffer,
MAX_BUFFER_SIZE);
if ((0 == recvSize) || (NULL != env->ExceptionOccurred())) {
break;
}
//发送
sentSize = SendToSocket(env, obj, clientSocket, buffer,
(size_t) recvSize);
if ((0 == sentSize) || (NULL != env->ExceptionOccurred())) {
break;
}
}
//close the client socket
close(clientSocket);
}
exit: if (serverSocket > 0) {
close(serverSocket);
}
}
//客户端:连接
void JNICALL Java_com_apress_echo_EchoClientActivity_nativeStartTcpClient(
JNIEnv *env, jobject obj, jstring ip, jint port, jstring message) {
int clientSocket = NewTcpSocket(env, obj);
if (NULL == env->ExceptionOccurred()) {
const char* ipAddress = env->GetStringUTFChars(ip, NULL);
if (NULL == ipAddress) {
goto exit;
}
ConnectToAddress(env, obj, clientSocket, ipAddress,
(unsigned short) port);
//释放ip
env->ReleaseStringUTFChars(ip, ipAddress);
//connect exception check
if (NULL != env->ExceptionOccurred()) {
goto exit;
}
const char* messageText = env->GetStringUTFChars(message, NULL);
if (NULL == messageText) {
goto exit;
}
//这里的size不用release??
jsize messageSize = env->GetStringUTFLength(message);
SendToSocket(env, obj, clientSocket, messageText, messageSize);
//
env->ReleaseStringUTFChars(message, messageText);
if (NULL != env->ExceptionOccurred()) {
goto exit;
}
char buffer[MAX_BUFFER_SIZE];
ReceiveFromSocket(env, obj, clientSocket, buffer, MAX_BUFFER_SIZE);
}
exit: if (clientSocket > -1) {
close(clientSocket);
}
}
//启动udp服务端
void JNICALL Java_com_apress_echo_EchoServerActivity_nativeStartUdpServer(
JNIEnv *, jobject, jint) {
}
boost::asio 序列16: tcp::socket 和 udp::socket & socket_basic
reactive_socket_service_base execution_context_service_base <reative_socket_service<Porotocol>>
\ |
reactive_socket_service<Protocol>
|
socket_basic io_object_impl<Protocol, Executor> (成员变量) system_executor
\ | |
basic_socket<Protocol, Executor> 其中 Exector =====> executor
/ \
basic_stream_socket<tcp> basic_datagram_socket<udp>
socket_basic:定义socket共用的一些数据类型和选项
shutdown_type | shutdown_receive = BOOST_ASIO_OS_DEF(SHUT_RD), shutdown_send = BOOST_ASIO_OS_DEF(SHUT_WR), shutdown_both = BOOST_ASIO_OS_DEF(SHUT_RDWR) |
message_flags | BOOST_ASIO_STATIC_CONSTANT(int, message_peek = BOOST_ASIO_OS_DEF(MSG_PEEK)); BOOST_ASIO_STATIC_CONSTANT(int, message_out_of_band = BOOST_ASIO_OS_DEF(MSG_OOB)); BOOST_ASIO_STATIC_CONSTANT(int, message_do_not_route = BOOST_ASIO_OS_DEF(MSG_DONTROUTE)); BOOST_ASIO_STATIC_CONSTANT(int, message_end_of_record = BOOST_ASIO_OS_DEF(MSG_EOR)); |
wait_type | enum wait_type /// Wait for a socket to become ready to write. /// Wait for a socket to have error conditions pending. |
SOL_SOCKET/SO_BROADCAST | typedef boost::asio::detail::socket_option::boolean< BOOST_ASIO_OS_DEF(SOL_SOCKET), BOOST_ASIO_OS_DEF(SO_BROADCAST)> broadcast; |
SOL_SOCKET/SO_DEBUG | typedef boost::asio::detail::socket_option::boolean< BOOST_ASIO_OS_DEF(SOL_SOCKET), BOOST_ASIO_OS_DEF(SO_DEBUG)> debug; |
SOL_SOCKET/SO_DONTROUTE | typedef boost::asio::detail::socket_option::boolean< BOOST_ASIO_OS_DEF(SOL_SOCKET), BOOST_ASIO_OS_DEF(SO_DONTROUTE)> do_not_route; |
SOL_SOCKET/SO_KEEPALIVE | typedef boost::asio::detail::socket_option::boolean< BOOST_ASIO_OS_DEF(SOL_SOCKET), BOOST_ASIO_OS_DEF(SO_KEEPALIVE)> keep_alive; |
SOL_SOCKET/SO_SNDBUF | typedef boost::asio::detail::socket_option::integer< BOOST_ASIO_OS_DEF(SOL_SOCKET), BOOST_ASIO_OS_DEF(SO_SNDBUF)> send_buffer_size; |
SOL_SOCKET/SO_SNDLOWAT | typedef boost::asio::detail::socket_option::integer< BOOST_ASIO_OS_DEF(SOL_SOCKET), BOOST_ASIO_OS_DEF(SO_SNDLOWAT)> send_low_watermark; |
SOL_SOCKET/SO_RCVBUF | typedef boost::asio::detail::socket_option::integer< BOOST_ASIO_OS_DEF(SOL_SOCKET), BOOST_ASIO_OS_DEF(SO_RCVBUF)> receive_buffer_size; |
SOL_SOCKET/SO_RCVLOWAT | typedef boost::asio::detail::socket_option::integer< BOOST_ASIO_OS_DEF(SOL_SOCKET), BOOST_ASIO_OS_DEF(SO_RCVLOWAT)> receive_low_watermark; |
SOL_SOCKET/SO_REUSEADDR | typedef boost::asio::detail::socket_option::boolean< BOOST_ASIO_OS_DEF(SOL_SOCKET), BOOST_ASIO_OS_DEF(SO_REUSEADDR)> reuse_address; |
SOL_SOCKET/SO_LINGER | typedef boost::asio::detail::socket_option::linger< BOOST_ASIO_OS_DEF(SOL_SOCKET), BOOST_ASIO_OS_DEF(SO_LINGER)> linger; |
SOL_SOCKET/SO_OOBINLINE | typedef boost::asio::detail::socket_option::boolean< BOOST_ASIO_OS_DEF(SOL_SOCKET), BOOST_ASIO_OS_DEF(SO_OOBINLINE)> out_of_band_inline; |
Socket option to report aborted connections on accept | typedef boost::asio::detail::socket_option::boolean< boost::asio::detail::custom_socket_option_level, boost::asio::detail::enable_connection_aborted_option> enable_connection_aborted; |
the FIONREAD IO control command | typedef boost::asio::detail::io_control::bytes_readable bytes_readable; |
- 点赞
- 收藏
- 分享
-
- 文章举报

C# Socket 系列一 简单的创建 socket 的监听
socket 的应用场景,在快速,稳定,保持长连接的数据传输代码。Http 也是 socket 封装出来的,基于一次请求一次回复,然后断开的 socket 连接封装。
比如我们常见的游戏服务器,目前的很火的物联网服务器,都需要开启 socket 服务器去监听实时传输的数据。
那么我们如何实现 socket 的监听呢。说到这里,我们需要知道,socket 的监听分为 tcp 和 udp 两种形式,但是 tcp 其实是 udp 封装而来的,可看做可靠的 udp 传输,基于 udp 的定向传输,收到消息回复发送方收到消息。等验证,来实现 tcp 的数据传输,所以一般我们 tcp 的传输相对 udp 稍微慢一点。
我们先将一下 socket 的 tcp 状态创建一个 TCPListener 类
/// <summary>
/// 建立TCP通信监听服务
/// </summary>
internal class TCPListener
{
private IPEndPoint _IP;
private Socket _Listeners;
private volatile bool IsInit = false;
private List<TSocketBase> sockets = new List<TSocketBase>();
/// <summary>
/// 初始化服务器
/// </summary>
public TCPListener(string ip = "0.0.0.0", int port = 9527)
{
IsInit = true;
IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(ip), port);
this._IP = localEP;
try
{
Console.WriteLine(string.Format("Listen Tcp -> {0}:{1} ", ip, port));
this._Listeners = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this._Listeners.Bind(this._IP);
this._Listeners.Listen(5000);
SocketAsyncEventArgs sea = new SocketAsyncEventArgs();
sea.Completed += new EventHandler<SocketAsyncEventArgs>(this.AcceptAsync_Async);
this.AcceptAsync(sea);
}
catch (Exception ex)
{
Console.WriteLine(ex);
this.Dispose();
}
}
private void AcceptAsync(SocketAsyncEventArgs sae)
{
if (IsInit)
{
if (!this._Listeners.AcceptAsync(sae))
{
AcceptAsync_Async(this, sae);
}
}
else
{
if (sae != null)
{
sae.Dispose();
}
}
}
private void AcceptAsync_Async(object sender, SocketAsyncEventArgs sae)
{
if (sae.SocketError == SocketError.Success)
{
var socket = new TSocketClient(sae.AcceptSocket);
sockets.Add(socket);
Console.WriteLine("Remote Socket LocalEndPoint:" + sae.AcceptSocket.LocalEndPoint + " RemoteEndPoint:" +
sae.AcceptSocket.RemoteEndPoint.ToString());
}
sae.AcceptSocket = null;
if (IsInit)
{
this._Listeners.AcceptAsync(sae);
}
else
{
sae.Dispose();
}
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
if (IsInit)
{
IsInit = false;
this.Dispose(true);
GC.SuppressFinalize(this);
}
}
/// <summary>
/// 释放所占用的资源
/// </summary>
/// <param name="flag1"></param>
protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool flag1)
{
if (flag1)
{
if (_Listeners != null)
{
try
{
Console.WriteLine(string.Format("Stop Listener Tcp -> {0}:{1} ", this.IP.Address.ToString(),
this.IP.Port));
_Listeners.Close();
_Listeners.Dispose();
}
catch
{
}
}
}
}
/// <summary>
/// 获取绑定终结点
/// </summary>
public IPEndPoint IP
{
get { return this._IP; }
}
}
主要两点我们 socket 的初始化代码 new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 初始化的类型是基于 tcp。
还有就是我们绑定 ip 地址,过去很多人 socket 的 bind 地址习惯写成 127.0.0.1(测试环境)或者读取网卡信息,读取 ip 地址,这样麻烦,代码要写很多,切不符合多网卡多线路实际环境。我们用 0.0.0.0 是表示开启 ipv4 的所有线路监听,包括你的多路网卡,以及 127.0.0.1
class Program
{
static void Main(string[] args)
{
TCPListener tcp = new TCPListener();
Console.ReadLine();
}
}
我们运行看一下效果
接下来我们使用 telnet 测试一下
开启 telnet
然后打开 cmd
输入 telnet 127.0.0.1 9527
我们看到收到了一个连接
关于Python -- socket 实现服务器之间的通信和python socket 服务端的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于2. socket 结构体 —— 表示 socket 地址、Android NDK Socket(POSIX Socket Api)编程、boost::asio 序列16: tcp::socket 和 udp::socket & socket_basic、C# Socket 系列一 简单的创建 socket 的监听等相关内容,可以在本站寻找。
本文标签: