c/c++自定义通讯协议(TCP/UDP)

2023-05-16

 前言:TCP与UDP是大家耳熟能详的两种传输层通信协议,本质区别在于传输控制策略不相同:使用TCP协议,可以保证传输层数据包能够有序地被接受方接收到,依赖其内部一系列复杂的机制,比如握手协商,ACK确认,超时重传,拥塞控制等; 而UDP基本上没有额外的控制策略,所以接收方能不能接收到传输层数据包是无法保证的。正是因为不能保证每一个数据包有序到达,UDP数据包与包之间,必须是相互独立的,每一个都应该是有意义的可以被解析出完整应用层报文的数据块,因此UDP又被称为面向(单个)报文的协议;而每一个TCP数据包则可以是应用层报文的某一部分,多个有序的数据包就可以拼接出完整的应用层报文,因此TCP被称作面向流的协议。

        我们知道,网络层(即IP层)数据包是有最大长度MTU限制的(因为物理层大包丢包概率很高),所以不论是发送UDP包还是TCP包,如果突破了该限制,数据包将会被IP层切片,接收方的IP层会根据分片id对传输层数据片段的进行重组,分片和重组都会占用cpu和内存资源,严重降低通讯效率。如果通信双方采用TCP通信,在握手连接阶段会协商MSS,即一个TCP最大包含的数据量,有了MSS约定,TCP层交付给IP层的数据包就不会超过IP层的MTU限制,也就是说分片工作在TCP传输层完成。而使用UDP时,一旦UDP数据包被IP层分片,接收方大概率是无法组成完整的UDP数据包的,因为就算某些片段丢失了,发送方也不会对整个UDP包进行重发,因此UDP通讯是禁止IP层分片的(一旦超过MTU,会直接丢弃)。

        所以,如果我们的应用需要传输大的数据包,就没办法使用单纯的UDP协议传输了,除非基于UDP在应用层自行实现一种类似于TCP内部的分片控制机制,完成数据的可靠传输。

一. 自定义网络协议

        假设我们的应用层报文一般比较小,不超过底层的MTU限制,这样一来,我们既可以使用TCP,也可以使用UDP来进行传输。下面是一个最简单的协议定义示例,包含消息头定义和消息体定义:

struct SmHeader
{
    int m_length;// 消息头长度+消息体长度
    int m_request_type;//请求类型
    int m_reply_type;//响应类型
    int m_body_type;//消息体类型
};

struct Body1
{
  int m_int_b1;
  float m_float_b1;
  char m_char_b1;
  char m_reserve[3];//字节对齐
};

struct Body2
{
  int m_int_array_b2[12];
  float m_float_array_b2[15];
};

struct Body3
{
  char m_char_array_b3[512];
};

struct Body1Assemble
{
    int m_count;
    Body1 m_b1[0];
};

union SmBody
{
  Body1 b1;
  Body2 b2;  
  Body3 b3;
  Body1Assemble b4;
};

struct SmMessage
{
  SmHeader head;
  SmBody body;
};

由上述协议的定义可知,这样一个完整的消息最少有sizeof(SmHeader) = 16字节(一个消息可以没有消息体,比如PING/PONG心跳包,只有消息头即可),由于Body1Assemble类型的数据体长度不确定,因此用TCP的话,可以传递很长的消息。

二. 消息的接收

由于发送方的发送速率与接收方的接收速率很难匹配,在接收方的接收缓冲区内会形成数据包累积,所以我们需要上述定义的消息头协助完成数据包的提取,有效处理接收端粘包问题。

char buffer[1024];//在应用层定义一个数据缓冲区,至少能够放得下最大的数据包
int bfsize = 1024;//缓冲区长度
int legacy_bytes = 0;//上一次解析处理剩余的字节数
bool skip_recv = false;//是否可以直接使用上一次剩余数据解析出完整数据

