关于socket编程
socket原理示例文章1
socket原理示例文章2
setsockopt参数简介
opencv4.2.0快速下载链接
还不清楚原理的朋友,建议结合两篇文章一起观看
关于socket编程其它相关函数,例如setsockopt、WSAGetLastError等函数,需要找专门的博客查看。
环境
- windows 10
- vs ide 2017
- opencv 4.2.0
功能描述
- camera隔一定时间截取一个图像帧,以当前时间为图片名,保存到相关文件下
- 调用client,先传图片名,再传图片
- server一直运行于服务端,等待接收图片
- 每次传输完成,都将断开连接
server.cpp
#include "Winsock.h"
#include "stdio.h"
#include<stdlib.h>
#include<iostream>
#include<thread>
#pragma comment(lib, "wsock32.lib")
#define RECV_PORT 2494 //the 'RECV_PORT' should be the same as the variable in the sockclient.cpp
SOCKET sockserver;
struct sockaddr_in ServerAddr;
SOCKET sockclient;
struct sockaddr_in ClientAddr;
using namespace std;
void TCPRecv()
{
#define BuffLength 800*1024
#define filename_length 100
#define predix "D:\\vs_source\\repos\\server\\store\\"
FILE* p = NULL;
int recv_len = 0;
char filename[filename_length] = {};
char rbuff[BuffLength] = {};
for (int number = 1;; number++)
{
memset(rbuff, 0, BuffLength);
if (1 == number)
{
recv_len = recv(sockclient, filename, filename_length, 0);
if (recv_len <= 0)
{
closesocket(sockclient);
cout << "filename error" << endl;
break;
}
string str1 = predix + string(filename);
fopen_s(&p, str1.c_str(), "wb");
}
else
{
recv_len = recv(sockclient, rbuff, BuffLength, 0);
if (recv_len <= 0)
{
closesocket(sockclient);
cout << "connection finish" << endl;
fclose(p);
break;
}
fwrite(rbuff, recv_len, 1, p);
fflush(p);
}
}
return;
}
//initialize winsock
DWORD StartSock() //load winsock
{
WSADATA WSAData;
//BOOL bReuseaddr = TRUE;
//BOOL bDontLinger = FALSE;
//setsockopt(sockclient, SOL_SOCKET, SO_DONTLINGER, (const char*)&bDontLinger, sizeof(BOOL));
//setsockopt(sockclient, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(BOOL));
if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0) //WSADATA: which store information about socket
{
cout << "socket init fail!" << endl;
return -1;
}
sockserver = socket(AF_INET, SOCK_STREAM, 0); //structure SOCKET,load winsock,SOCK_STREAM stand for TCP protocol
if (sockserver == SOCKET_ERROR)
{
printf("sockserver create fail ! \n");
WSACleanup(); //release winsock
return -1;
}
ServerAddr.sin_family = AF_INET; // sockaddr_in is one kind of structure,serveraddr is the same as sockaddr_in
ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
ServerAddr.sin_port = htons(RECV_PORT);
if (SOCKET_ERROR == ::bind(sockserver, (struct sockaddr FAR*) & ServerAddr, sizeof(ServerAddr)))
{ //bind socket with addr
printf("bind is the error");
return -1;
}
if (listen(sockserver, SOMAXCONN) < 0)
{
printf("Listen error");
return -1;
}
int Addrlen = sizeof(ClientAddr);
//the accept won't return until the successfully monitor of clientsocket
/*
the current time photo transmission can not be triggered until the end of 'accept' function
*/
for (int i = 1;; i++)
{
sockclient = accept(sockserver, (struct sockaddr FAR*) & ClientAddr, &Addrlen);
cout << "connection: " << i << endl;
thread T(TCPRecv);
T.join();
}
}
void main()
{
if (StartSock() == -1)
return;
system("pause");
}
关于StarSock()
使用socket需要加载winsock库
if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0) //WSADATA: which store information about socket
{
cout << "socket init fail!" << endl;
return -1;
}
通过socket()获取套接字,失败则WSACleanup,退出程序
sockserver = socket(AF_INET, SOCK_STREAM, 0); //structure SOCKET,load winsock,SOCK_STREAM stand for TCP protocol
if (sockserver == SOCKET_ERROR)
{
printf("sockserver create fail ! \n");
WSACleanup(); //release winsock
return -1;
}
设置sockserver的参数,AF_INET指IPV4,INADDR_ANY指任何地址,RECV_PORT指绑定的端口。
ServerAddr.sin_family = AF_INET; // sockaddr_in is one kind of structure,serveraddr is the same as sockaddr_in
ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
ServerAddr.sin_port = htons(RECV_PORT);
if (SOCKET_ERROR == ::bind(sockserver, (struct sockaddr FAR*) & ServerAddr, sizeof(ServerAddr)))
{ //bind socket with addr
printf("bind is the error");
return -1;
}
绑定端口后,开始监听。SOMAXCONN代表最大数目,对于单对单通信没有影响。
if (listen(sockserver, SOMAXCONN) < 0)
{
printf("Listen error");
return -1;
}
初始化后,for循环传输图片
int Addrlen = sizeof(ClientAddr);
//the accept won't return until the successfully monitor of clientsocket
/*
the current time photo transmission can not be triggered until the end of 'accept' function
*/
for (int i = 1;; i++)
{
sockclient = accept(sockserver, (struct sockaddr FAR*) & ClientAddr, &Addrlen);
cout << "connection: " << i << endl;
thread T(TCPRecv);
T.join();
}
关于TCPRecv()
define参数,方便设置。
#define BuffLength 800*1024
#define filename_length 100
#define predix "C:\\Users\\DELL\\source\\repos\\server\\store\\"
第一次recv文件名,并创建文件。之后则传输文件
if (1 == number)
{
recv_len = recv(sockclient, filename, filename_length, 0);
if (recv_len <= 0)
{
closesocket(sockclient);
cout << "filename error" << endl;
break;
}
string str1 = predix + string(filename);
fopen_s(&p, str1.c_str(), "wb");
}
else
{
recv_len = recv(sockclient, rbuff, BuffLength, 0);
if (recv_len <= 0)
{
closesocket(sockclient);
cout << "connection finish" << endl;
fclose(p);
break;
}
fwrite(rbuff, recv_len, 1, p);
fflush(p);
}
camera.cpp
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <time.h>
#include <Windows.h>
#include <iostream>
#include "sockclient.h"
using namespace std;
using namespace cv;
//temp file of the same as .SLN in the temp folder of the project directory
string writePath = "../store/";
void camera()
{
VideoCapture capture(0); //0 stand for original camera, but external uses 1, 2, 3, ……
time_t times;
struct tm nowtime;
namedWindow("hello", CV_WINDOW_AUTOSIZE);
char name[64];
while (1)
{
Sleep(1000); //in milliseconds
Mat frame;
capture >> frame;
time(×);
localtime_s(&nowtime, ×);
if (nowtime.tm_sec % 5 == 0)
{
strftime(name, sizeof(name) - 1, "%Y-%m-%d-%H-%M-%S", &nowtime);
imwrite(writePath + name + ".jpg", frame);
//Sleep(2000);
string str = name + string(".jpg");
char* filename = (char*)str.data();
client(filename);
}
imshow("hello", frame);
waitKey(10);
}
}
int main(int argc, char** argv) {
camera();
}
camera()
使用了opencv库,感兴趣的朋友需要先下载并配置opencv。
快速下载链接:opencv4.2.0
在程序中设定了5秒拍一次,可以自行设置
if (nowtime.tm_sec % 5 == 0)
程序在局域网上的两台主机上测试,如果传输图片不全,可能是网络问题,可以通过Sleep()给予server端足够时间。若无法解决,则可通过WSASetLastError()、WSAGetLastError()两个函数获取错误信息,具体请search。
//Sleep(2000);
在opencv库中,以下两个函数是配套使用的
waitKey的作用是刷新图像帧,否则imshow的窗口无法显示,但可以正常传输。
imshow("hello", frame);
waitKey(10);
sockclient.cpp()
#include "Winsock.h"
#include "stdio.h"
#include <iostream>
#include<thread>
#pragma comment(lib, "wsock32.lib")
#define RECV_PORT 2494 //set-up server port
#define ServerIPAddr 16 //one more than what you see
#define predix "F:\\vs_repos\\GetCameraImageFrame\\store\\"
SOCKET Server;
sockaddr_in ServerAddr;
char server_IP[ServerIPAddr] = "192.168.1.4"; //server_IP
//char server_IP[ServerIPAddr] = "127.0.0.1";
using namespace std;
DWORD StartSock() //start-up socket project
{
WSADATA WSAData;
if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0) // load winsock2.0
return -1;
return 1;
}
//create socket
DWORD CreateSocket()
{ //the socket function will return a socket object; SOCK_STREAM realted TCP
Server = socket(AF_INET, SOCK_STREAM, 0);
if (Server == SOCKET_ERROR)
{
WSACleanup();
return -1;
}
return 1;
}
DWORD CallServer() //request connection
{
//set address structure
ServerAddr.sin_family = AF_INET; //Ipv4
ServerAddr.sin_addr.s_addr = inet_addr(server_IP);
ServerAddr.sin_port = htons(RECV_PORT);
if (connect(Server, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
{
WSACleanup();
closesocket(Server);
return -1;
}
return 1;
}
int SendFile(char* filename)
{
#define BuffLen 800*1024
FILE * pFile;
char sbuff[BuffLen] = {};
string str1 = predix + string(filename);
fopen_s(&pFile, str1.c_str(), "rb+");
int DataLength;
if (pFile)
{
int j = send(Server, filename, strlen(filename) + 1, 1);
Sleep(100);
while (true)
{
memset(sbuff, 0, BuffLen);
DataLength = fread(sbuff, 1, BuffLen, pFile);
cout << DataLength << endl;
if (DataLength < BuffLen) //based on the principle: logical data can be storaged continuously
{
int i = send(Server, sbuff, DataLength + 1, 1);
break;
}
else
{
send(Server, sbuff, DataLength, 1);
}
}
return 1;
}
else
{
WSACleanup();
closesocket(Server);
return -1;
}
}
int client(char* filename) //the function will send the photo file in the way of data stream
{
if (-1 == StartSock())
return -1;
if (-1 == CreateSocket())
return -1;
if (-1 == CallServer())
return -1;
if (-1 == SendFile(filename))
return -1;
Sleep(1000);
WSACleanup();
closesocket(Server);
}
初始化过程与server相似,client需要指定server的ip地址与端口
ServerAddr.sin_family = AF_INET; //Ipv4
ServerAddr.sin_addr.s_addr = inet_addr(server_IP);
ServerAddr.sin_port = htons(RECV_PORT);
if (connect(Server, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
{
WSACleanup();
closesocket(Server);
return -1;
}
第一个是局域网ip,第二个为本机测试
char server_IP[ServerIPAddr] = "192.168.1.4"; //server_IP
//char server_IP[ServerIPAddr] = "127.0.0.1";
SendFile()
逻辑与server端的逻辑对应,传输文件名时需间隔一定时间,再传输文件,防止两次传输间隔太短,server端将文件名与文件内容一起recv
int j = send(Server, filename, strlen(filename) + 1, 1);
Sleep(100);
因为send函数实质是将数据搬到本机的缓冲区,传输的过程是由系统完成的。
因此传输完成后,需要等待一定时间,防止传输不全。
需要释放winsock库,关闭sock client。才能进行下一次传输,否则server端可能还没断开本次连接,从而之后的连接一直呆在listen的队列中。
Sleep(1000);
WSACleanup();
closesocket(Server);
未解决问题
本次学习过程遇到一些问题如下
- 无法查看或者打开pdb文件,本次编写程序较为简单,该问题暂未对程序运行造成影响,试过网上许多解决方案,均以失败告终,不了了之。

- 本机传输大小与缓冲的大小有关
但BuffLen无法设置过大,否则会出现栈溢出的问题。
另外,为何本机传输文件大小会与此有关?
博主在局域网测试时,文件大小只会影响传输时间,没有出现该问题。
#define BuffLen 800*1024
- recv与server的传输长度问题,即第三个参数
send(Server, sbuff, DataLength + 1, 1);
本机模式运行正确,在局域网测试有字节数错误;
在局域网调好后,本机则错误了。
参考
https://blog.csdn.net/sunxiaolei/article/details/8674401
https://blog.csdn.net/qq_27923041/article/details/83857964
https://www.cnblogs.com/fuchongjundream/p/3914696.html
本文详细介绍了如何使用Socket编程实现图像帧的传输,包括客户端与服务器端的代码实现,以及使用OpenCV进行图像捕获的方法。
1960

被折叠的 条评论
为什么被折叠?



