c++通过tcpip收发数据_c++制作tcpip数据包 - CSDN
  • 因为项目需要,服务端需要一个SOCKET来接收客户端的请求,好吧,没办法度娘哇,结果很多都是linux的例子,功夫不负有心人啊,终于找到个demo,并且客户端代码详尽,记录之,以便以后查看。 一、Socket是什么 ...

      因为项目需要,服务端需要一个SOCKET来接收客户端的请求,好吧,没办法度娘哇,结果很多都是linux的例子,功夫不负有心人啊,终于找到个demo,并且客户端代码详尽,记录之,以便以后查看。

    一、Socket是什么

       Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

    二、一些基本概念

    1. TCP/IP
                 TCP/IP是个协议组,可分为三个层次:网络层、传输层和应用层。
                 在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。
                 在传输层中有TCP协议与UDP协议。
                 在应用层有:TCP包括FTP、HTTP、TELNET、SMTP等协议
                 UDP包括DNS、TFTP等协议

    2. 短连接:
                 连接->传输数据->关闭连接
                 HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。也可以这样说:短连接是指SOCKET连接后发送后接收完数据后马上断开连接。

    3. 长连接:
                 连接->传输数据->保持连接 -> 传输数据-> 。。。 ->关闭连接。
                 长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差。

    4. http的长连接:
                 HTTP也可以建立长连接的,使用Connection:keep-alive,HTTP 1.1默认进行持久连接。HTTP1.1和HTTP1.0相比较而言,最大的区别就是增加了持久连接支持(貌似最新的 http1.0 可以显示的指定 keep-alive),但还是无状态的,或者说是不可以信任的。
      什么时候用长连接,短连接?
                 长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况,。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。
      而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。
                 总之,长连接和短连接的选择要视情况而定

    5. 发送接收方式
      1、异步 
             报文发送和接收是分开的,相互独立的,互不影响。这种方式又分两种情况: 
             (1)异步双工:接收和发送在同一个程序中,由两个不同的子进程分别负责发送和接收 
             (2)异步单工:接收和发送是用两个不同的程序来完成。 
      2、同步 
             报文发送和接收是同步进行,既报文发送后等待接收返回报文。 同步方式一般需要考虑超时问题,即报文发出去后不能无限等待,需要设定超时时间,超过该时间发送方不再等待读返回报文,直接通知超时返回。
             在长连接中一般是没有条件能够判断读写什么时候结束,所以必须要加长度报文头。读函数先是读取报文头的长度,再根据这个长度去读相应长度的报文。  

    三、java socket建立连接的过程  

            1、 首先调用Socket类的构造函数,以服务器的指定的IP地址或指定的主机名和指定的端口号为参数,创建一个Socket流,在创建Socket流的过程中包含了向服务器请求建立通讯连接的过程实现。
           2、 建立了客户端通讯Socket后。就可以使用Socket的方法getInputStream()和getOutputStream()来创建输入/输出流。这样,使用Socket类后,网络输入输出也转化为使用流对象的过程。
           3、 使用输入输出流对象的相应方法读写字节流数据,因为流连接着通讯所用的Socket,Socket又是和服务器端建立连接的一个端点,因此数据将通过连接从服务器得到或发向服务器。这时我们就可以对字节流数据按客户端和服务器之间的协议进行处理,完成双方的通讯任务。
           4、 待通讯任务完毕后,我们用流对象的close()方法来关闭用于网络通讯的输入输出流,在用Socket对象的close()方法来关闭Socket。

    四、Socket 通信示例

           主机 A 的应用程序要能和主机 B 的应用程序通信,必须通过 Socket 建立连接,而建立 Socket 连接必须需要底层 TCP/IP 协议来建立 TCP 连接。建立 TCP 连接需要底层 IP 协议来寻址网络中的主机。我们知道网络层使用的 IP 协议可以帮助我们根据 IP 地址来找到目标主机,但是一台主机上可能运行着多个应用程序,如何才能与指定的应用程序通信就要通过 TCP 或 UPD 的地址也就是端口号来指定。这样就可以通过一个 Socket 实例唯一代表一个主机上的一个应用程序的通信链路了。

    五、建立通信链路

           当客户端要与服务端通信,客户端首先要创建一个 Socket 实例,操作系统将为这个 Socket 实例分配一个没有被使用的本地端口号,并创建一个包含本地和远程地址和端口号的套接字数据结构,这个数据结构将一直保存在系统中直到这个连接关闭。在创建 Socket 实例的构造函数正确返回之前,将要进行 TCP 的三次握手协议,TCP 握手协议完成后,Socket 实例对象将创建完成,否则将抛出 IOException 错误。
           与之对应的服务端将创建一个 ServerSocket 实例,ServerSocket 创建比较简单只要指定的端口号没有被占用,一般实例创建都会成功,同时操作系统也会为 ServerSocket 实例创建一个底层数据结构,这个数据结构中包含指定监听的端口号和包含监听地址的通配符,通常情况下都是“*”即监听所有地址。之后当调用 accept() 方法时,将进入阻塞状态,等待客户端的请求。当一个新的请求到来时,将为这个连接创建一个新的套接字数据结构,该套接字数据的信息包含的地址和端口信息正是请求源地址和端口。这个新创建的数据结构将会关联到 ServerSocket 实例的一个未完成的连接数据结构列表中,注意这时服务端与之对应的 Socket 实例并没有完成创建,而要等到与客户端的三次握手完成后,这个服务端的 Socket 实例才会返回,并将这个 Socket 实例对应的数据结构从未完成列表中移到已完成列表中。所以 ServerSocket 所关联的列表中每个数据结构,都代表与一个客户端的建立的 TCP 连接。

           备注:

            Windows 下单机最大TCP连接数

               调整系统参数来调整单机的最大TCP连接数,Windows 下单机的TCP连接数有多个参数共同决定:
               以下都是通过修改注册表[HKEY_LOCAL_MACHINE \System \CurrentControlSet \Services \Tcpip \Parameters]
               1.    最大TCP连接数        TcpNumConnections
               2.    TCP关闭延迟时间    TCPTimedWaitDelay    (30-240)s        
               3.    最大动态端口数   MaxUserPort  (Default = 5000, Max = 65534) TCP客户端和服务器连接时,客户端必须分配一个动态端口,默认情况下这个动态端口的分配范围为 1024-5000 ,也就是说默认情况下,客户端最多可以同时发起3977 个Socket 连接    
               4.   最大TCB 数量   MaxFreeTcbs系统为每个TCP 连接分配一个TCP 控制块(TCP control block or TCB),这个控制块用于缓存TCP连接的一些参数,每个TCB需要分配 0.5 KB的pagepool 和 0.5KB 的Non-pagepool,也就说,每个TCP连接会占用 1KB 的系统内存。非Server版本,MaxFreeTcbs 的默认值为1000 (64M 以上物理内存)Server 版本,这个的默认值为 2000。也就是说,默认情况下,Server 版本最多同时可以建立并保持2000个TCP 连接。
               5.   最大TCB Hash table 数量   MaxHashTableSize TCB 是通过Hash table 来管理的。这个值指明分配 pagepool 内存的数量,也就是说,如果MaxFreeTcbs = 1000 , 则 pagepool 的内存数量为 500KB那么 MaxHashTableSize 应大于 500 才行。这个数量越大,则Hash table 的冗余度就越高,每次分配和查找 TCP  连接用时就越少。这个值必须是2的幂,且最大为65536.

    六、服务端代码:

    1. /*     
    2.  * testSocketService.c     
    3.  *     
    4.  *  Created on: 2012-8-16     
    5.  *      Author: 皓月繁星     
    6.  */  
    7. #include <WINSOCK2.H>   
    8. #include <stdio.h>     
    9.                     
    10. #define PORT           5150    
    11. #define MSGSIZE        1024    
    12.                    
    13. #pragma comment(lib, "ws2_32.lib")      
    14.                    
    15. int main()      
    16. {      
    17.     WSADATA wsaData;      
    18.     SOCKET sListen;      
    19.     SOCKET sClient;      
    20.     SOCKADDR_IN local;      
    21.     SOCKADDR_IN client;      
    22.     char szMessage[MSGSIZE];      
    23.     int ret;      
    24.     int iaddrSize = sizeof(SOCKADDR_IN);      
    25.     WSAStartup(0x0202, &wsaData);      
    26.                    
    27.     sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);      
    28.                    
    29.     local.sin_family = AF_INET;      
    30.     local.sin_port = htons(PORT);      
    31.     local.sin_addr.s_addr = htonl(INADDR_ANY);      
    32.     bind(sListen, (struct sockaddr *) &local, sizeof(SOCKADDR_IN));      
    33.                    
    34.     listen(sListen, 1);      
    35.                    
    36.     sClient = accept(sListen, (struct sockaddr *) &client, &iaddrSize);      
    37.     printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr),      
    38.             ntohs(client.sin_port));      
    39.                    
    40.     while (TRUE) {      
    41.         ret = recv(sClient, szMessage, MSGSIZE, 0);      
    42.         szMessage[ret] = '\0';      
    43.         printf("Received [%d bytes]: '%s'\n", ret, szMessage);      
    44.     }      
    45.     return 0;      
    46.   }  

    七、客户端代码

    1. /*     
    2.  * testSocketClient.c     
    3.  *     
    4.  *  Created on: 2012-8-16     
    5.  *      Author: 皓月繁星     
    6.  */  
    7. #include <WINSOCK2.H>   
    8. #include <stdio.h>     
    9.                     
    10. //定义程序中使用的常量      
    11. #define SERVER_ADDRESS "127.0.0.1" //服务器端IP地址      
    12. #define PORT           5150         //服务器的端口号      
    13. #define MSGSIZE        1024         //收发缓冲区的大小      
    14. #pragma comment(lib, "ws2_32.lib")      
    15.                    
    16. int main()      
    17. {      
    18.     WSADATA wsaData;      
    19.     //连接所用套节字      
    20.     SOCKET sClient;      
    21.     //保存远程服务器的地址信息      
    22.     SOCKADDR_IN server;      
    23.     //收发缓冲区      
    24.     char szMessage[MSGSIZE];      
    25.     //成功接收字节的个数      
    26.     int ret;      
    27.                    
    28.     // Initialize Windows socket library      
    29.     WSAStartup(0x0202, &wsaData);      
    30.                    
    31.     // 创建客户端套节字      
    32.     sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //AF_INET指明使用TCP/IP协议族;      
    33.                                                          //SOCK_STREAM, IPPROTO_TCP具体指明使用TCP协议      
    34.     // 指明远程服务器的地址信息(端口号、IP地址等)      
    35.     memset(&server, 0, sizeof(SOCKADDR_IN)); //先将保存地址的server置为全0      
    36.     server.sin_family = PF_INET; //声明地址格式是TCP/IP地址格式      
    37.     server.sin_port = htons(PORT); //指明连接服务器的端口号,htons()用于 converts values between the host and network byte order      
    38.     server.sin_addr.s_addr = inet_addr(SERVER_ADDRESS); //指明连接服务器的IP地址      
    39.                                                         //结构SOCKADDR_IN的sin_addr字段用于保存IP地址,sin_addr字段也是一个结构体,sin_addr.s_addr用于最终保存IP地址      
    40.                                                         //inet_addr()用于将 形如的"127.0.0.1"字符串转换为IP地址格式      
    41.     //连到刚才指明的服务器上      
    42.     connect(sClient, (struct sockaddr *) &server, sizeof(SOCKADDR_IN)); //连接后可以用sClient来使用这个连接      
    43.                                                                         //server保存了远程服务器的地址信息      
    44.     while (TRUE) {      
    45.         printf("Send:");      
    46.         //从键盘输入      
    47.         gets(szMessage); //The gets() functionreads characters from stdin and loads them into szMessage      
    48.         // 发送数据      
    49.         send(sClient, szMessage, strlen(szMessage), 0); //sClient指明用哪个连接发送; szMessage指明待发送数据的保存地址 ;strlen(szMessage)指明数据长度      
    50.     }      
    51.                    
    52.     // 释放连接和进行结束工作      
    53.     closesocket(sClient);      
    54.     WSACleanup();      
    55.     return 0;      
    56. }  

    测试图例:

    展开全文
  • 本篇文章实现了一个基于TCP 的

    本篇文章实现了一个基于TCP 的一个非常简单的客户/服务器通信程序实例。该程序中通讯协议使用的是面向连接的TCP协议SOCK_STREAM, 服务器的ip地址为本地地址即:

    127.0.0.1,端口号为自定义的5099(大于1024即可),服务端的功能只要有客户端连接请求,服务端就接受连接,在连接成功后向客户端发送数据,并且接受客户端发送的

    数据。客户端在向服务端发出请求后接受并显示服务端的数据,并且给服务端也发送数据。

    以下代码是服务端程序代码:

    #include <WinSock2.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    #pragma comment(lib, "ws2_32.lib")
    
    void main()
    {
    	WSADATA wsaData;
    	int port = 5099;
    
    	char buf[] = "Server: hello, I am a server....."; 
    
    	if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    	{
    		printf("Failed to load Winsock");
    		return;
    	}
    
    	//创建用于监听的套接字
    	SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
    
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_port = htons(port); //1024以上的端口号
    	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    
    	int retVal = bind(sockSrv, (LPSOCKADDR)&addrSrv, sizeof(SOCKADDR_IN));
    	if(retVal == SOCKET_ERROR){
    		printf("Failed bind:%d\n", WSAGetLastError());
    		return;
    	}
    
    	if(listen(sockSrv,10) ==SOCKET_ERROR){
    		printf("Listen failed:%d", WSAGetLastError());
    		return;
    	}
    
    	SOCKADDR_IN addrClient;
    	int len = sizeof(SOCKADDR);
    
    	while(1)
    	{
    		//等待客户请求到来	
    		SOCKET sockConn = accept(sockSrv, (SOCKADDR *) &addrClient, &len);
    		if(sockConn == SOCKET_ERROR){
    			printf("Accept failed:%d", WSAGetLastError());
    			break;
    		}
    
    		printf("Accept client IP:[%s]\n", inet_ntoa(addrClient.sin_addr));
    
    		//发送数据
    		int iSend = send(sockConn, buf, sizeof(buf) , 0);
    		if(iSend == SOCKET_ERROR){
    			printf("send failed");
    			break;
    		}
    
     		char recvBuf[100];
     		memset(recvBuf, 0, sizeof(recvBuf));
    // 		//接收数据
     		recv(sockConn, recvBuf, sizeof(recvBuf), 0);
     		printf("%s\n", recvBuf);
    
    		closesocket(sockConn);
    	}
    
    	closesocket(sockSrv);
    	WSACleanup();
    	system("pause");
    }

    接下来是客户端代码:

    #include <WinSock2.h>
    #include <stdio.h>
    
    #pragma comment(lib, "ws2_32.lib")
    
    void main()
    {
    	//加载套接字
    	WSADATA wsaData;
    	char buff[1024];
    	memset(buff, 0, sizeof(buff));
    
    	if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    	{
    		printf("Failed to load Winsock");
    		return;
    	}
    
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_port = htons(5099);
    	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    
    	//创建套接字
    	SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
    	if(SOCKET_ERROR == sockClient){
    		printf("Socket() error:%d", WSAGetLastError());
    		return;
    	}
    
    	//向服务器发出连接请求
    	if(connect(sockClient, (struct  sockaddr*)&addrSrv, sizeof(addrSrv)) == INVALID_SOCKET){
    		printf("Connect failed:%d", WSAGetLastError());
    		return;
    	}else
    	{
    		//接收数据
    		recv(sockClient, buff, sizeof(buff), 0);
    		printf("%s\n", buff);
    	}
    
    	//发送数据
    	char buff = "hello, this is a Client....";
    	send(sockClient, buff, sizeof(buff), 0);
    
    	//关闭套接字
    	closesocket(sockClient);
    	WSACleanup();
    }

    运行结果:






    展开全文
  • c++ mfc tcpip
  • tcpip通信的c++代码

    2019-08-19 19:38:02
    tcpip通信server端的c++代码 #include <iostream> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> #include <sstream> #include...

    tcpip通信server端的c++代码

    
    #include <iostream>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <string.h>
    #include <sstream>
    #include <errno.h>
    
    #define SERV_PORT 8888        //服务器端口
    #define SERV_IP "192.168.2.117"   //服务器ip
    
    using namespace std;
    
    int main(int argc,char** argv)
    {
    
        int servfd,clitfd;   //创建两个文件描述符,servfd为监听套接字,clitfd用于数据传输
        struct sockaddr_in serv_addr,clit_addr; //创建地址结构体,分别用来存放服务端和客户端的地址信息
    
        memset(&serv_addr,0,sizeof(serv_addr));  //初始化
        memset(&clit_addr,0,sizeof(clit_addr));  //初始化
    
        if((servfd = socket(AF_INET,SOCK_STREAM,0)) == -1)  //创建套接字
        {
            cout<<"creat socket failed : "<<strerror(errno)<<endl;//如果出错则打印错误
            return 0;
        }
    
            //给服务端的地址结构体赋值
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(SERV_PORT); //将主机上的小端字节序转换为网络传输的大端字节序(如果主机本身就是大端字节序就不用转换了)
        serv_addr.sin_addr.s_addr = inet_addr(SERV_IP); //将字符串形式的ip地址转换为点分十进制格式的ip地址
    
            //绑定地址信息到监听套接字上,第二个参数强转是因为形参类型为sockaddr ,而实参类型是sockaddr_in 型的
        if(bind(servfd,(sockaddr *)& serv_addr,sizeof(serv_addr)) == -1)
        {
            cout<<"bind failed : "<<strerror(errno)<<endl;
            return 0;
        }
    
            //将servfd套接字置为监听状态
        if(listen(servfd,1024) == -1)
        {
            cout<<"listen failed : "<<strerror(errno)<<endl;
            return 0;
        }
    
        cout<<"Init Success ! "<<endl;
        cout<<"ip : "<<inet_ntoa(serv_addr.sin_addr)<<"  port : "<<ntohs(serv_addr.sin_port)<<endl;
        cout<<"Waiting for connecting ... "<<endl;
    
        socklen_t clit_size = 0; //用于accept函数中保存客户端的地址结构体大小
    
            //accept成功后,clitfd则指向了这条服务端与客户端成功连接的”通路“
        if((clitfd = accept(servfd,(sockaddr *)& clit_addr,&clit_size)) == -1)
        {
            cout<<"accept failed : "<<strerror(errno)<<endl;
            return 0;
        }
    
        cout<<"Client access : "<<inet_ntoa(clit_addr.sin_addr)<<"  "<<ntohs(clit_addr.sin_port)<<endl;
    
    
        char buf[1024]; //用于读写数据
    
        while(1)
        {
            int rdstate;
            if((rdstate = read(clitfd,buf,sizeof(buf))) > 0 )//通过clitfd来读取数据,返回值为读取的长度
            {
                int i=0;
                cout<<"(Server)recv : ";
                for(i=0;i<rdstate;i++)
                {
                    cout<<buf[i];
                    buf[i] = toupper(buf[i]); //转换为大写
                }
                buf[i]='\0';
                cout<<endl;
                write(clitfd,buf,strlen(buf)); //发回客户端
            }
                    else if(rdstate == 0)  //客户端退出
            {
                cout<<"client exit ! "<<endl;
                return 0;
            }
        }
    
        close(servfd);  //关闭套接字
        close(clitfd);
        return 0;
    }
    

    tcpip通信client段的c++代码

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <stdio.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <sys/shm.h>
    
    #define MYPORT  502
    #define BUFFER_SIZE 1024
    
    int main() {
        ///定义sockfd
        int sock_cli = socket(AF_INET, SOCK_STREAM, 0);
    
        ///定义sockaddr_in
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(MYPORT);  ///服务器端口
        servaddr.sin_addr.s_addr = inet_addr("192.168.2.171");  ///服务器ip
    
        //连接服务器,成功返回0,错误返回-1
        if (connect(sock_cli, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
            perror("connect");
            exit(1);
        }
    
        char sendbuf[BUFFER_SIZE];
        char recvbuf[BUFFER_SIZE];
        while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) {
            send(sock_cli, sendbuf, strlen(sendbuf), 0); ///发送
            if (strcmp(sendbuf, "exit\n") == 0)
                break;
            recv(sock_cli, recvbuf, sizeof(recvbuf), 0); ///接收
            fputs(recvbuf, stdout);
    
            memset(sendbuf, 0, sizeof(sendbuf));
            memset(recvbuf, 0, sizeof(recvbuf));
        }
    
        close(sock_cli);
        return 0;
    }
    

    tcpip通信client端的c++代码(为connect()函数和recv()函数设置的最大阻塞时间10s)

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <stdio.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <sys/shm.h>
    #include <sys/time.h>
    #include <iostream>
    #include <errno.h>
    
    #define MYPORT  502
    #define BUFFER_SIZE 1024
    
    int main() {
        ///定义sockfd
        int sock_cli = socket(AF_INET, SOCK_STREAM, 0);
    
        ///定义sockaddr_in
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;//该参数表示ipv4协议
        servaddr.sin_port = htons(MYPORT);  ///服务器端口
        servaddr.sin_addr.s_addr = inet_addr("192.168.2.171");  ///服务器ip
    
    //    /** connect函数会阻塞 默认阻塞时间貌似为70秒 **/
    //    //连接服务器,成功返回0,错误返回-1
    //    if (connect(sock_cli, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
    //        perror("connect");
    //        exit(1);
    //    }
    
        /**1.建立socket(前面代码已完成)
           2.将该socket设置为非阻塞模式
           3.调用connect()
           4.使用select()检查该socket描述符是否可写(注意,是可写)
           5.根据select()返回的结果判断connect()结果
           6.将socket设置为阻塞模式**/
        int flags;
        if((flags = fcntl(sock_cli, F_GETFL, 0)) < 0) {//备份原始的socket设置参数给flags
            perror("Netwrok test...\n");
            close(sock_cli);
            return -1;
        }
        if(fcntl(sock_cli, F_SETFL, flags | O_NONBLOCK) < 0) {//在原始参数基础上增加参数O_NONBLOCK(表示非阻塞模式)
            perror("Network test...\n");
            close(sock_cli);
            return -1;
        }
    
        if(connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) {
            //非阻塞模式下连接,一般会返回连接失败状态,错误码为EINPROGRESS(表示正在建立连接)
            if(errno != EINPROGRESS) { // EINPROGRESS
                perror("Network test...\n");
                close(sock_cli);
                return -1;
            }
        }
        else {
            printf("Connected\n");
            return 0;
        }
    
        fd_set fdr, fdw;
        FD_ZERO(&fdr);
        FD_ZERO(&fdw);
        FD_SET(sock_cli, &fdr);
        FD_SET(sock_cli, &fdw);
        struct timeval connect_timeout={10,0};//s
        
        //select()为阻塞函数,时刻查询sock_cli的状态
        int res = select(sock_cli + 1, &fdr, &fdw, NULL, &connect_timeout);
    
        if(res < 0) {
            perror("Network test...\n");
            close(sock_cli);
            return -1;
        }
    
        if(res == 0) {
            printf("Connect server timeout");
            close(sock_cli);
            return -1;
        }
    
        if(res == 1) {
            if(FD_ISSET(sock_cli, &fdw))
            {
                printf("Connected\n");
                //close(sock_cli);
                //return 0;
            }
        }
    
        if(fcntl(sock_cli, F_SETFL, flags) < 0) {
            perror("Network test...\n");
            //close(sock_cli);
            //return -1;
        }
        /****/
    
        //设置recv()函数最大阻塞时间
        struct timeval recving_timeout={10,0};//s
        //int ret = setsockopt(sock_cli,SOL_SOCKET,SO_SNDTIMEO,(const char*)&timeout,sizeof(timeout));
        int set_blocking_time = setsockopt(sock_cli,SOL_SOCKET,SO_RCVTIMEO,(const char*)&recving_timeout,sizeof(recving_timeout));
        if(set_blocking_time < 0){
            std::cout<<"设置recv()函数最大阻塞时间failed"<<std::endl;
        }
    
        char sendbuf[BUFFER_SIZE];
        char recvbuf[BUFFER_SIZE];
        //fgets()函数阻塞
        while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) {
            send(sock_cli, sendbuf, strlen(sendbuf), 0); ///发送
            if (strcmp(sendbuf, "exit\n") == 0)
                break;
            //recv()函数阻塞
            int recvd = recv(sock_cli, recvbuf, sizeof(recvbuf), 0); ///接收
            if(recvd==-1)
            {
                std::cout<<"recv faild."<<std::endl;
            }
            fputs(recvbuf, stdout);
    
            memset(sendbuf, 0, sizeof(sendbuf));
            memset(recvbuf, 0, sizeof(recvbuf));
        }
    
        close(sock_cli);
        return 0;
    }
    

    清除cocket缓存区数据函数

    /** 清除缓存区数据 **/
    void cleanBuff(int sock_conn){
        // 设置select立即返回
        timeval time_out;
        time_out.tv_sec = 0;
        time_out.tv_usec = 0;
    
        // 设置select对sock_conn的读取感兴趣
        fd_set read_fds;
        FD_ZERO(&read_fds);
        FD_SET(sock_conn, &read_fds);
    
        int res = -1;
        char recv_data[2];
        memset(recv_data, 0, sizeof(recv_data));
        while(true){
            res = select(FD_SETSIZE, &read_fds, nullptr, nullptr, &time_out);
            if (res == 0) break;  //数据读取完毕,缓存区清空成功
            recv(sock_conn, recv_data, 1, 0);  //触发数据读取
        }
    }

     

    展开全文
  • 有段时间没有更新博客了,近来比较忙,...任务是某设备通过WIFI以TCP/IP的协议发送图像数据,数据按照规定的报文协议接收数据。 报文内容分为控制域(8个字节)与数据域(不定长),报文的启动字符为0628H占两个字

    有段时间没有更新博客了,近来比较忙,没有顾上写博客。终于完成了一个大任务,有时间回顾一下这段时间的成果。这篇博客,先介绍和总结一下很久前的工作。TCP/IP接收数据拼包。由于时间太长很多东西记不清楚了,请见谅。

    任务是某设备通过WIFI以TCP/IP的协议发送图像数据,数据按照规定的报文协议接收数据。

    报文内容分为控制域(8个字节)与数据域(不定长),报文的启动字符为0628H占两个字节,接下来两个字节是报文长度(除去控制域本身之外的所有字节长度,因此加上启动字符在内的完整的报文为报文长度的值+8个字节)。控制域后面4个字节预留。数据域前2个字节为数据类型,接下来2个字节为数据内容长度,接下来2个字节为帧类型,接下来2个字节标志位标示是否有后续帧,然后是真正的图像数据内容(由于图像数据内容很大,一帧报文数据可能发不完,因此分多帧发送,后续帧标志位就标志某一帧图像数据是否发完)。

    然后约定,所有数据类型按小端对齐(低字节在前。当然也可以约定大端对齐,高字节在前。)

    好的,协议定好,接下来就开始发送数据和接收数据把。不过,发送数据的工作不在我这边,我只负责接收。不过,发送数据的工作跟接收数据的工作可以互相参考一下。整理一下我的任务,我需要通过TCP接收发过来的数据,识别出启动字符和报文长度,然后按报文长度的值接收报文。接收完一帧报文后,开始解包操作。由于一帧完整的图像可能分多帧报文发,所以,解包的时候需要注意是否有后续帧,数据是否完整了。

    好,网上如何TCP接收数据的代码很多,先上代码。

    [cpp] view plain copy
    1. #include <stdio.h>  
    2. #include<sys/socket.h>  
    3. #include<arpa/inet.h> //inet_addr  
    4. #include<netdb.h> //hostent  
    5. #include <sys/types.h>  
    6. #include <assert.h>  
    7. #include<string.h> //strcpy  
    8. #include<map>  
    9. #include<vector>  
    10. #include<fstream>  
    11. #include<unistd.h>  
    12. using namespace std;  
    13. #pragma pack(push, 1)  
    14. static int stepSize=0;  
    15.   
    16.   
    17. void handleDataUint(char *dataUnit, int size)  
    18. {  
    19.   //得到数据之后,在这里进行拼包或者进行下一步处理等操作  
    20. }  
    21.   
    22. int main(int argc, char **argv)  
    23. {  
    24.      
    25.       int socket_desc,rcv_size;  
    26.       int err=-1;  
    27.       socklen_t optlen;  
    28.       struct sockaddr_in server;//定义服务器的相关参数  
    29.       char server_reply[5000];  
    30.   
    31.       //Create socket  
    32.       //下面的AF_INET也可以用PF_INET。AF_INET主要是用于互联网地址,而 PF_INET 是协议相关,通常是sockets和端口  
    33.       socket_desc = socket(AF_INET , SOCK_STREAM , 0);//第二个参数是套接口的类型:SOCK_STREAM或SOCK_DGRAM。第三个参数设置为0。  
    34.       if (socket_desc == -1)  
    35.       {  
    36.           printf("Could not create socket");  
    37.       }  
    38.       rcv_size = 4*640000;    /* 接收缓冲区大小为4*640K */  
    39.       optlen = sizeof(rcv_size);  
    40.       err = setsockopt(socket_desc,SOL_SOCKET,SO_RCVBUF, (char *)&rcv_size, optlen);//设置套接字,返回值为-1时则设置失败  
    41.       if(err<0){  
    42.               printf("设置接收缓冲区大小错误\n");  
    43.       }  
    44.       server.sin_addr.s_addr = inet_addr("192.168.10.2");//服务器IP地址  
    45.       server.sin_family = AF_INET;//对应与socket,也可选PF_INET  
    46.       server.sin_port = htons( 52404 );//端口号  
    47.   
    48.       if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0)//用建立的socket尝试同设置好的服务器connect  
    49.       {  
    50.           perror("connect error:");  
    51.           return 1;  
    52.       }  
    53.       printf("Connected");  
    54.   
    55.       char recbuff[800000];//接收的数据缓存大小,这是我自己设置的区域,为了存放报文  
    56.       unsigned long buffsize;  
    57.       int packetCount = 0;  
    58.   
    59.   
    60.       buffsize=0;//该值标示当前缓存数据的大小,及下一帧数据存放的地址  
    61.           while (1) {  
    62.   
    63.   
    64.               int readSize = recv(socket_desc, server_reply , sizeof(server_reply) , 0);//从服务器接收数据,其中第三个参数为单次接收的数据大小  
    65. //同上面的rcv_size区分开,上面的rcv_size是TCP/IP的机理,传输过程中,数据会暂时先存储在rcv_size里.  
    66. //然后你recv再从rcv_size这个缓冲里取你设置的sizeof(serve_reply)的数据。其中readSize为recv的返回值,该值返回你实际接收到的数据大小,这点要注意。  
    67. //接收到的数据放在server_reply[5000]里面  
    68.               if(readSize < 0) {//实际接收到的数据为负,表示接收出错  
    69.                   printf("recv failed");  
    70.                   //should shut down connection and reconnect!  
    71.                   assert(false);  
    72.                   return 1;  
    73.               }  
    74.               else if (readSize == 0) {//表示无数据传输,接收到数据为0  
    75.                   printf("readSize=0");  
    76.                   break;  
    77.               }  
    78.               ++packetCount;//接收到包的次数加1  
    79.   
    80.               memcpy(recbuff + buffsize, server_reply, readSize);//memcpy为内存拷贝函数,将该次接收到的server_reply的数据拷贝到recbuff里  
    81. //其中+buffsize,从recbuff头地址+buffsize的地址开始拷贝(第一次buffsize=0,及从头开始拷贝)拷贝的大小为readSize,即recv实际接收到的数据大小。  
    82.               buffsize += readSize;//buffsize=buffsize+readSize,此时buffsize指向该次拷贝的数据大小的下一位。  
    83.               const int packetHeadSize = 8;//定义控制域的数据大小为8个字节  
    84.               static int expectedPacketSize = -1;//定义期望得到的数据包大小为-1,以此判断本次接收到的数据是否是数据头部  
    85.               if (expectedPacketSize == -1 && buffsize >= packetHeadSize) {//接收到的数据比8个字节大,即包含了控制域及数据域,则进行下一步分析  
    86.                   //start token must be 0x0628, otherwise reconnect!  
    87.                   unsigned char t0 = recbuff[0];//取出接收数据的前两个字节  
    88.                   unsigned char t1 = recbuff[1];  
    89.                   assert(t0 == 0x28 && t1 == 0x06);//判断是否为0628H,是否符合启动字符条件,注意小端对齐  
    90.                   if (!(t0 == 0x28 && t1 == 0x06))//如果不是0628H,则表示该次数据有误  
    91.                   {  
    92.                       return 1;  
    93.                   }  
    94.                   //find packet length!!  
    95.   
    96.                   unsigned short len = 0;//定义报文长度  
    97.                   memcpy(&len, recbuff + 2, 2);//将接收数据的下第三第四个字节付给len,根据协议第三第四个字节存储的是该帧报文的长度  
    98.                   expectedPacketSize = len + packetHeadSize;//则期望得到的数据包大小为报文长度加控制域长度  
    99.               }  
    100.   
    101.               //get one whole packet!  
    102.               if (expectedPacketSize != -1 && buffsize >= expectedPacketSize) {//当期望得到的数据包大小不是-1  
    103.                   // 并且recbuff里接收到的数据大小已经大于所需要的数据大小,如果接收到的数据小于完整报文的长度,则继续接收  
    104.                   //  
    105.                   //下面为接收到的完整的一帧报文,定义了一个解包函数负责解包,从缓存数据的第9个字节开始取,取完整数据域长度的数据,即只取数据域的内容  
    106.                   handleDataUint(recbuff + packetHeadSize, expectedPacketSize -packetHeadSize);  
    107. //下面的memmove函数是内存移动函数,将下一帧报文移动到recbuff的起始处,覆盖掉已经取出的数据  
    108.                   memmove(recbuff, recbuff + expectedPacketSize, buffsize - expectedPacketSize);  
    109.                   buffsize -= expectedPacketSize;  
    110.                   expectedPacketSize = -1;  
    111.               }  
    112.           }  
    113.       return 0;  
    114. }  
    115. #pragma pack(pop)  

    其中,涉及到缓存区的数据处理,主要是memcpy,memmove等函数的使用,且buffsize,expectedPacketSize等数据大小的使用。buffsize不仅可以表示目前recbuff缓存区已经存入的数据大小,而且还表征了下一帧要存放的数据地址。而expectedPacketSize=-1可以用于判断某次recv接收到的数据是否完毕,是否含有报文的开头,不等于-1的时候又可以表示期望获得的完整一帧报文的数据大小。

    由于TCP/IP的限制,一帧很大的报文需要分次多次发送,这样就需要将多次发送过来的包进行拼包处理,已避免粘包等情况。

    以上是我用C++的拼包的代码。希望有用哈~

    时间太长,很多别的东西记不住了,就先这样吧,我得去忙了。

    展开全文
  • 实现: 客户端往服务器端发送一条数据,服务器端接收数据并输出; 服务器端再发送出接收到的数据给客户端! 根据自己的脑洞,想一下是否可以通过的改进实现简单的QQ聊天室! 少说废话,上代码 [文字描述都写在注释啦,也就...
  • C++中的TCP通信

    2019-07-18 13:30:47
    这两天简单地看了下C++的socket通信,说起socket通信,就不得不提及TCP/IP 协议,这个协议大名鼎鼎,我想看过编程的至少听说过。在TCP/IP协议下,最常见的就是TCP和UDP,不过C++中的UDP我还没有看过,今天就简单说说...
  • C++ TCP/IP通信

    2019-05-27 14:31:38
    数据报套接字 代码实例通信: 服务器端代码: 1 // socketserver.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include "conio.h" 6 #include "windows.h" 7 //socket头文件 8 #...
  • C++发送字节数据

    2020-05-30 19:57:02
    项目的甲方要求我们做一个C++的发送代码,给他们那个模块做测试,由于我们的报文在接收端需要按照字节读取,然后完整项目中用的是QT的接口,所以我用C++勉强实现了一下 码来 //#include<tchar.h> #include <...
  • 这是一个TCP/IP的程序,通过ONRECEIVE接收客户端连接,接收数据进行显示,并且能够显示到界面上,把符合条件的数据保存到SQLSERVER数据库中。其中,还有LISTBOX的横向滚动条的显示的内容,每次接收的内容滚动到控件...
  • 有许多朋友希望使用Matlab作客户端(因为Matlab处理数据比较方便),C++程序作服务器,两者之间进行通讯。其中有些人会遇到问题,接收字串的格式总是出错,本篇文章就解释一下如何实现。 注意:此处的通信仍限制于...
  • 所谓的回射是指:客户端A向服务端B发送数据,服务端B接收数据之后,再将接收到的数据发送回客户端B。所谓的迭代服务器,是指服务器端只用一个进程处理或线程处理所有客户端的请求。与之对应的是并发服务器,并发...
  • 客户端代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows....u...
  • c++实现socket以json格式传输图片 原文链接 主要任务: 1、C++实现socket通信 2、socket传输数据要封装成json格式 3、json传输字符串 4、json传输图片 大致流程: 客户端读取图片,经过Base64编码,转成字符...
  • TCP的数据发送和接收

    2013-10-25 14:12:20
    TCP通信的单位是一个报文,TCP报文包括报文头部和数据部分,头部最少占用20个字节。运用TCP协议进行通信前需要建立连接(三次握手),数据传输完成后,最后需要断开连接(4次握手)。  TCP发送缓存和接收缓存,TCP...
  • C/C++多线程编程设计源代码。服务器端和客户端
  • 发送数据 CString bodyDesc; m_Content.GetWindowText(bodyDesc); SendData2("怪兽乐园", 23, bodyDesc);void CClientDlg::SendData2(LPCSTR bodyName, short bodyAge, CString bodyDesc) { static DWORD SEQ = ...
  • TCP block方式时,接收端接收数据太慢,导致发送端阻塞, 这时候下面的tcp还活着吗?  应该是活着,因为接收端收数据包,发ack 但是,如果接收端不在接收数据, 发送端导致堵塞? 这时候tcp是活着的吗? 必须靠...
  • TCPIP控制UR机械臂运动(发送string控制指令并改写的程序)
  • 在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。 二、一些基本概念 TCP/IP ...
1 2 3 4 5 ... 20
收藏数 1,334
精华内容 533
关键字:

c++通过tcpip收发数据