while(1)
{
    int current_size = 0;
    if(!skip_recv)
    {
        current_size = recv(fd,buffer+legacy_bytes,bfsize-legacy_bytes);
        if(current_size<=0)
            break;
        current_size += legacy_bytes;
        legacy_bytes = 0;
    }
    else
    {
        current_size = legacy_bytes;
        legacy_bytes = 0;
        skip_recv = false;
    }
    int expected_size = -1;
    if(current_size>=sizeof(SmHeader))
    {
        const SmHeader* head = (SmHeader*)buffer;
        expected_size = head->m_length;
        if(expected_size<=0 || expected_size>bfsize)
        {
            printf("Invalid message header or buffer insufficient.\r\n");
        }
        else if(current_size>=expected_size)
        {
            /***********process a complete message******/
            // 解析buffer中的消息;
            SmMessage* msg = (SmMessage*)buffer;
            // 处理消息 balabala...
            if(head->m_body_type == BODY1ASSEMBLE)
            {
                struct Body1* data;
                for(int i=0;i<msg->body.b4.m_count;i++)
                {
                    data = &msg->body.b4.m_b1[i];
                    //printf("data id: %d, data member1: %d",i,data->m_int_b1);
                }
            }
            /********************end********************/
            if(current_size>expected_size)
            {
                legacy_bytes = current_size-expected_size;
                memmove(buffer,buffer+expected_size,legacy_bytes);
                if(legacy_bytes>=sizeof(SmHeader))
                {
                    const SmHeader* next_head = (SmHeader*)buffer;
                    int next_expected_size = next_head->m_length;
                    if(next_expected_size>0)
                        skip_recv = legacy_bytes>=next_expected_size;
                }
            }
            else
            {
                legacy_bytes = 0;
            }
        }
        else
        {
            legacy_bytes = current_size;
            printf("Incompleted message.\r\n");
        }
    }
    else
    {
        legacy_bytes = current_size;
    }
}

三. python版本的TCP/UDP报文收发

import socket
import struct
import copy
import threading

buffer = bytearray()
buffer_size = 0
skip_recv = False  #之前遗留的数据是否可以直接解出完整的数据包
legacy_Bytes_count=0  #之前遗留的数据长度


def case_reply_1():
    fmt = '5ifc'
    return fmt


def case_reply_2():
    fmt = '16i15f'
    return fmt


def case_reply_3():
    fmt = '4i512c'
    return fmt

def case_reply_4():
    fmt = '5i'
    return fmt


def case_reply_default():
    print("No implementation for processing this type of message.")
    return None


def case_request_0():
    reqType = 0
    repType = -1
    msgType = 1
    """
        struct RequestBody1
        {
            char name[8];
            int idx;
            float account;
        }
    """
    name = b"Jhon\n\n\n\n"
    idx = 101
    account = 1361.58
    length = 16 + len(name) + 4 + 4
    fmt = "4i8sif" 
    st = struct.Struct(fmt)
    data = st.pack(length,reqType,repType,msgType,name,idx,account)
    return data

def case_request_1():
    reqType = 1
    repType = -1
    msgType = 2
    """
        struct RequestBody2
        {
            bool female;
            char reserve[3];
            int age;
        }
    """
    female = True
    age = 25
    length = 16 + 8
    fmt = "4i?3xi"
    st = struct.Struct(fmt)
    data = st.pack(length,reqType,repType,msgType,female,age)
    return data


def case_request_default():
    data = bytes()
    return data


def unpack_messages(num:int, msg:bytearray)->tuple:
    numbers = {
        0:   case_reply_1,
        1:   case_reply_2,
        2:   case_reply_3,
        3:   case_reply_4
    }
    method = numbers.get(num, case_reply_default)
    if method:
        fmt = method()
        cnt = 0
        if num == 3:
            cnt = struct.calcsize(fmt)
            if len(msg) < cnt:
                return tuple()
            assemble = struct.unpack(fmt,msg[:cnt])
            extra_fmt = 'ifc3s'
            fmt += assemble[4]*extra_fmt
        if fmt:
            cnt = struct.calcsize(fmt)
        # python的struct字节对齐和c/c++中的字节对齐处理方式不同,可能由于字节对齐的问题解析出错,
        # 此时可以根据msg内部字节排列情况,逐个解析
        if cnt != len(msg):
            print("message parse error.")
            return tuple()
        
        structured_msg = struct.unpack(fmt,msg)
        return structured_msg
    else:
        return tuple()


