---------https://docs.python.org/2/library/socket.html-----------官网是个不错的选择!!!
1.服务器端
参考网站:https://www.cnblogs.com/dreamer-fish/p/5501924.html
server.py运行于公网服务器(腾讯云-安全组加入8080端口文章-6)
#coding=utf-8
'''
程序思路:9位字符串用于接收文件(这里是图片)的大小self.filesize;根据文件大小接收后续客户端发来的文件流(二进制流)。
'''
from socket import *
import threading
import SocketServer,time,struct,os
class MyRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
print('connected from:', self.client_address)
while True:
self.buf = self.request.recv(9)
if self.buf:
self.filesize = int(self.buf)
print('file size is:{}'.format(self.filesize))
self.filenewname = "new.jpg"
recvd_size = 0 #定义接收了的文件大小
file = open(self.filenewname,'w')
print('stat receiving...')
while not recvd_size == self.filesize:
if self.filesize - recvd_size > 1024:
rdata = self.request.recv(1024)
recvd_size += len(rdata)
else:
rdata = self.request.recv(self.filesize - recvd_size)
recvd_size = self.filesize
file.write(rdata)
file.close()
print('receive done')
if __name__ == '__main__':
HOST = ''
POST = 8080
ADDR = (HOST, POST)
tcpServ = SocketServer.ThreadingTCPServer(ADDR, MyRequestHandler)
print('waiting for connection...' )
tcpServ.serve_forever()
运行程序后,本地(联网)测试8080端口:telnet 【公网IP】 8080
2.客户端
参考网站1:https://blog.csdn.net/yutianzuijin/article/details/27205121
参考网站2:https://www.bogotobogo.com/cplusplus/sockets_server_client.php
运行于连接网络的电脑上。
- 获取文件大小(#include <sys/stat.h>)
- sprintf将文件大小转换为9位长度的字符串(文件大小前补0)
- socket发送9位(字符串形式)文件大小
- socket发送文件。
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <sys/stat.h>
#include<unistd.h>
#define PORT 8080
#define LENGTH 512
using namespace std;
//文件大小
int file_size2(char* filename)
{
struct stat statbuf;
stat(filename,&statbuf);
int size=statbuf.st_size;
return size;
}
int main(int argc, char *argv[]){
int sockfd;
int nsockfd;
char revbuf[LENGTH];
struct sockaddr_in remote_addr;
/* Get the Socket file descriptor */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
fprintf(stderr, "ERROR: Failed to obtain Socket Descriptor! (errno = %d)\n",errno);
exit(1);
}
/* Fill the socket address struct */
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(PORT);
inet_pton(AF_INET, "127.0.0.1", &remote_addr.sin_addr); //127.0.0.1表示本机IP,修改为云服务器公网IP即可
bzero(&(remote_addr.sin_zero), 8);
/* Try to connect the remote */
if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
{
fprintf(stderr, "ERROR: Failed to connect to the host! (errno = %d)\n",errno);
exit(1);
}
else
printf("[Client] Connected to server at port %d...ok!\n", PORT);
/* Send File to Server */
char sdbuf[LENGTH];
char fname[256];
char head_buffer[256]="";
int n;
bzero(fname,256);
bzero(head_buffer,256);
cout<<"input file name:";
fgets(fname,255,stdin); //传输文件名
char *fs_name = (char*)malloc((strlen(fname)-1)*sizeof(char));
memcpy(fs_name,fname,(strlen(fname)-1));
cout<<"file name:"<<fs_name<<endl;
FILE *fs = fopen(fs_name, "rw");
int f_size = file_size2(fs_name); //文件大小
cout<<"f_size:"<<f_size<<endl;
char char_f_size[128];
sprintf(char_f_size,"%09d",f_size);
//strcat(head_buffer,fs_name);
strcat(head_buffer,char_f_size);
n = write(sockfd,head_buffer, strlen(head_buffer));
cout<<"finish writing head, head width(send length):"<<strlen(head_buffer)<<endl;
if(n<0) printf("Error: sending filename");
printf("[Client] Sending %s to the Server... ", fs_name);
if(fs == NULL)
{
printf("ERROR: File %s not found.\n", fs_name);
exit(1);
}
bzero(sdbuf, LENGTH);
int fs_block_sz;
while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs)) > 0)
{
if(send(sockfd, sdbuf, fs_block_sz, 0) < 0)
{
fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno);
break;
}
bzero(sdbuf, LENGTH);
}
printf("Ok File %s from Client was Sent!\n", fs_name);
close (sockfd);
printf("[Client] Connection lost.\n");
return (0);
}
c++程序需要编译,Linux64bit环境下CMakeLists文件提供如下:(也可以直接gcc编译)
project(ser_cli)
cmake_minimum_required(VERSION 2.8)
cmake_policy(SET CMP0015 NEW)
include_directories(include)
link_directories(lib)
set(CMAKE_CXX_FLAGS "-std=c++11")
add_executable(c_client c_client.cpp)
#添加后可进行调试
set( CMAKE_BUILD_TYPE Debug )
target_link_libraries(c_client)
粘包问题:我采用的是加入文件头部(文件长度)的方式。但同时需要注意,每次client发送数据后,最好清空一下数组:
memset(head,0,sizeof(head));//头部数组(head)清空
memset(data,0,sizeof(data));//文件数组(data)清空
如果不清空我的程序导致读取的包太短的问题,清空就没问题了。
将文件封装为json格式,图片以base64封装为json格式:https://blog.csdn.net/weixin_42755375/article/details/81783125
json头文件:https://github.com/nlohmann/json#conversion-from-stl-containers(stl容器数据结构list vector等都可以打包)