TCP通信实现客户端向服务器发送图片

这篇具有很好参考价值的文章主要介绍了TCP通信实现客户端向服务器发送图片。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

TCP通信:

1. TCP 协议通信交互流程:
linuxc++ socket 网络编程之tcp方式图片等文件传输,tcp/ip,网络,服务器
具体的流程如下:
(1)服务器根据地址类型(ipv4、ipv6)、socket 类型、协议创建 socket.
(2)服务器为 socket 绑定 ip 地址和端口号。
(3)服务器 socket 监听端口号的请求,随时准备接受来自客户端的连接,此时服务器的 socket
处于关闭状态。
(4)客户端创建 socket。
(5)客户端打开 socket,根据服务器 ip 地址和端口号试图与服务器的 socket 建立连接。
(6)服务器的 socket 接收到来自客户端的 socket 连接请求之后,被动打开开始接收客户端的请
求,直到客户端返回连接建立成功的信息。在这个过程中,服务器的 socket 进入阻塞状态,也就
是 accept()方法一直到客户端返回连接信息后才返回开始接收下一个客户端的连接请求。
(7)客户端连接成功,向服务器发送连接状态信息。
(8)服务器用 accept() 方法返回,连接成功,接收来自客户端的连接请求。
(9)客户端用 send()方法向 socket 中写入信息。
(10)服务器通过 recv()方法从 socket 中读取信息。
(11)客户端通过 close()方法关闭 socket。
(12)服务器通过 close()方法关闭 socket。
linuxc++ socket 网络编程之tcp方式图片等文件传输,tcp/ip,网络,服务器
第一次握手:建立连接时,客户端发送 SYN 包(SYN=J)到服务器,并进入 SYNSEND 状态,
等待服务器确认。第二次握手:服务器收到 SYN 包,必须确认客户的 SYN ( ACK=J+1),同时自己
也发送一个 SYN 包( SYN=K),即 SYN+ACK 包,此时服务器进入 SYN RECV 状态。第三次握手:
客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK(ACK=K+I ),此包发送完毕,客户
端和服务器进入 ESTABLISHED 状态,完成 3 次握手。 网络层的 IP 地址可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机 中的应用程序(进程)。这样利用三元组(ip 地址,协议,端口)就可以标识网络的进程了,网络 中的进程通信就可以利用这个标志与其他进程进行交互 。网络中的进程是通过 socket 来通信的, socket 是“ open-write/read-close ”模式的一种实现,socket 即是一种特殊的文件, 一些 socket 函数就是对其进行的操作(读/写、打开、关闭)。TCP 服务器端依次调用 socket()、bind()、 listen()之后,就会监听指定的 socket 地址了。TCP 客户端依次调用 socket()、connect()之后就 会向 TCP 服务器发送了一个连接请求。TCP 服务器监听到这个请求之后,就会调用 accept()函数 取接收请求,这样连接就建立好了。
2. 主要函数
(1) socket()函数
socket 的函数原型如下所示:socket(int domain,int type,int protocol)。Socket()用于
创建一个 socket 描述符(
socket descriptor),它唯一标识一个 socket。这个 socket 描述符可
以作为参数,通过它来进行一些读写操作。socket 有三个参数:domain、type 和 protocol。其
中,domain 即协议域,又称 family。产用的协议域有: AF _INET、AF_INET6、AF_ LOCAL (或
称 AF_ UNIX, Unix 域 socket)、AF ROUTE 等。协议域决定了 socket 的地址类型、在通信中必
须采用对应的地址,例如 AFINET 决定了要用 ipv4 地址(32 位)与端口号(16 位)的组合,AFUNIX
决定了要用一个绝对路径名作为地址。而type用来指定socket类型。常用的socket类型有: SOCK.
STREAM、SOCK DGRAM、SOCK_ RAW、SOCK_ PACKET、 SOCK_ SEQPACKET 等。
其中,SOCK STREAM 表示提供面向连接的稳定数据传输,即 TCP 协议。SOCK DGRAM 表示
使用不连续、不可靠的数据包连接。最后,
protocol指定了 socket的协议。常用的协议有 IPPROTO_
TCP、IPPTOTO_ UDP、IPPROTO_SCTP、IPPROTO TIPC 等,它们分别对应 TCP 传输协议、
UDP 传输协议、STCP 传输协议、TIPC 传输协议。
当调用 socket 创建一个 socket 时,返回的 socket 描述字它存在于协议域(address family,
AF_ XXX)空间中,但没有一个具体的地址。如果想要给它赋予一个地址,就必须调用 bind()函数,
否则系统就会在调用 connect()、listen() 时自动随机分配一个端口。如果调用成功就返回新创建
的套接字的描述符,如果失败就返回 INVALIDSOCKET( Linux 下失败返回-1 )。套接字描述符是
一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符
和套接字数据结构的对应关系。套接字数据结构都存放在操作系统的内核缓冲里。
(2) bind()函数
bind()的函数原型是:int bind(
int sockfd, const struct sockaddr *addr, socklen_t
addrlen) 。
bind()函数把一个地址域中的特定地址赋给 socket。例如对应 AF INET,
AF_INETT6
就是把一个 ipv4 或 ipv6 地址和端口号组合赋给 socket。函数的有个参数:sockfd、addr 和
addrlen。其中 sockfd 是 socket 描述字,它是通过 socket()函数创建来唯一标识一个 socket 的。
bind()函数就是将给这个描述字绑定一个名字。而 addr 描述了一个 const struct sockaddr*指针,
指向要绑定给 sockfd 的协议地址。这个地址的结构根据地址创建 socket 时的地址协议域的不同
而不同,ipv4 和 ipv6 分别对应不同的代码。例如在本实验中采用 ipv4 协议,对应代码如下:
servaddr.sin_family = AF_INET;
//地址域(指定地址格式) ,设为 AF_INET
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //AF_INET:所有 IP 都可以连上
servaddr.sin_port = htons(6666);
//端口号
最后,addrlen 对应的是地址的长度。通常服务器在启动的时候都会绑定一个众所周知的地址
(如 ip 地址+端口号)。用于提供服务。客户就可以通过它来连接服务器,而客户端就不用指定,有 系统自动分配一个端口号和自身的 IP 地址组合。这就是为什么通常服务器端在调用 listen 之前会
调用 bind(),而客户端就不用调用,而是在 connect()时由系统随机生成一个。返回值:如果函数执
行成功,返回值为 0,反之为 SOCKET ERROR。
(3) listen()函数和 connect() 函数
listen()和 connect()函数的原型是:int listen(int sockfd , int backlog)和 int connect(int
sockfd , const struct sockaddr *addr, socklen_t addr len)。服务器在调用 socket()和 bind()
函数之后就会调用 listen()函数来监听这个 socket,如果客户端这时调用 connect()发出连接请求,
服务器端就会接收到这个请求从而建立连接。
listen()函数的第一个参数即为要监听的 socket 描述字,第二个参数为相应 socket 可以排队
的最大连接个数。socket()函数创建的 socket 默认是一个主动类型的,listen 函数将 socket 变为
被动类型的,等待客户的连接请求。connect()函数的第一个参数即为客户端的 socket 描述字,第
二参数为服务器的 socket 地址,第三个参数为 socket 地址的长度。客户端通过调用 connect()
函数来建立与 TCP 服务器的连接。
(4) accept()函数
建立好连接后,就可以开始网络 I/0 操作了,accept 的函数原型是:int accept(int sockfd,
struct sockaddr *addr, socklen_t *addrl en)。accept 函数的第一个参数为服务器的 socket 描
述字;第二个参数为指向 struct sockaddr*的指针,用于返回客户端的协议地址;第三个参数为
协议地址的长度。如果 accpet 成功,那么其返回值是由内核自动生成的一个全新的描述字,代表
与返回客户的 TCP 连接。一个服务器通常仅仅只创建一个监听 socket 描述字,它在该服务器的生
命周期内一直存在。内核为每个由服务器进程接受的客户创建了一个已连接 socket 描述字,当服
务器完成了对某个客户的服务,相应的已连接 socket 描述字就被关闭。
(5) read() 和 write() 函数
至此服务器与客户已经建立好连接了,可以调用网络 I/0 进行读写操作了,即实现了网络中
不同进程之间的通信。网络 I/0 操作有下面几组:
read()/write();
recv()/send();
readv()/writev();
recvmsg()/sendmsg();recvfom()/sendto()。
最常用的则是 read()和 write()。read() 的函数原型是:ssize_ t read(int fd, void *buf, size_ t
count)read()函数是负责从 fd 中读取内容。当读取成功时,read()返回实际所读的字节数,如果返
回的值是 0 表示已经读到文件的结束了,小于 0 表示出现了错误。如果错误为 EINTR 说明读是由
中断引起的,如果是 ECONNREST 表示网络连接出了问题。三个参数分别是代表 socket 描述字
(fd);缓冲区(buf);缓冲区长度(count)。write()的函数原型是:ssize_ t write(int fd, const
void *buf, size_ t count)。write()函数将缓冲区中的 nbytes 字节内容写人文件描述符 fd 成功时
返回写的字节数。失败时返回-1,并设置 errmo 变量。在网络程序中,当我们向套接字文件描述
符写时有两种可能:①write 的返回值大于 0,表示写了部分或者是全部的数据;②返回的值小于 0,
此时出现了错误。实际中要根据错误类型来处理。如果错误为 EINTR 表示在写的时候出现了中断
错误。如果为 EPIPE 表示网络连接出现了问题(对方已经关闭了连接)。
(6) close()函数
完成了读写操作就要关闭相应的socket 描述字。
close 的函数原型是:int close (int fd)。close 操作一般使相应 socket 描述字的引用计数值为-1,只有当引用计数为 0 的时候,才会触发 TCP
客户端向服务器发送终止连接请求。
3. 代码实现
(1) 服务器端 首先建立一个 socket 描述符,设置套接字的类型为:SOCK_STREAM,即表示提供面向连接 的稳定数据传输,即 TCP 协议。并把一个地址域中的特定地址赋给 socket。对应 AF INET,即 把一个 ipv4 地址和端口号组合赋给 socket。对应代码:listenfd = socket(AF_INET,
SOCK_STREAM, 0。接着,设置可连接的 ip 为任意 ip 值:sin_addr.s_addr =
htonl(INADDR_ANY),设置端口号:sin_port = htons(6666)。然后,使用 bind()函数将 servaddr
地址绑定到该 socket 描述符:bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))
== -1,再用 listen()函数让服务器处于监听模式等待客户端的连接,并设置最大连接数。接着使用
while 循环接受客户端的请求,在循环里用指向 client_addr 的指针来获取对方的地址 connfd =
accept(listenfd, (struct sockaddr*)&client_addr, &size)) == -1,然后用 read()函数接收客户
端发来的数据并重写缓冲区。最后结束连接,关闭套接字。
(2) 客户端
首先创建套接字并设置相关的协议,地址域、端口号及地址格式,并设置在终端获取客户端所
要连接的 ip 地址。然后采用 inet_pton()函数将字符串转换成网络地址,并复制到服务器的
sin_addr 中:inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0。接着用 connnect()
函数将两者连接起来,连接成功后,读取文件内容(/home/kkk/桌面/tt.jpg),发送给服务器端。
服务器端新建 tt.jpg 文件,将接收到的文件内容保存到 tt.jpg 中,并设置 tt.png 位于当前目录下。
最后完成连接,关闭套接字。