def connect_to_motion_server(IP:str, PORT:int, LPORT:int=-1):
    client = None
    try:
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        if LPORT > 0:
            client.bind(('',LPORT))#客户端绑定本地端口
        client.connect((IP, PORT))
        print('Connect Success!')
    except socket.error as msg:
        print(msg)
        client.close()
        client = None
    return client


def pack_messages(num:int)->bytes:
    numbers = {
        0:   case_request_0,
        1:   case_request_1
    }
    method = numbers.get(num, case_request_default)
    if method:
        data = method()
        return data
    else:
        return bytes()


def send_message_to_server(_client, msgtyp:int, scktype:str="TCP", address: tuple = None):
    data = pack_messages(msgtyp)
    size = send_to_socket(_client, data, scktype, address)
    return size


def send_to_socket(_client, data: bytes, sock_type: str = 'TCP', address: tuple = None):
    size = 0
    if len(data) < 1:
        return size
    if sock_type == 'TCP':
        size = _client.send(data)
    elif sock_type == 'UDP':
        if address:
            size = _client.sendto(data, address)
        else:
            pass
    return size

    
def process_message(c):
    while True:
        leng, packet = get_reply_from_server(c)
        if leng < 0:
            break
        print("receive message: ",packet)
    return


def get_reply_from_server(c) -> (int, tuple):
    global buffer, buffer_size, skip_recv, legacy_Bytes_count
    pkt_len, pkt, skip_recv,legacy_Bytes_count = read_from_socket(c, buffer,buffer_size,legacy_Bytes_count,skip_recv,
                                                                    min_size=16,min_fmt='iiii')
    return pkt_len, pkt


def read_from_socket(_client, recv_buffer:bytearray, bfsize:int, legacy_size:int, skip_flag:bool, sock_type:str='TCP', min_size=4,min_fmt='i'):
    assert bfsize > 0 and legacy_size >= 0, "please initialize recv_buffer first."
    complete_pkg = None
    header = tuple()
    if not skip_flag:
        extra_to_read = bfsize - legacy_size
        recv_buffer[legacy_size:bfsize] = b'\x00'
        tmp = None
        if sock_type == 'TCP':
            tmp = _client.recv(extra_to_read)
        else:
            tmp, addr = _client.recvfrom(extra_to_read)
        csize = len(tmp)
        if csize <= 0:
            return -1, None, False, 0 # socket disconnect
        csize += legacy_size
        recv_buffer[legacy_size:csize] = tmp
        legacy_size = 0
    else:
        csize = legacy_size
        legacy_size = 0
        skip_flag = False

    if csize >= min_size:
        hst = struct.Struct(min_fmt)
        prefix = hst.unpack(recv_buffer[:min_size])
        if len(prefix) == 0 or prefix[0] <= 0:
            print("Invalid message header, drop it.")
        elif csize >= prefix[0]:
            esize = prefix[0]
            complete_pkg = copy.deepcopy(recv_buffer[:esize])
            if csize > esize:
                legacy_size = csize - esize
                recv_buffer[:legacy_size] = recv_buffer[esize:csize]
                if legacy_size >= min_size:
                    next_prefix = hst.unpack(recv_buffer[:min_size])
                    skip_flag = legacy_size >= next_prefix[0]
            else:
                legacy_size = 0
            header = prefix
        else:
            legacy_size = csize
            print("Incomplete message. Ignore it.")
    else:
        legacy_size = csize
    if header and complete_pkg:
        # header = (length, reqType, resType, msgType)
        st_data = unpack_messages(header[3], complete_pkg)
        return len(complete_pkg), st_data, skip_flag, legacy_size
    else:
        return 0, None, skip_flag, legacy_size


