套接字是Linux下网络编程中的一项重要技术。通过套接字,我们可以方便地实现网络传输、通信等功能。而在实战中,我们也会经常遇到需要传输视频文件的场景。那么,如何使用套接字来实现视频传输呢?本文将为大家详细介绍Linux下套接字视频传输的实现方法。
一、套接字简介
套接字(Socket)是一种抽象的概念,它实际上是对TCP/IP协议族中的传输层和网络层的封装和抽象。在Linux中,套接字是通过一组系统调用函数来实现的。一般而言,使用套接字需要经过以下步骤:
1. 创建套接字:使用socket()函数创建一个套接字,该函数的调用格式为:
int socket(int domn, int type, int protocol);
2. 绑定套接字:使用bind()函数将套接字与本地地址绑定,该函数的调用格式为:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
3. 监听套接字:使用listen()函数将套接字转换为被动套接字,该函数的调用格式为:
int listen(int sockfd, int backlog);
4. 接受连接:使用accept()函数接受客户端的连接请求,该函数的调用格式为:
int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len);
5. 发送和接收数据:通过send()和recv()函数进行数据的发送和接收。
二、视频传输实现步骤
由于视频文件的大小比较大,传统的发送和接收方法可能会出现拥塞等问题。因此,我们需要采用分包发送的方式。具体而言,我们将视频文件分成多个小包,每个小包的大小一般设置为1KB或2KB。发送方将这些小包按照一定的顺序发送给接收方,接收方再将这些小包拼接起来,就可以得到完整的视频文件。
以下是Linux下套接字视频传输的具体实现步骤:
1. 发送端
我们需要将视频文件分成多个小包。假设我们将视频文件分成了N个小包,那么每个小包的编号从0到N-1。发送方需要按照编号的顺序将每个小包发送给接收方。
为了确保传输的可靠性,我们需要设置校验和以及确认机制。具体而言,在发送每个小包之前,发送方需要计算该小包的校验和,然后将该校验和和小包一起发送给接收方。接收方在收到小包后,会计算其校验和并与发送方发送的校验和进行比较。如果校验和相同,则认为该小包传输成功,回复一个确认消息给发送方,否则认为该小包传输失败,再次请求发送该小包。
具体的发送方法如下:
(1)我们需要打开视频文件并读取其中的数据:
FILE *fp = fopen(filename, “rb”);
if(fp==NULL){
printf(“cannot open file!\n”);
return;
}
unsigned char sendbuf[BUFSIZE];
size_t read_len;
while((read_len=fread(sendbuf,1,BUFSIZE,fp))>0){
//TODO: 将sendbuf分成多个小包,计算校验和并发送给接收方
}
fclose(fp);
(2)然后,我们需要将sendbuf分成多个小包,并计算每个小包的校验和:
unsigned char packet[PACKET_LEN];
for(int i=0; i
int index = i*PACKET_DATA_LEN;
memcpy(packet+PACKET_HEADER_LEN, sendbuf+index, PACKET_DATA_LEN);
//计算校验和
unsigned short checksum = 0;
for(int j=PACKET_HEADER_LEN; j
checksum += (unsigned short)packet[j];
}
packet[0] = i>>8;
packet[1] = i&0xFF;
packet[2] = checksum>>8;
packet[3] = checksum&0xFF;
//发送小包
sendto(sockfd, packet, PACKET_LEN, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
}
(3)我们还需要等待接收方的确认回复:
while(ack_count
recvfrom(sockfd, ack_buf, ACK_LEN, 0, (struct sockaddr *)&from_addr, &addrlen);
if(check_ack(ack_buf)==0){
//收到确认回复,ack_count加1
ack_count++;
}else{
//收到未知消息,忽略
}
}
2. 接收端
接收端的代码较为简单,主要工作是接收小包并将其拼接成完整的视频文件。具体而言,我们需要采用缓冲区的方式,将每个小包存放在缓冲区中,当缓冲区满的时候,我们将缓冲区中的小包拼接起来,写入到本地的视频文件中。
具体的接收方法如下:
(1)我们需要创建一个缓冲区,并初始化各个参数:
unsigned char buffer[BUFSIZE];
unsigned char data_buf[PACKET_DATA_LEN];
unsigned char ack_buf[ACK_LEN];
int recv_len;
int receive_next=0;
int current_packet=0;
int current_packet_size=0;
(2)然后,我们需要接收小包并写入缓冲区中:
while(1){
recv_len = recvfrom(sockfd, data_buf, PACKET_DATA_LEN, 0, (struct sockaddr *)&from_addr, &addrlen);
if(recv_len
break;
}
int recv_index = data_buf[0]*256 + data_buf[1];
unsigned short checksum = data_buf[2]*256 + data_buf[3];
//计算校验和
unsigned short local_checksum = 0;
for(int i=PACKET_HEADER_LEN; i
local_checksum += (unsigned short)data_buf[i];
}
//校验和错误,重新请求数据
if(checksum!=local_checksum){
send_ack(sockfd, 0, from_addr);
continue;
}
//小包序号错误,重新请求数据
if(recv_index!=receive_next){
send_ack(sockfd, receive_next, from_addr);
continue;
}
//将小包存入缓冲区
memcpy(buffer+current_packet_size, data_buf+PACKET_HEADER_LEN, PACKET_DATA_LEN);
current_packet++;
current_packet_size += PACKET_DATA_LEN;
//缓冲区满了,将数据写入文件中并重新初始化缓冲区
if(current_packet_size>=BUFSIZE){
current_packet_size = 0;
write_buffer_to_file(buffer);
memset(buffer, 0, BUFSIZE);
}
//发送确认消息
send_ack(sockfd, receive_next, from_addr);
receive_next++;
}
(3)我们还需要将最后一个小包残留在缓冲区中的数据写入到视频文件中:
if(current_packet_size>0){
write_buffer_to_file(buffer);
}
三、
本文介绍了Linux下套接字视频传输的实现方法,并详细介绍了发送端和接收端的具体代码实现。实际上,套接字技术在Linux下的应用非常广泛,可以用于各种网络通信场景,包括但不限于文件传输、远程控制等。因此,对套接字技术的学习和掌握对于linux系统编程的从业人员来说是非常重要的。希望本文能够对大家有所帮助。
相关问题拓展阅读:
- linux下socket文件传输问题
linux下socket文件传输问题
要下班了,时间急,不写代码了先给你一个思路
实现最简单的udp socket 模型,实现发送一个字符串。
实现一个简单的打开文件,读取文件的例子,如用fgets(),类似的函数有很多,然后再把读取的培虚文件内容忘另一个文件里写(相关函数fopen(),write(),read())。
把上面两个函数结合到一起者族,在客户端实现打开要传送的文件,按一定的大小读取,读取后调用sendto()发送到服务器端。在服务器端创建一个文件,然后调用recvfrom()接受客户端发送过来的数据,向来是创建的那个文件中写。
下面是改好的udp发送文件的例子。
服务器端程序的编译
gcc -o file_server file_server
客户端程序的编译
gcc -o file_client file_client.c
服务器程序和客户端程应当分别运行在2台计算机上.
服务器端程序的运行,在一个计算机的终端执行
./file_server
客户端程序的运行,在另一个计算机的终端中执行
./file_client 运行服务器程序的计算机的IP地址
根据提示输入要传输的服务器上的文件,该文件在服务器的运行目录上
在实际编程和测试中,可以用2个终端代替2个计算机,这样就可以在一台计算机上测试网络程序,
服务器端程序的运行,在一个终端执行
./file_server
客户端程序的运行,在另一个终端中执行
./file_client 127.0.0.1
说明: 任何计算机都可以通过127.0.0.1访问自己. 也可以用计算机的实际IP地址代替127.0.0.1
//////////////////////////////////////////////////////////////////////////////////////
// file_server.c 文件传输顺序服务器示例
//////////////////////////////////////////////////////////////////////////////////////
//本文件是服务器的代码
#include // for sockaddr_in
#include // for socket
#include // for socket
#include// for printf
#include// for exit
#include// for bzero
/*
#include
#include
#include
#include
*/
#define HELLO_WORLD_SERVER_PORT
#define LENGTH_OF_LISTEN_QUEUE 20
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
int main(int argc, char **argv)
{
//设置一个socket地址结构server_addr,代表服务器internet地址, 端口
struct sockaddr_in server_addr, pcliaddr;
bzero(&server_addr,sizeof(server_addr)); //把一段内存区的内容全部设置为0
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
//创建用于internet的据报套接字(UDPt,用server_socket代表服务器socket
// 创建数据报套接字(UDP)
int server_socket = socket(PF_INET,SOCK_DGRAM,0);
if( server_socket FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer));
//int fp = open(file_name, O_RDON);
//if( fp 0)
while( (file_block_length = fread(buffer,sizeof(char),BUFFER_SIZE,fp))>0)
{
printf(“file_block_length = %d\n”,file_block_length);
//发送buffer中的字符串到new_server_socket,实际是给客户端
if(send(new_server_socket,buffer,file_block_length,0) // for sockaddr_in
#include // for socket
#include // for socket
#include// for printf
#include// for exit
#include// for bzero
/*
#include
#include
#include
#include
*/
#define HELLO_WORLD_SERVER_PORT
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
int main(int argc, char **argv)
{
if (argc != 2)
{
printf(“Usage: ./%s ServerIPAddress\n”,argv);
exit(1);
}
//设置一个socket地址结构client_addr,代表客户机internet地址, 端口
struct sockaddr_in client_addr;
bzero(&client_addr,sizeof(client_addr)); //把一段内存区的内容全部设置为0
client_addr.sin_family = AF_INET; //internet协议族
client_addr.sin_addr.s_addr = htons(INADDR_ANY);//INADDR_ANY表示自动获取本机地址
client_addr.sin_port = htons(0); //0表示让系统自动分配一个空闲端口
//创建用于internet的流协议(TCP)socket,用client_socket代表客户机socket
int client_socket = socket(AF_INET,SOCK_DGRAM,0);
if( client_socket BUFFER_SIZE?BUFFER_SIZE:strlen(file_name));
//向服务器发送buffer中的数据
socklen_t n = sizeof(server_addr) ;
sendto(client_socket,buffer,BUFFER_SIZE,0,(struct sockaddr*)&server_addr,n);
// int fp = open(file_name, O_WRON|O_CREAT);
// if( fp
FILE * fp = fopen(file_name,”w”);
if(NULL == fp )
{
printf(“File:\t%s Can Not Open To Write\n”, file_name);
exit(1);
}
//从服务器接收数据到buffer中
bzero(buffer,BUFFER_SIZE);
int length = 0;
while( length = recv(client_socket,buffer,BUFFER_SIZE,0))
{
if(length
{
printf(“Recieve Data From Server %s Failed!\n”, argv);
break;
}
//int write_length = write(fp, buffer,length);
int write_length = fwrite(buffer,sizeof(char),length,fp);
if (write_length
{
printf(“File:\t%s Write Failed\n”, file_name);
break;
}
bzero(buffer,BUFFER_SIZE);
}
printf(“Recieve File:\t %s From Server Finished\n”,file_name, argv);
return 0;
}
请采纳。
如果你的客户端在发送文件时,每次都重新connect,再神租进行数据传输,则你的程序无法解决数据的区分。
如果客户端是一次connect循环发送,后台服务循环接收,则
(1)如果你的服务端只有一个进程(不支持并发),则A和B不会同时运行,只能按顺序接收游激兆完铅闷A再接收B
(2)如果,每一个新链接上来,你都建立一个新的进程去工作,则不会有问题。
对每个客户端请求,服务端守护进程fork子进程