client.cpp为客户端实现代码
sever.cpp为服务器端实现代码
执行make命令后,生成server和client两个可执行文件。分别打开两个终端窗口,分别执行./server命令和./client 127.0.0.1命令,表示连上本机的6666端口,./server命令的首先执行。执行./client 127.0.0.1命令后,client客户端执行完毕直接退出,这时server的终端窗口输出“recv msg from client:”。打开server.cpp文件所在的目录,可看到tt.jpg文件已经生成。

client.cpp

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#define MAXLINE 4096

int main(int argc, char** argv){
    int   sockfd, len;
    char  buffer[MAXLINE];
    struct sockaddr_in  servaddr;    
    FILE *fq;

	//创建套接字,打印客户端所要连接的服务器ip地址:127.0.0.1
    if( argc != 2){
        printf("usage: ./client <ipaddress>\n");
        return 0;
    }

    if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
        return 0;
    }


	//设置远程地址信息
    memset(&servaddr, 0, sizeof(servaddr));	//先把servaddr地址清空,再复制
    servaddr.sin_family = AF_INET;		//地址族(指定地址格式) ,设为AF_INET
    servaddr.sin_port = htons(6666);		//连接服务器端口号



	// arg[1]是写ip地址的地方,inet_pton 是 IP 地址转换函数,可以在将 IP 地址在“点分十进制”和“二进制整数”之间转换
	//inet_pton函数的原型是:int inet_pton(int af,const char *src,void *dst)
	//这个函数将字符串转换成网络地址,第一个参数af=AF_INET,,src指向ASCII的地址的首地址(x.x.x.x的格式)
	//函数将该地址转换成in_addr的结构体并复制到*dst,即sin_addr中
    if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
        printf("inet_pton error for %s\n",argv[1]);
        return 0;
    }

    if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
        printf("connect error: %s(errno: %d)\n",strerror(errno),errno);		//连接失败响应函数
        return 0;
    }


	//连接成功后,读取文件内容(/home/kkk/桌面/tt.jpg),发送给服务器端
    if( ( fq = fopen("/home/kkk/桌面/tt.jpg","rb") ) == NULL ){
        printf("File open.\n");
        close(sockfd);
        exit(1);
    }

    bzero(buffer,sizeof(buffer));


	//服务器端新建tt.png文件,将接收到的文件内容保存到tt.png中,tt.jpg在当前目录下;
    while(!feof(fq)){
        len = fread(buffer, 1, sizeof(buffer), fq);
        if(len != write(sockfd, buffer, len)){
            printf("write.\n");
            break;
        }
    }
    close(sockfd);  // 关闭套节字 
    fclose(fq);

    return 0;
}