def communicate_buffer_init(size):
    global buffer,buffer_size, skip_recv, legacy_Bytes_count
    buffer_size = size
    skip_recv = False
    legacy_Bytes_count = 0
    buffer = buffer.zfill(buffer_size)


if __name__ == '__main__':
    communicate_buffer_init(1024)
    cli = connect_to_motion_server("127.0.0.1", 12289)
    _inputText = 'c'
    if cli:
        motion_thread = threading.Thread(target=process_message, args=(cli,))
        motion_thread.start()
        while _inputText!= 'q':
            _inputText = input(r'please type a request:').lower()
            sz = 0
            if _inputText == '0':
                sz = send_message_to_server(cli,0)
            if _inputText == '1':
                sz = send_message_to_server(cli,1)
            if sz < 0:
                break
            
        motion_thread.join()
    

四.补充一个c++实现的UDP收发类

/***************************************udpclient.h******************************/

#ifndef UDP_CLIENT_H
#define UDP_CLIENT_H
#include <winsock.h>

class Clientudp
{
public:
	Clientudp();
	bool InitializeClient(const char* ip, int local_port, int remote_port = -1);
	void SetReadTimeout(int timeout_ms);
	int PushToWriteBuffer(const char* msg, unsigned int size);
	int PullFromReadBuffer(char* msg, unsigned int size);
	bool GetClientStatus();
	~Clientudp();
private:
	int m_fd_;
	int m_read_timeout = -1;
	bool m_socket_avaliable_ = false;
	sockaddr_in m_remote_addr_;
	int m_remote_port_ = -1;
	sockaddr_in m_bind_addr_;
};

#endif



/*************************************udpclient.cpp***************************/

#include <iostream>
#include "UdpClient.h"

Clientudp::Clientudp()
{}

Clientudp::~Clientudp()
{
    if(m_socket_avaliable_)
        closesocket(m_fd_);
}

void Clientudp::SetReadTimeout(int timeout_ms)
{
	m_read_timeout = timeout_ms;
}

