python socket tcp编程,【整理】Linux Socket网络编程_TCP编程(4)_C++与PythonSocket通信 | 勤奋的小青蛙...

由于工作中一些需求,需要完成C++与Python之间的Socket通信,基于自定义协议的通信。

环境:

Centos 6.5+gcc (GCC) 4.8.2+kernel 2.6.32+cmake version 2.8.12.2+Python 3.4.3(windows环境)

Bug修复请参考:

难点:

1:Python Socket Struct解析。

由于C++的Socket开发是基于自定义协议的,通信中加入了结构体,在Python中需要对socket通信中的结构体进行解析。Python中正好有struct模块,所以,可以借助该模块进行自定义协议的通信。

2:Python中自定义协议与服务器端协议统一

客户端协议的解析读取,发送等都要与服务端相同,不然势必无法正常按照协议进行通信。

代码如下:

服务端(基于C语言):

socket_server.c:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "msg.h"

#define MAXCONNECTIONS 100

int sockfd;

void sig_handler(int signo) {

if (signo == SIGINT) {

printf("server close\n");

close(sockfd);

exit(1);

}

}

/*输出连接上来的客户端的相关信息*/

void out_addr(struct sockaddr_in *clientaddr) {

//将端口从网络字节序转换成主机字节序

int port = ntohs(clientaddr->sin_port);

char ip[16];

memset(ip, 0, sizeof(ip));

//将ip地址从网络字节序转换成点分十进制

inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, ip, sizeof(ip));

printf("client: %s(%d) connected\n", ip, port);

}

/*输出服务器端时间*/

void do_service(int fd) {

/*和客户端进行读写操作(双向通信)*/

char buff[1024];

while (1)

{

memset(buff, 0, sizeof(buff));

printf("start read and write...\n");

size_t size;

//printf("sizeof buff is %d = \n", sizeof(buff));

if ((size = read_msg(fd, buff, sizeof(buff))) < 0) {

print("protocal error");

break;

}

else if (size == 0) {

break;

}

else {

printf("Server Received = %s\n", buff);

//printf("socket_server send size = %d \n", strlen(buff));

//printf("socket_server fd = %d \n", fd);

//printf("socket_server send size = %d \n", sizeof(buff));

//printf("socket_server send size = %d \n", strlen(buff));

if (write_msg(fd, buff, sizeof(buff)) < 0) {

printf("errorno = %d", errno);

if (errno == EPIPE) {

break;

}

perror("protocal error");

}

}

}

}

void out_fd(int fd) {

struct sockaddr_in addr;

socklen_t len = sizeof(addr);

//从fd中获取连接的客户端相关信息

if (getpeername(fd, (struct sockaddr *)&addr, &len) < 0) {

perror("getpeername error");

return;

}

char ip[16];

memset(ip, 0, sizeof(ip));

int port = ntohs(addr.sin_port);

inet_ntop(AF_INET, &addr.sin_addr.s_addr, ip, sizeof(ip));

printf("%16s(%5d) closed!\n", ip, port);

}

void *th_fn(void *arg) {

int fd = (int)arg;

do_service(fd);

out_fd(fd);

close(fd);

return (void *)0;

}

int main(int argc, char *argv[]) {

if (argc < 2) {

printf("usage: %s #port\n", argv[0]);

exit(1);

}

if (signal(SIGINT, sig_handler) == SIG_ERR) {

perror("signal sigint error");

exit(1);

}

signal(SIGPIPE, SIG_IGN);

/*步骤1:创建socket(套接字)*/

sockfd = socket(AF_INET, SOCK_STREAM, 0);

/*步骤2:将socket和地址(包括ip,port)进行绑定*/

struct sockaddr_in serveraddr;

memset(&serveraddr, 0, sizeof(serveraddr));

/*向地址中填入ip,port,internet地址簇类型*/

serveraddr.sin_family = AF_INET; //ipv4

serveraddr.sin_port = htons(atoi(argv[1])); //port

serveraddr.sin_addr.s_addr = INADDR_ANY; //接收所有网卡地址

if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) {

perror("bind error");

exit(1);

}

/*步骤3:调用listen函数启动监听(指定port监听)

通知系统去接受来自客户端的连接请求

第二个参数:指定队列的长度*/