sever.cpp

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#define MAXLINE 4096

int main(int argc, char** argv){
    int  listenfd, connfd;
    struct sockaddr_in  servaddr;
    char  buff[4096];
    FILE *fp;
    int  n;
    
    //指定套接字的类型: SOCK_STREAM(即TCP协议,一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送。)
    //建立一个socket描述符,socket(ipv4,提供面向连接的稳定传输数据,指定协议为0),AFINET决定了要用ipv4地址(32位)与端口号(16位)的组合
    if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
        printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
        return 0;						//如果连接错误,打印错误提示
    }
    printf("----init socket----\n");  //初始化套接字

    memset(&servaddr, 0, sizeof(servaddr));		//先把servaddr地址清空,再复制
    servaddr.sin_family = AF_INET;			//套接字使用的地址族(指定地址格式):ipv4
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);	//AF_INET表示什么IP都可以连上
    servaddr.sin_port = htons(6666);			//端口号6666,1024 ~ 49151:普通用户注册的端口号


   	 //设置端口可重用
    int contain;
    setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &contain, sizeof(int));

	将servaddr地址绑定到该socket描述符
    if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
        printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
        return 0;
    }
    printf("----bind sucess----\n");



	//进入监听模式,监听这个socket描述符,10这里指的是监听队列中允许保持的尚未处理的最大连接数是10
    if( listen(listenfd, 10) == -1){
        printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
        return 0;
    }



    if((fp = fopen("tt.jpg","ab") ) == NULL )
    {
        printf("File.\n");
        close(listenfd);
        exit(1);
    }

    printf("======waiting for client's request======\n");


	// 循环接受客户的连接请求 
    while(1){
        struct sockaddr_in client_addr;
        socklen_t size=sizeof(client_addr);

		//一个指向client_addr的指针用于获取对方的地址
        if( (connfd = accept(listenfd, (struct sockaddr*)&client_addr, &size)) == -1){
            printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
            continue;
        }

		//从客户端接收数据
        while(1){

            n = read(connfd, buff, MAXLINE);  //recv 函数接收到的字符串是不带 ”\0”结束符的
            if(n == 0)
                break;
            fwrite(buff, 1, n, fp);
        }
        buff[n] = '\0';
        printf("recv msg from client: %s\n", buff); 要用 printf输出时必须得先加上结束符”\0"
        close(connfd);
        fclose(fp);
    }


		/*在 while 循环里持续接收包,注意, accept 和 read 是都是在 while 循环里的,
		也就是收到包之后,listenfd就没用了,并关闭它,下一个包重新接收包 。 
		*/
    close(listenfd);   //关闭监听套节字
    return 0;
}