bool Clientudp::InitializeClient(const char* ip, int local_port, int remote_port)
{
	if (m_socket_avaliable_)
	{
		closesocket(m_fd_);
		m_socket_avaliable_ = false;
	}
	if ((m_fd_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
	{
		// create socket failed.
		m_socket_avaliable_ = false;
		return m_socket_avaliable_;
	}
	if (m_read_timeout > 0)
	{
		struct timeval read_timeout = { m_read_timeout, 0 };
		setsockopt(m_fd_, SOL_SOCKET, SO_RCVTIMEO, (char *)&read_timeout, sizeof(struct timeval));
	}
	memset(&m_remote_addr_, 0, sizeof(m_remote_addr_));
	m_remote_addr_.sin_family = AF_INET;
	m_remote_addr_.sin_addr.s_addr = inet_addr(ip);
	m_remote_addr_.sin_port = htons(remote_port);
	m_remote_port_ = remote_port;
	memset(&m_bind_addr_, 0, sizeof(m_bind_addr_));
	m_bind_addr_.sin_family = AF_INET;
	m_bind_addr_.sin_addr.s_addr = htonl(INADDR_ANY);
	m_bind_addr_.sin_port = htons(local_port);

	if (bind(m_fd_, (SOCKADDR*)&m_bind_addr_, sizeof(m_bind_addr_)) == SOCKET_ERROR)
	{
		int error_code = WSAGetLastError();
		if (error_code == WSAEADDRINUSE)
			printf("The port %d on this machine has been Occqupied.\n", local_port);
		printf("Bind Client to Fixed address Failed, Then you should settle a correct remote port and send data to remote machine before recv.\n");
		if (m_remote_port_ <= 0)
		{
			printf("InitializeClient Failed, Remote port = %d seems not to be a valid port number.\n", m_remote_port_);
			closesocket(m_fd_);
			m_socket_avaliable_ = false;
			return false;
		}
	}
	m_socket_avaliable_ = true;
	return m_socket_avaliable_;
}

int Clientudp::PushToWriteBuffer(const char* msg, unsigned int size)
{
	if (!m_socket_avaliable_)
	{
		return -1;
	}
	int ret = sendto(m_fd_, msg, size, 0, (sockaddr*)&m_remote_addr_, sizeof(m_remote_addr_));
	if (ret == SOCKET_ERROR)
	{
		int error_code = WSAGetLastError();
		//printf("error_code: %d\n",error_code);
		if (m_remote_port_ < 0 && error_code == WSAEINVAL)
		{
			printf("You should recv data from peer firstly before send.\n");
			return 0;
		}
		closesocket(m_fd_);
		m_socket_avaliable_ = false;
		m_remote_port_ = -1;
		return -1;
	}
	else
	{
		return ret;
	}
}

int Clientudp::PullFromReadBuffer(char* msg, unsigned int size)
{
	if (!m_socket_avaliable_)
	{
		return -1;
	}
	int addr_len = sizeof(m_remote_addr_);
	int ret = recvfrom(m_fd_, msg, size, 0, (sockaddr*)&m_remote_addr_, &addr_len);
	if (ret == SOCKET_ERROR)
	{
		int error_code = WSAGetLastError();
		if (error_code == WSAEMSGSIZE || error_code == WSAEINTR || error_code == WSAETIMEDOUT || error_code == WSAEWOULDBLOCK)
		{
            if(error_code == WSAEMSGSIZE)//datagram is too large to put into msg buffer.
                memset(msg,0x00,size);
			return 0;
		}
		else
		{
			closesocket(m_fd_);
			m_socket_avaliable_ = false;
			return -1;
		}
	}
	else
	{
		if (m_remote_port_ < 0)
		{
			int asize;
			getsockname(m_fd_, (sockaddr*)&m_remote_addr_, &asize);
			m_remote_port_ = ntohs(m_remote_addr_.sin_port);
		}
		return ret;
	}
}

bool Clientudp::GetClientStatus()
{
	return m_socket_avaliable_;
}

udp测试:

#include <iostream>
#include "udpclient.h"

int main()
{
    WSADATA ws
    WSAStartup(MAKEWORD(2,2),&ws);
    Clientudp udp;
    udp.InitializeClient("127.0.0.1", 1234, -1);
    char RxBuffer[1024];
    char TxBuffer[1024];
    memset(RxBuffer,0x00,1024);
    memset(TxBuffer,0x00,1024);
    /*如果remote_port为-1,则目的端口号未知,只能先收再发。如果知道对方端口号,则收发顺序可随便*/
    int readLen= udp.PullFromReadBuffer(RxBuffer,1024);
    printf("Read message length: %d",readLen);
    sprintf_s(TxBuffer,1023,"hello world");
    udp.PushToWriteBuffer(TxBuffer,1024);
    getchar();
    WSACleanup();   
    return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c/c++自定义通讯协议(TCP/UDP) 的相关文章

  • 分配 TCP/IP 端口供内部应用程序使用

    我编写了一个由 Windows 服务托管的 WCF 服务 它需要侦听已知的 TCP IP 端口 我可以在什么范围内安全地分配端口供我的组织内使用 该端口将嵌入到服务和使用该服务的客户端的配置文件中 端口 0 1023 是众所周知的端口 由
  • 触发“对等方重置连接”

    我想测试当发生 对等方重置连接 错误时我们的应用程序 嵌入式 ftp 服务器 中发生的日志记录 这个帖子 https stackoverflow com questions 1434451 connection reset by peer很
  • 序列化是通过套接字发送数据的最佳选择吗?

    有人告诉我 序列化不是通过套接字发送数据的最佳方法 但他们说他们在一本书上读过一次 并且不确定更好的方法 因为他们以前没有真正做过网络 那么序列化是最好的方法还是有更好的方法 如果这有很大的不同的话 这也是一个游戏 通过搜索有关通过它发送对
  • 为什么我收到的数据包数据大小大于mss?

    我在两台 PC 上使用 ifconfig ethX mtu 300 修改了 MTU 并使用 netperf 测试网络 我用 WireShark 嗅探了 SYN 数据包中的 MSS 260 但我得到了一些大于 260 的数据包 为什么 嗅探器
  • 如果其中一台机器死机,TCP 连接如何终止?

    如果两个主机 A 和 B 之间建立了 TCP 连接 假设主机 A 已向主机 B 发送了 5 个八位字节 然后主机 B 崩溃了 由于未知原因 主机 A 将等待确认 但如果没有收到确认 将重新发送八位字节并减小发送者窗口大小 这将重复几次 直到
  • 中断 Select 以添加另一个要在 Python 中监视的套接字

    我正在 Windows XP 应用程序中使用 TCP 实现点对点 IPC 我正在使用select and socketPython 2 6 6 中的模块 我有三个 TCP 线程 一个读取线程通常会阻塞select 一个通常等待事件的写入线程
  • 如何将udp发送到udp node.js服务器?

    我对此很陌生 所以我真的不知道我在做什么 但我已经设置了一个 node js udp 服务器 我想从客户端 来自网站 向它发送一个数据包 但我不知道如何在 javascript 中做到这一点 或者是否可能 我不是在研究如何从 Node js
  • 如何使用 Nmap 检索 TCP 和 UDP 端口? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我需要在使用 Nmap 的同一扫描中以尽可能最快的方式检索 TCP 和 UDP 端口 我会尽力解释得更好 如果我使用最常用的命令 nmap 192 1
  • 将 C++ TCP/IP 应用程序从 IPv4 转换为 IPv6。难的?值得这么麻烦吗?

    多年来 我使用 WinSock 为 Windows 开发了少量 C 服务器 客户端应用程序 路由器 Web 邮件 FTP 服务器等 等等 我开始越来越多地考虑创建这些应用程序的 IPv6 版本 当然 同时也保留原始的 IPv4 版本 问题
  • Apache HttpClient TCP Keep-Alive(套接字保持活动)

    我的 http 请求需要太多时间才能被服务器处理 大约 5 分钟 由于连接闲置 5 分钟 代理服务器将关闭连接 我正在尝试在 Apache DefaultHttpClient 中使用 TCP Keep Alive 来使连接长时间处于活动状态
  • 如何知道哪个本地应用程序连接到我的套接字(Windows)

    我有一个绑定到某个 TCP 端口的 Windows 服务 该端口用于我的应用程序之间的 IPC 有没有一种编程 WinAPI WinSocket 等 方法可以知道哪个应用程序连接到我的端口 即在我的 Windows 服务中 我想获取连接到我
  • UDP接收和发送Matlab

    我目前正在努力从外部设备接收数据包 然后将数据发送到另一个设备 我有一个工作 Simulink 模型 但我不知道如何在 Matlab 中对其进行编码 Matlab 中 UDP 接收块的参数如下图所示UDP 接收参数 https i stac
  • 网络服务发现不是发现服务类型

    我想通过 Android 设备在本地网络中找到服务器 我可以通过使用找到它NSDManager具有服务器服务类型的服务 例如 workstation tcp是服务类型 在我的本地网络中我有一个 无线路由器和无线中继器 两者都有不同的SSID
  • 使用 IdTCPClient 和 IdTCPServer 发送和接收 TMemoryStream

    我在 XE2 中找到了 Remy Lebeau 的 IdTCP 组件聊天演示 我想玩一下 可以发现 我想使用这些组件发送图片 最好的方法似乎是使用 TMemoryStream 如果我发送字符串 连接工作正常 字符串传输成功 但是当我将其更改
  • Java TCP Echo 服务器 - 广播

    我有一个简单的回显服务器 我希望当连接的用户向服务器键入任何内容时 所有其他客户端和该客户端都会收到消息 MOD 它现在不会发送给所有客户端 但它应该发送 而且我只是不知道我的代码出了什么问题 所以现在它只会将消息 MOD 发送给发送消息的
  • 使用多个 NIC 广播 UDP 数据包

    我正在 Linux 中为相机控制器构建嵌入式系统 非实时 我在让网络做我想做的事情时遇到问题 该系统有 3 个 NIC 1 个 100base T 和 2 个千兆端口 我将较慢的连接到相机 这就是它支持的全部 而较快的连接是与其他机器的点对
  • 使用 NestJS 的 TCP 服务器

    是否可以使用 NestJS 创建 TCP 服务器 我有一个仅通过 TCP 进行通信的 GPS 跟踪器 由于 NestJS 可以通过 TCP 在微服务之间进行通信 我认为也许 NestJS 可以用作低级网络应用程序 例如 java netty
  • SO_BINDTODEVICE Linux 套接字选项的问题

    我有一台带有两个网卡的电脑 一 eth0 用于 LAN 互联网 另一个用于与一个微控制器设备进行 UDP 通信 微控制器有一个 IP 192 168 7 2 和一个 MAC 地址 第二个电脑网络适配器 eth1 有 192 168 7 1
  • 用于实时传输协议的开源 .net C# 库

    net中有好的RTP开源库吗 我的目的是用于音频和视频同步问题并提高每秒帧数速率 我对 RTP 不太了解 但你可能想看看本文 http www codeproject com KB IP Using RTP in Multicasting
  • Java 客户端到服务器未知来源

    我有一个简单的乒乓球游戏 需要通过网络工作 服务器将创建一个带有球和 2 个球棒位置的游戏 当客户端连接到服务器时 服务器将创建一个名为 PongPlayerThread 的新类 它将处理客户端到服务器的输入和输出流 我的服务器工作100

随机推荐

  • CUDA程序编写具体参数设置

    介绍了GPU的结构以及资源的控制要素 xff08 GPU硬件结构和程序具体参数设置 yu132563的专栏 CSDN博客 xff09 以及编程过程中的一些需要注意的问题 xff08 CUDA程序性能调优 yu132563的专栏 CSDN博客
  • CUDA Stream流并发性

    目录 1 CUDA 中的异步命令 2 默认流 3 Multistream多流示例 异构计算是指高效地使用系统中的所有处理器 xff0c 包括 CPU 和 GPU 为此 xff0c 应用程序必须在多个处理器上并发执行函数 CUDA 应用程序通
  • Madagascar环境下编程

    引用 原创 Madagascar环境下编程 2013 07 17 04 50 34 转载 标签 xff1a 教育 分类 xff1a madagascar 本文转载自seismig 原创 Madagascar环境下编程 Madagascar是
  • mySQL(关系型数据库管理系统)编辑

    收藏 2906 1034 mySQL xff08 关系型数据库管理系统 xff09 编辑 MySQL 1 是一个 关系型数据库管理系统 xff0c 由瑞典 MySQL AB公司开发 xff0c 目前属于 Oracle公司 MySQL是最流行
  • CPU的核心数、线程数的关系和区别

    我们在选购电脑的时候 xff0c CPU是一个需要考虑到核心因素 xff0c 因为它决定了电脑的性能等级 CPU从早期的单核 xff0c 发展到现在的双核 xff0c 多核 CPU除了核心数之外 xff0c 还有线程数之说 xff0c 下面
  • STM32单片机,下载器下载完程序能正常跑起来,断电再上电程序不运行

    晶振坏了 转载于 https www cnblogs com god of death p 7050281 html
  • CUDA性能优化----warp深度解析

    CUDA性能优化 warp深度解析 2017 01 12 16 41 07 分类 xff1a HPC amp CUDA优化 标签 xff1a gpu cuda hpc 举报 字号 订阅 下载LOFTER 我的照片书 1 引言 CUDA性能优
  • 螺旋桨转矩

    xfeff xfeff 在螺旋桨气动力分析时 xff0c 首先应用翼型理论进行螺旋桨叶素分析 利用翼型升阻特性数据 xff0c 回避了有限机翼的展弦比问题 xff0c 诱导流动由涡流模型确定 xff0c 取决于桨叶数目 间距以及作用于每片桨
  • 给初学者们讲解人工神经网络(ANN)

    1 介绍 这份教学包是针对那些对人工神经网络 xff08 ANN xff09 没有接触过 基本上完全不懂的一批人做的一个简短入门级的介绍 我们首先简要的引入网络模型 xff0c 然后才开始讲解ANN的相关术语 作为一个应用的案例 xff0c
  • OpenMP基本概念

    OpenMP是一种用于共享内存并行系统的多线程程序设计方案 xff0c 支持的编程语言包括C C 43 43 和Fortran OpenMP提供了对并行算法的高层抽象描述 xff0c 特别适合在多核CPU机器上的并行程序设计 编译器根据程序
  • 散度和旋度的物理意义是什么?

    高等数学学的时间有点久远了 xff0c 最近需要推倒一些公式 xff0c 以前高数学的时候这公式那定理的都没说什么物理意义 xff0c 现在接触的问题都是具有一定物理意义的 xff0c 感觉对不上 xff0c 回来找找资料好好理解一下 xf
  • 格林公式、高斯公式及斯托克斯公式的理解及相互关系

    最近要推倒波动方程积分解 xff0c 要对散度 旋度以及他们之间的相互关系有一个理解 看了两天 xff0c 自己认为理解的差不多了 xff0c 现在写在这个地方 xff0c 作为笔记 xff0c 以后忘记了拿过来看一下 xff0c 加深一下
  • Radon变换理论介绍

    本人最近在研究Radon变换 xff0c 在查阅了各种资料之后在此写下个人的理解 xff0c 希望与各位牛牛进行交流共同进步 xff0c 也使得理解更加深刻些 Radon变换的本质是将原来的函数做了一个空间转换 xff0c 即 xff0c
  • test

    lt DOCTYPE html gt lt html lang 61 34 en 34 gt lt head gt lt meta charset 61 34 utf 8 34 gt lt meta http equiv 61 34 X U
  • BIT内存顺序

    机器的最小寻址单位是字节 xff0c bit无法寻址 xff0c 也就没有高低地址和起始地址的概念 xff0c 我们需要定义一下bit的 地址 以一个字节为例 xff0c 我们把从左到右的8个bit的位置 position 命名按顺序命名如
  • 无人驾驶感知篇之融合(五)

    今天早上看到上海新增一万七千左右 xff0c 看的真的很揪心 xff01 希望白衣战士能早点战胜这场疫情 xff0c 期待明天能有好消息 xff01 今天具体讲讲多贝叶斯估计算法的原理 xff0c 多贝叶斯估计法的主要思想是将传感器信息依据
  • MAC地址的介绍(单播、广播、组播、数据收发)

    MAC地址组成 网络设备的MAC地址是全球唯一的 MAC地址长度为48比特 xff0c 通常用十六进制表示 MAC地址包含两部分 xff1a 前24比特是组织唯一标识符 xff08 OUI xff0c OrganizationallyUni
  • stm32通用定时器输出PWM控制舵机

    stm32的通用定时器有TIM2 TIM3 TIM4 TIM5 xff0c 每个定时器都有独立的四个通道可以作为 xff1a 输入捕获 输出比较 PWM输出 单脉冲模式输出等 stm32除了基本定时器 xff0c 其他定时器都能输出PWM
  • Linux内核Socket CAN中文文档

    自己在年假中空闲之余翻译的内核中Socket CAN的文档 xff0c 原文地址在 xff1a http lxr linux no linux 43 v2 6 34 Documentation networking can txt 但是这篇
  • c/c++自定义通讯协议(TCP/UDP)

    前言 xff1a TCP与UDP是大家耳熟能详的两种传输层通信协议 xff0c 本质区别在于传输控制策略不相同 xff1a 使用TCP协议 xff0c 可以保证传输层数据包能够有序地被接受方接收到 xff0c 依赖其内部一系列复杂的机制 x