if (listen(sockfd, MAXCONNECTIONS) < 0) {

perror("listen error");

exit(1);

}

/*步骤4:调用accept函数从队列中获得一个客户端的连接请求,

并返回新的socket描述符*/

struct sockaddr_in clientaddr;

socklen_t clientaddr_len = sizeof(clientaddr);

/*设置线程的分离属性*/

pthread_attr_t attr;

pthread_attr_init(&attr);

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

while (1)

{

int fd = accept(sockfd, NULL, NULL);

if (fd < 0) {

perror("accept error");

continue;

}

/*步骤5:启动子线程去调用IO函数(read/write)和连接的客户端进行双向通信*/

pthread_t th;

int err;

/*以分离状态启动子线程*/

if ((err = pthread_create(&th, &attr, th_fn, (void *)fd)) != 0) {

perror("pthread create error");

}

pthread_attr_destroy(&attr);

}

return 0;

}

msg.h:

#ifndef __MSG_H__

#define __MSG_H__

#include

typedef struct {

//协议头部

char head[13];

char checknum; //校验码

//协议体部

char buff[1024]; //数据

} Msg;

/************************************************************************/

/* 发送一个基于自定义协议的message

* 发送的数据存放在buff中*/

/************************************************************************/

extern int write_msg(int sockfd, char *buff, size_t len);

/************************************************************************/

/* 读取一个基于自定义协议的message

* 读取的数据存放在buff中*/

/************************************************************************/

extern int read_msg(int sockfd, char *buff, size_t len);

#endif

msg.c:

#include "msg.h"

#include

#include

#include

#include

/*计算校验码*/

static unsigned char msg_check(Msg *message) {

//unsigned char s = 0;

int s = 0;

int i;

//printf("message head = %s \n", message->head);

//printf("message buff = %s \n", message->buff);

for (i = 0; i < sizeof(message->head); i++) {

//printf("i1 = %c \n", message->head[i]);

s = ((s + message->head[i]) % 255);

//printf("i1 checknum = %d \n", s);

}

for (i = 0; i < sizeof(message->buff); i++) {

//printf("i2 = %c \n", message->buff[i]);

s = ((s + message->buff[i]) % 255);

//printf("i2 checknum = %d \n", s);

}

//printf("msg_check num is %d \n", s);

return s;

}

/************************************************************************/

/* 发送一个基于自定义协议的message

* 发送的数据存放在buff中*/

/************************************************************************/

int write_msg(int sockfd, char *buff, size_t len) {

//printf("write_msg1 \n");

Msg message;

memset(&message, 0, sizeof(message));

strcpy(message.head, "gmqh_sh_2016");

memcpy(message.buff, buff, len);

message.checknum = msg_check(&message);

//printf("write_msg2 \n");

if (write(sockfd, &message, sizeof(message)) != sizeof(message)) {

return -1;

}

}

/************************************************************************/

/* 读取一个基于自定义协议的message

* 读取的数据存放在buff中*/

/************************************************************************/

int read_msg(int sockfd, char *buff, size_t len) {

//printf("read_msg1 \n");

Msg message;

memset(&message, 0, sizeof(message));

size_t size;

//printf("read_msg1-1 \n");

if ((size = read(sockfd, &message, sizeof(message))) < 0) {

//printf("read_msg1-2 \n");

return -1;

}

else if (size == 0) {

return 0;

}

//printf("read_msg2 \n");

//进行校验码验证,判断接收到message是否完整

unsigned char s = msg_check(&message);

//printf("read_msg message.head = %s \n", message.head);

//printf("read_msg message.checknum = %d \n", message.checknum);

//printf("read_msg message.buff = %s \n", message.buff);

//printf("read_msg checknum cal = %d \n", s);

if ((s == (unsigned char)message.checknum) && (!strcmp("gmqh_sh_2016", message.head))) {

//printf("read_msg3 \n");

memcpy(buff, message.buff, len);

return sizeof(message);

}

//printf("read_msg4 \n");

return -1;

}

CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)

#project name

project(multi_thread_socket_demo)

set(CMAKE_CXX_COMPILER "g++")

#set compiler for c++ language

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -lrt -lpthread -D_GLIBCXX_USE_NANOSLEEP")

#source files(can add source files manually)

