Socket通信-Linux系统中C语言实现TCP/UDP图片和文件传输
TCP实现
传输控制协议(TCP,Transmission Control Protocol) 是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议。TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出SYN连接请求后,等待对方回答SYN+ACK,并最终对对方的 SYN 执行 ACK 确认。这种建立连接的方法可以防止产生错误的连接,TCP使用的流量控制协议是可变大小的滑动窗口协议。
1.服务端
基于TCP协议的socket的server端程序编程步骤:
1、建立socket ,使用socket()
2、绑定socket ,使用bind()
3、打开listening socket,使用listen()
4、等待client连接请求,使用accept()
5、收到连接请求,确定连接成功后,使用输入,输出函数recv(),send()与client端互传信息
6、关闭socket,使用close()
服务端代码server.c
`
点击查看代码
/*server.c*/
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define SERVER_PORT 5678 //端口号
#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,代表服务器ip地址和端口
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(SERVER_PORT);
// 创建用于流协议(TCP)socket,用server_socket代表服务器向客户端提供服务的接口
int server_socket = socket(PF_INET, SOCK_STREAM, 0);
if (server_socket < 0)
{
printf("Create Socket Failed!\n");
exit(1);
}
else
printf("Create Socket Success.\n");
// 把socket和socket地址结构绑定
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))
{
printf("Server Bind Port: %d Failed!\n", SERVER_PORT);
exit(1);
}
else
printf("Client Bind Port Success.\n");
// server_socket用于监听
if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE))
{
printf("Server Listen Failed!\n");
exit(1);
}
else
printf("Listening....\n");
// 服务器始终监听
while(1)
{
// 定义客户端的socket地址结构client_addr,当收到来自客户端的请求后,调用accept
// 接受此请求,同时将client端的地址和端口等信息写入client_addr中
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
// 接受一个从client端到达server端的连接请求,将客户端的信息保存在client_addr中
// 如果没有连接请求,则一直等待直到有连接请求为止,这是accept函数的特性
// accpet返回一个新的socket,这个socket用来与此次连接到server的client进行通信
// 这里的new_server_socket代表了这个通信通道
int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);
if (new_server_socket < 0)
{
printf("Server Accept Failed!\n");
break;
}
else
printf("Server Accept Success.\n");
char buffer[BUFFER_SIZE];
bzero(buffer, sizeof(buffer));
length = recv(new_server_socket, buffer, BUFFER_SIZE, 0);
if (length < 0)
{
printf("Server Recieve Data Failed!\n");
break;
}
else
printf("Server Recieve Data Success.\n");
char file_name[FILE_NAME_MAX_SIZE + 1];
bzero(file_name, sizeof(file_name));
strncpy(file_name, buffer,
strlen(buffer) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buffer));
FILE *fp = fopen(file_name, "r"); //获取文件操作符
if (fp == NULL)
{
printf("File:\t%s Not Found!\n", file_name);
}
else
{
bzero(buffer, BUFFER_SIZE);
int file_block_length = 0;
while( (file_block_length = fread(buffer, sizeof(char), BUFFER_SIZE, fp)) > 0)
{
// 发送buffer中的字符串到new_server_socket,实际上就是发送给客户端
if (send(new_server_socket, buffer, file_block_length, 0) < 0)
{
printf("Send File:\t%s Failed!\n", file_name);
break;
}
bzero(buffer, sizeof(buffer));
}
fclose(fp);
printf("File:\t%s Transfer Finished!\n", file_name);
}
close(new_server_socket);
}
close(server_socket);
return 0;
}
`
2.客户端
基于TCP协议的socket的Client程序编程步骤:
1、建立socket,使用socket()
2、通知server请求连接,使用connect()
3、若连接成功,就使用输入输出函数recv(),send()与server互传信息
4、关闭socket,使用close()
客户端代码client.c
点击查看代码
/*client.c*/
#include<netinet/in.h> // for sockaddr_in
#include<sys/types.h> // for socket
#include<sys/socket.h> // for socket
#include<stdio.h> // for printf
#include<stdlib.h> // for exit
#include<string.h> // for bzero
#define SERVER_PORT 5678
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
int main(int argc, char **argv)
{
if (argc != 2) //判断有没有输入服务器ip
{
printf("Usage: ./%s ServerIPAddress\n", argv[0]);
exit(1);
}
// 设置一个socket地址结构client_addr, 代表客户机的ip地址和端口
struct sockaddr_in client_addr;
bzero(&client_addr, sizeof(client_addr));
client_addr.sin_family = AF_INET; // internet协议族IPv4
client_addr.sin_addr.s_addr = htons(INADDR_ANY); // INADDR_ANY表示自动获取本机地址
client_addr.sin_port = htons(0); // auto allocated, 让系统自动分配一个空闲端口
// 创建用于internet的流协议(TCP)类型socket,用client_socket代表客户端socket
int client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket < 0)
{
printf("Create Socket Failed!\n");
exit(1);
}
else
printf("Create Socket Success.\n");
// 把客户端的socket和客户端的socket地址结构绑定
if (bind(client_socket, (struct sockaddr*)&client_addr, sizeof(client_addr)))
{
printf("Client Bind Port Failed!\n");
exit(1);
}
else
printf("Client Bind Port Success.\n");
// 设置一个socket地址结构server_addr,代表服务器的internet地址和端口
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
// 服务器的IP地址来自程序的参数
if (inet_aton(argv[1], &server_addr.sin_addr) == 0)
{
printf("Server IP Address Error!\n");
exit(1);
}
server_addr.sin_port = htons(SERVER_PORT);
int server_addr_length = sizeof(server_addr);
// 向服务器发起连接请求,连接成功后client_socket代表客户端和服务器端的一个socket连接
if (connect(client_socket, (struct sockaddr*)&server_addr, server_addr_length) < 0)
{
printf("Can Not Connect To %s!\n", argv[1]);
exit(1);
}
else
printf("Alreadly Connect To %s.\n", argv[1]);
char file_name[FILE_NAME_MAX_SIZE + 1];
bzero(file_name, sizeof(file_name));
printf("Please Input File Name On Server: ");
scanf("%s", file_name);
char buffer[BUFFER_SIZE];//缓存区
bzero(buffer, sizeof(buffer));
strncpy(buffer, file_name, strlen(file_name) > BUFFER_SIZE ? BUFFER_SIZE : strlen(file_name));
// 向服务器发送buffer中的数据,此时buffer中存放的是客户端需要接收的文件的名字
send(client_socket, buffer, BUFFER_SIZE, 0);
// send , sendto(), recv(),recvfrom()
FILE *fp = fopen(file_name, "w");
if (fp == NULL)
{
printf("File: %s Can Not Open To Write!\n", file_name);
exit(1);
}
// 从服务器端接收数据到buffer中
bzero(buffer, sizeof(buffer));
int length = 0;
while(length = recv(client_socket, buffer, BUFFER_SIZE, 0))
{
if (length < 0)
{
printf("Recieve Data From Server %s Failed!\n", argv[1]);
break;
}
int write_length = fwrite(buffer, sizeof(char), length, fp);
if (write_length < length)
{
printf("File:\t%s Write Failed!\n", file_name);
break;
}
bzero(buffer, BUFFER_SIZE);
}
printf("Recieve File: %s From Server[%s] Finished!\n", file_name, argv[1]);
// 传输完毕,关闭socket
fclose(fp);
close(client_socket);
return 0;
}
如图,客户端(左)从服务端(右)下载文件/图片:
二、UDP实现
UDP(User Datagram Protocol) 全称是用户数据报协议,是一种非面向连接的协议,这种协议并不能保证我们的网络程序的连接是可靠的。
1.服务端
基于UDP协议的socket的server编程步骤:
1、建立socket,使用socket()
2、绑定socket,使用bind()
3、以recvfrom()函数接收发送端传来的数据(使用recvfrom函数 时需设置非阻塞,以免程序卡在此处)
4、关闭socket,使用close()
点击查看代码
/*server.c*/
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define SERVER_PORT 5678 //端口号
#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;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(SERVER_PORT);
// create a stream socket
// 创建用于internet的流协议(UDP)socket,用server_socket代表服务器向客户端提供服务的接口
int server_socket = socket(PF_INET, SOCK_DGRAM, 0);
if (server_socket < 0)
{
printf("Create Socket Failed!\n");
exit(1);
}
else
printf("Create Socket Success.\n");
// 把socket和socket地址结构绑定
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))
{
printf("Server Bind Port: %d Failed!\n", SERVER_PORT);
exit(1);
}
else
printf("Server Bind Port Success.\n");
printf("Waiting......\n");
// 服务器端一直运行用以持续为客户端提供服务
while(1)
{
// 接受此请求,同时将client端的地址和端口等信息写入client_addr中
struct sockaddr_in client_addr;
int length = 0;
int addrlen = sizeof(client_addr);
char buffer[BUFFER_SIZE];
bzero(buffer, sizeof(buffer));
length = recvfrom(server_socket, buffer, BUFFER_SIZE, 0,(struct sockaddr *)&client_addr,&addrlen);
if (length < 0)
{
printf("Server Recieve Data Failed!\n");
break;
}
char file_name[FILE_NAME_MAX_SIZE + 1];
bzero(file_name, sizeof(file_name));
strncpy(file_name, buffer,
strlen(buffer) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buffer));
FILE *fp = fopen(file_name, "r");
if (fp == NULL)
{
printf("File:\t%s Not Found!\n", file_name);
}
else
{
bzero(buffer, BUFFER_SIZE);
int file_block_length = 0;
while( (file_block_length = fread(buffer, sizeof(char), BUFFER_SIZE, fp)) > 0)
{
// 发送buffer中的字符串到server_socket,实际上就是发送给客户端
if (sendto(server_socket, buffer, BUFFER_SIZE, 0,(struct sockaddr *)&client_addr,addrlen) < 0)
{
printf("Send File:\t%s Failed!\n", file_name);
break;
}
bzero(buffer, sizeof(buffer));
}
fclose(fp);
printf("File:\t%s Transfer Finished!\n", file_name);
}
}
close(server_socket);
return 0;
}
2.客户端
基于UDP协议的socket的client端编程步骤:
1、建立Socket,使socket()
2、用sendto()函数向接收端发送数据。
3、关闭socket,使用close()函数
点击查看代码
/*client.c*/
#include<netinet/in.h> // for sockaddr_in
#include<sys/types.h> // for socket
#include<sys/socket.h> // for socket
#include<stdio.h> // for printf
#include<stdlib.h> // for exit
#include<string.h> // for bzero
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#define SERVER_PORT 5678
#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[0]);
exit(1);
}
// 设置一个socket地址结构client_addr, 代表客户机的internet地址和端口
struct sockaddr_in client_addr;
bzero(&client_addr, sizeof(client_addr));
client_addr.sin_family = AF_INET; // internet协议族
client_addr.sin_addr.s_addr = htons(INADDR_ANY); // INADDR_ANY表示自动获取本机地址
client_addr.sin_port = htons(0); // auto allocated, 让系统自动分配一个空闲端口
// 创建用于internet的流协议(TCP)类型socket,用client_socket代表客户端socket
int client_socket = socket(AF_INET, SOCK_DGRAM, 0);
if (client_socket < 0)
{
printf("Create Socket Failed!\n");
exit(1);
}
// 设置超时,防止recvfrom()函数阻塞
struct timeval timeout;
timeout.tv_sec = 1;//秒
timeout.tv_usec = 0;//微秒
if (setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) {
perror("setsockopt failed:");
}
// 把客户端的socket和客户端的socket地址结构绑定
if (bind(client_socket, (struct sockaddr*)&client_addr, sizeof(client_addr)))
{
printf("Client Bind Port Failed!\n");
exit(1);
}
// 设置一个socket地址结构server_addr,代表服务器的internet地址和端口
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
// 服务器的IP地址来自程序的参数
if (inet_aton(argv[1], &server_addr.sin_addr) == 0)
{
printf("Server IP Address Error!\n");
exit(1);
}
server_addr.sin_port = htons(SERVER_PORT);
int server_addr_length = sizeof(server_addr);
char file_name[FILE_NAME_MAX_SIZE + 1];
bzero(file_name, sizeof(file_name));
printf("Please Input File Name On Server: ");
scanf("%s", file_name);
char buffer[BUFFER_SIZE];
bzero(buffer, sizeof(buffer));
strncpy(buffer, file_name, strlen(file_name) > BUFFER_SIZE ? BUFFER_SIZE : strlen(file_name));
// 向服务器发送buffer中的数据,此时buffer中存放的是客户端需要接收的文件的名字
if( sendto(client_socket, buffer, BUFFER_SIZE, 0,(struct sockaddr *)&server_addr,server_addr_length) )
printf("Waiting receive %s from server....\n",file_name);
FILE *fp = fopen(file_name, "w");
if (fp == NULL)
{
printf("File:\t%s Can Not Open To Write!\n", file_name);
exit(1);
}
// 从服务器端接收数据到buffer中
bzero(buffer, sizeof(buffer));
int length = 0;
//length = recvfrom(client_socket, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&server_addr, &server_addr_length)//非阻塞
while( length = recvfrom(client_socket, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&server_addr, &server_addr_length))
{
if (length < 0)
{
//printf("Recieve Data From Server %s Failed!\n", argv[1]);
break;
}
int write_length = fwrite(buffer, sizeof(char), length, fp);
if (write_length < length)
{
printf("File: %s Write Failed!\n", file_name);
break;
}
bzero(buffer, BUFFER_SIZE);
}
printf("Receive File: %s From Server[%s] Finished!\n", file_name, argv[1]);
// 传输完毕,关闭socket
fclose(fp);
close(client_socket);
return 0;
}
如图,客户端(左)从服务端(右)下载文件/图片:
注:使用recvfrom函数 时需设置非阻塞,以免程序卡住。
Socket通信-Linux系统中C语言实现TCP/UDP图片和文件传输的更多相关文章
- 在linux系统中配置NVMe over TCP
1. 准备环境 1.1 准备linux系统 要求的linux系统可以是运行在物理机上,也可以是虚拟机上: 建议有个linux系统,一个做host,一个做target,如果资源紧张也可以把host和ta ...
- Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)
转载地址:http://blog.csdn.net/mad1989/article/details/9147661 ZERO.前言 有关通信原理内容是在网上或百科整理得到,代码部分为本人所写,如果不当 ...
- 在linux系统中配置NVMe over FC
在linux系统中配置NVMe over FC与配置NVMe over TCP类似,前5步操作请参考<在linux系统中配置NVMe over TCP>,网页连接如下: https://w ...
- 获得Unix/Linux系统中的IP、MAC地址等信息
获得Unix/Linux系统中的IP.MAC地址等信息 中高级 | 2010-07-13 16:03 | 分类:①C语言. Unix/Linux. 网络编程 ②手册 | 4,471 次阅读 ...
- linux系统中文件的几种类型
Linux系统是以文件的形式来进行管理的.Linux文件类型常见的有:普通文件.目录.字符设备文件.块设备文件.符号链接文件等,如果想了解这方面知识的弟兄,就进来了解了解. Linux系统不同于win ...
- Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名
Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名 转载自:http://b ...
- 用户管理 之 Linux 系统中的超级权限的控制
在Linux操作系统中,root的权限是最高的,也被称为超级权限的拥有者.普通用户无法执行的操作,root用户都能完成,所以也被称之为超级管理用户. 在系统中,每个文件.目录和进程,都归属于某一个用户 ...
- Linux系统中“动态库”和“静态库”那点事儿【转】
转自:http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻. ...
- Linux系统中“动态库”和“静态库”那点事儿
摘自http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻.在 ...
- 为什么在 Linux 系统中,不建议超频
CPU 是一部计算机内的心脏啦!因为不论你做什么事情都需要 CPU 来加以运作的!(虽然有时工作量大.有时工作量小!),在 586 以前的计算机( 包含 386, 486, 与 586 ) ,CPU ...
随机推荐
- Shiro-BasicHttpAuthenticationFilter 鉴权过滤器的使用方式
它的作用是用来根据路径匹配结果,调用相应过滤器 onPreHandle 这里是正在的执行逻辑,之前的都是判断,它返回了两个方法: isAccessAllowed() onAccessDenied() ...
- helm values reference other values
https://helm.sh/docs/chart_template_guide/yaml_techniques/#yaml-anchors https://helm.sh/zh/docs/char ...
- 实践解决:IDEA2022版本创建Maven项目时没有出现src目录
问题:IDEA创建Maven项目时没有出现src目录 创建Maven项目 新版本的IDEA创建是选用的是Maven Archetype,选择这个也是和Maven一样的.按照这样流程创建完成之后的的架构 ...
- P10936 导弹防御塔 题解
题目链接 题目大意 城堡有 m 个敌人.n 个能发射导弹的防御塔.导弹的速度固定,都为 v.导弹需要 T1 秒发射,T2 分钟冷却,还需要防御塔到敌人距离的 dis/v 的时间.给定防御塔和敌人的坐标 ...
- 多线程极速导出/9字段10W行只需2秒/导入导出打印组件/功能丰富简单易用
一.功能特点 组件同时集成了导出数据到csv.xls.pdf和打印数据. 所有操作全部提供静态方法无需new,数据和属性等各种参数设置采用结构体数据,极为方便. 同时支持QTableView.QTab ...
- Qt采集本地摄像头推流成rtsp/rtmp(可网页播放/支持嵌入式linux)
一.功能特点 支持各种本地视频文件和网络视频文件. 支持各种网络视频流,网络摄像头,协议包括rtsp.rtmp.http. 支持将本地摄像头设备推流,可指定分辨率和帧率等. 支持将本地桌面推流,可指定 ...
- StarRocks元数据无法合并
一.先说结论 如果您的StarRocks版本在3.1.4及以下,并且使用了metadata_journal_skip_bad_journal_ids来跳过某个异常的journal,结果之后就出现了FE ...
- 使用Docker编译PaddlePaddle
在ubuntu中使用Docker编译PaddlePaddle 要在ubuntu中使用docker编译paddle框架,首先分为以下几个步骤: 安装docker环境 拉取paddle的docker镜像 ...
- Solution -「AGC 020F」Arcs on a Circle
\(\mathscr{Description}\) Link. 在一个周长为 \(c\) 的圆周上放置长度分别为 \(l_1,l_2,\cdots,l_n\) 的弧,每条弧的位置独立均匀随机. ...
- ABP 系列总结
2019年第一次接触 ABP 框架,那时候还是比较笨重的旧版本的,后来升级到 vNext 版本,我也基于 ABP 模块化的设计方式开发了一些模块用于日常工作.这个系列主要为了系统地记录一下日常工作与学 ...