运行结果:

linuxc++ socket 网络编程之tcp方式图片等文件传输,tcp/ip,网络,服务器

参考文章:

Linux C/C++ TCP Socket传输文件或图片实例 - zkfopen - 博客园

Socket原理及实践(Java/C/C++) - xiuzhublog - 博客园

 Linux C/C++ TCP Socket通信实例 - zkfopen - 博客园文章来源地址https://www.toymoban.com/news/detail-644956.html

到了这里,关于TCP通信实现客户端向服务器发送图片的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 网络通信(13)-C#TCP服务器和客户端同时在一个进程实现的实例

    有时项目需求中需要服务器和客户端同时在一个进程实现,一边需要现场接收多个客户端的数据,一边需要将数据汇总后发送给远程服务器。下面通过实例演示此项需求。 C#TCP服务器和客户端同时在一个进程实现的实例如下: 界面设计 UI文件代码

    2024年01月22日
    浏览(65)
  • S7-200SMART 实现MODBUS TCP通信的具体方法示例(客户端读写+服务器响应)

    前面和大家介绍了MODBUS TCP的基本使用方法,具体可参考以下链接中的内容: S7-200SMART实现MODBUS TCP通信(客户端+服务器)的具体方法和步骤示例 本次继续和大家分享S7-200SMART 中实现MODBUS TCP通信的具体方法 , 任务要求:

    2024年02月16日
    浏览(51)
  • 简易TCP客户端和服务器端通信

    #includeiostream #include winsock2.h   #include ws2tcpip.h   #includestdlib.h using namespace std; #define  BUF_SIZE  1024 int main() {     cout \\\"客户端\\\" endl;     //设置Winsock版本,     WSADATA   wsaData;     if (WSAStartup(MAKEWORD(2, 2), wsaData) != 0)     {         cout \\\"error\\\" endl;         exit(1);     }     //创建通

    2024年04月29日
    浏览(49)
  • 网络编程3——TCP Socket实现的客户端服务器通信完整代码(详细注释帮你快速理解)

    本人是一个刚刚上路的IT新兵,菜鸟!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果这篇文章可以帮助到你,劳请大家点赞转发支持一下! 今天分享的内容是TCP流套接字实现的客户端与服务器的通信,一定要理解 DatagramSocket,DatagramPacket 这两个类的作用以及方法

    2024年02月12日
    浏览(63)
  • Java【网络编程2】使用 TCP 的 Socket API 实现客户端服务器通信(保姆级教学, 附代码)

    📕各位读者好, 我是小陈, 这是我的个人主页 📗小陈还在持续努力学习编程, 努力通过博客输出所学知识 📘如果本篇对你有帮助, 烦请点赞关注支持一波, 感激不尽 📙 希望我的专栏能够帮助到你: JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统

    2024年02月05日
    浏览(64)
  • TCP/IP客户端和服务器端建立通信过程

    使用Qt提供的类进行基于 TCP 的套接字通信需要用到两个类: QTcpServer 类用于监听客户端连接以及和客户端建立连接,在使用之前先介绍一下这个类提供的一些常用API函数: 构造函数 给监听的套接字设置监听 listen() 函数 在代码中 通过启动监听按钮 设置监听 参数: address :

    2024年02月07日
    浏览(65)
  • Linux下TCP网络服务器与客户端通信程序入门

    实现客户端连接服务器,通过终端窗口发送信息给服务器端,服务器接收到信息后对信息数据进行回传,客户端读取回传信息并返回。 服务器当前IP地址要知道 建立socket 绑定本地IP地址并设置端口号 知道服务器的IP地址和端口号 然后进行连接

    2024年02月14日
    浏览(62)
  • TCP通信之QTcpServer和QTcpSocket,服务器和客户端通讯

    TCP传输控制协议 是一个可靠的(相对于UDP),面向流,面向连接的运输协议。 Socket 俗称“套接字”。就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地

    2024年02月12日
    浏览(66)
  • 【网络原理】使用Java基于TCP搭建简单客户端与服务器通信

    TCP服务器与客户端的搭建需要借助以下API ServerSocket 是创建TCP服务端Socket的API。 ServerSocket 构造方法 : 方法签名 方法说明 ServerSocket(int port) 创建一个服务端流套接字Socket,并绑定到指定端口 ServerSocket 方法: 方法签名 方法说明 Socket accept() 开始监听指定端口(创建时绑定的端

    2024年03月12日
    浏览(80)
  • Linux网络编程:socket、客户端服务器端使用socket通信(TCP)

    socket(套接字),用于网络中不同主机间进程的通信。 socket是一个伪文件,包含读缓冲区、写缓冲区。 socket必须成对出现。 socket可以建立主机进程间的通信,但需要协议(IPV4、IPV6等)、port端口、IP地址。          (1)创建流式socket套接字。                 a)此s

    2024年02月11日
    浏览(65)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包