#set(SOURCE_FILES main.cpp ThostFtdcMdApi.h ThostFtdcTraderApi.h ThostFtdcUserApiDataType.h ThostFtdcUserApiStruct.h MdSpi.cpp MdSpi.h TdSpi.cpp TdSpi.h CTP_Manager.cpp CTP_Manager.h Login.h Order.cpp Order.h User.cpp User.h Utils.cpp Utils.h DBManager.h DBManager.cpp Trader.h Trader.cpp FutureAccount.h FutureAccount.cpp Strategy.h Strategy.cpp Algorithm.h Algorithm.cpp)

#source directory(add source files automatically)

aux_source_directory(. SOURCE_FILES)

find_package(Threads)

#set extern libraries

#set(LIBRARIES libthostmduserapi.so libthosttraderapi.so libmongoclient.so libboost_thread.so libboost_system.so libboost_regex.so)

#add execute file

add_executable(multi-thread-socket-server ${SOURCE_FILES})

#add link library

target_link_libraries(multi-thread-socket-server ${CMAKE_THREAD_LIBS_INIT})

编译方式:

cmake .

make

客户端(基于Python语言):

socket_client.py:

# -*- coding: utf-8 -*-

from collections import namedtuple

import socket

import sys

import struct

Message = namedtuple("Message", "head checknum buff")

# m = Message("gmqh", 0, "hello, world")

# 计算校验码

def msg_check(message):

#将收到的head以及buff分别累加 % 255

checknum = 0

for i in message.head:

# print("i1 = %c \n" % i)

checknum = ((checknum + ord(i)) % 255)

# print("i1 checknum = %d \n" % checknum)

for i in message.buff:

# print("i2 = %c \n" %i)

checknum = ((checknum + ord(i)) % 255)

# print("i2 checknum = %d \n" % checknum)

return checknum

#发送数据

def write_msg(sockfd, buff):

# print("send buff = ", buff)

# print("send buff len = ", len(buff))

# print("send buff = ", buff.encode())

# print("send buff len = ", len(buff.encode()))

#构造Message

m = Message("gmqh_sh_2016", 0, buff)

#数据发送前,将校验数据填入Message结构体

checknum = msg_check(m)

m = Message("gmqh_sh_2016", checknum, buff)

# print("send m.buff = ", m.buff.encode())

# print("send m.checknum = ", m.checknum)

#打包数据(13位的head,1位校验码,不定长数据段)

data = struct.pack(">13s1B" + str(len(m.buff.encode()) + 1) + "s", m.head.encode(), m.checknum, m.buff.encode());

print("send data = ", data)

#发送数据

size = sockfd.send(data)

# print(size)

return size

#读取数据

def read_msg(sockfd):

try:

#接收数据1038个字节(与服务器端统一:13位head+1位checknum+1024数据段)

data = sockfd.recv(1038)

except socket.error as e:

print(e)

#解包数据

head, checknum, buff = struct.unpack(">13s1B"+ str(len(data) - 14) +"s", data)

# print(head, checknum, buff, '\n')

#将解包的数据封装为Message结构体

m = Message(head.decode().split('\x00')[0], checknum, buff.decode())

tmp_checknum = msg_check(m)

m = Message(head.decode().split('\x00')[0], tmp_checknum, buff.decode())

#将收到的标志位与收到数据重新计算的标志位进行对比+head内容对比

if ((m.checknum == checknum) and (m.head == "gmqh_sh_2016")):

#打印接收到的数据

print("receive data = ", m.buff)

return 1

else:

return -1

if __name__ == '__main__':

#创建socket套接字

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

if s:

# 连接服务器: IP,port

try:

#进行与服务端的连接(ip地址根据实际情况进行更改)

s.connect(("192.168.1.12", 8888))

except socket.error as e:

print("socket error", e)

sys.exit(1)

#输入提示符

prompt = b'->'

while True:

buff = input(prompt)

if buff == "":

continue

#发送数据

if (write_msg(s, buff) < 0):

print("write msg error")

continue

else:

#接收数据

if (read_msg(s) < 0):

print("read msg error")

continue

pass

s.close()

else:

print("socket error")

运行效果:

1e4211c7064f87759714c230ad5abf29.png

文章的脚注信息由WordPress的wp-posturl插件自动生成

|2|left

打赏

1b6439c6a040252321edad911c85491b.png微信扫一扫,打赏作者吧~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值