GVKun编程网logo

Python -- socket 实现服务器之间的通信(python socket 服务端)

5

在这里,我们将给大家分享关于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 服务端)

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 地址

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)编程

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

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 read.
    wait_read,


    /// Wait for a socket to become ready to write.
    wait_write,

    /// Wait for a socket to have error conditions pending.
    wait_error
  };

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;

       

  • 点赞
  • 收藏
  • 分享
    • 文章举报
Hit_HSW
发布了203 篇原创文章 · 获赞 109 · 访问量 32万+
私信 关注

C# Socket 系列一 简单的创建 socket 的监听

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 的监听等相关内容,可以在本站寻找。

本文标签: