Linux C++:网络编程

    xiaoxiao2022-07-12  186

       以前学C语言时学过,现在学C++时可以复习,重新巩固下基础知识。

    什么是套接字:

        TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)或插口。套接字用(IP地址:端口号)表示。

    套接字分类:

    流式套接字(SOCK_STREAM):流式套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复发送,并按顺序接收。流式套接字之所以能够实现可靠的数据服务,原因在于其使用了传输控制协议,即TCP(The Transmission Control Protocol)协议。数据报套接字(SOCK_DGRAM):数据报套接字提供了一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP(User Datagram Protocol)协议进行数据的传输。由于数据报套接字不能保证数据传输的可靠性,对于有可能出现的数据丢失情况,需要在程序中做相应的处理。原始套接字(SOCK_RAW):原始套接字(SOCKET_RAW)允许对较低层次的协议直接访问,比如IP、 ICMP协议,它常用于检验新的协议实现,或者访问现有服务中配置的新设备,因为RAW SOCKET可以自如地控制Windows下的多种协议,能够对网络底层的传输机制进行控制,所以可以应用原始套接字来操纵网络层和传输层应用。比如,我们可以通过RAW SOCKET来接收发向本机的ICMP、IGMP协议包,或者接收TCP/IP栈不能够处理的IP包,也可以用来发送一些自定包头或自定协议的IP包。网络监听技术很大程度上依赖于SOCKET_RAW。

    网络通信一般使用CS模式连接。

    网络模型架构:

    TCP/UDP属于传输层。

    TCP网络通信:

        TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,用户数据报协议(UDP)是同一层内 [1]  另一个重要的传输协议。在因特网协议族(Internet protocol suite)中,TCP层是位于IP层之上,应用层之下的中间层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换。

    通信流程图如下

    TCP三次握手建立连接:

    第一次握手:客户端发送syn包(seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

    TCP四次握手断开连接:

    第一次握手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不会再给你发数据了(当 然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但此时主动关闭方还可以接受数据。第二次握手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。第三次握手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。第四次握手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。

    代码示例服务端(使用了select  模式,目前只可以进行一对一连接,如果想一对多的话,添加多线程即可)

    #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 <iostream> #define SERVER_PORT 6555 int main() { fd_set rfds; struct timeval tv; int retval, maxfd; //选择器 /*创建socket*/ int ss = socket(AF_INET, SOCK_STREAM, 0); //AF_INET IPV4 ;SOCK_STREAM TCP struct sockaddr_in server_sockaddr; server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(SERVER_PORT); server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); /*bind*/ if(bind(ss, (struct sockaddr* ) &server_sockaddr, sizeof(server_sockaddr))==-1) { perror("bind"); exit(1); } /*listen*/ if(listen(ss, 5) == -1) { perror("listen"); exit(1); } /*connect*/ struct sockaddr_in client_addr; socklen_t length = sizeof(client_addr); ///成功返回非负描述字,出错返回-1 int conn = accept(ss, (struct sockaddr*)&client_addr, &length); //目测需要客户端部分的addr if( conn < 0 ) { perror("connect"); exit(1); } while(1) { /*把可读文件描述符的集合清空*/ FD_ZERO(&rfds); /*把标准输入的文件描述符加入到集合中*/ FD_SET(0, &rfds); maxfd = 0; /*把当前连接的文件描述符加入到集合中*/ FD_SET(conn, &rfds); /*找出文件描述符集合中最大的文件描述符*/ if(maxfd < conn) maxfd = conn; /*设置超时时间*/ tv.tv_sec = 5; tv.tv_usec = 0; /*等待聊天*/ retval = select(maxfd+1, &rfds, NULL, NULL, &tv); if(retval == -1){ printf("select出错,客户端程序退出\n"); break; }else if(retval == 0){ printf("服务端没有任何输入信息,并且客户端也没有信息到来,waiting...\n"); continue; }else{ /*客户端发来了消息*/ if(FD_ISSET(conn,&rfds)){ char buffer[1024]; memset(buffer, 0 ,sizeof(buffer)); int len = recv(conn, buffer, sizeof(buffer), 0); if(strcmp(buffer, "exit\n") == 0) break; printf("%s", buffer); //send(conn, buffer, len , 0);把数据回发给客户端 } /*用户输入信息了,开始处理信息并发送*/ if(FD_ISSET(0, &rfds)){ char buf[1024]; fgets(buf, sizeof(buf), stdin); //printf("you are send %s", buf); send(conn, buf, sizeof(buf), 0); } } } close(conn); close(ss); return 0; }

    客户端:

    #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 6555 #define BUFFER_SIZE 1024 int main() { int sock_cli; fd_set rfds; struct timeval tv; int retval, maxfd; ///定义sockfd 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("127.0.0.1"); ///服务器ip //连接服务器,成功返回0,错误返回-1 while (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { perror("connect"); exit(1); } while(1){ /*把可读文件描述符的集合清空*/ FD_ZERO(&rfds); /*把标准输入的文件描述符加入到集合中*/ FD_SET(0, &rfds); maxfd = 0; /*把当前连接的文件描述符加入到集合中*/ FD_SET(sock_cli, &rfds); /*找出文件描述符集合中最大的文件描述符*/ if(maxfd < sock_cli) maxfd = sock_cli; /*设置超时时间*/ tv.tv_sec = 5; tv.tv_usec = 0; /*等待聊天*/ retval = select(maxfd+1, &rfds, NULL, NULL, &tv); if(retval == -1){ printf("select出错,客户端程序退出\n"); break; }else if(retval == 0){ printf("客户端没有任何输入信息,并且服务器也没有信息到来,waiting...\n"); continue; }else{ /*服务器发来了消息*/ if(FD_ISSET(sock_cli,&rfds)){ char recvbuf[BUFFER_SIZE]; int len; len = recv(sock_cli, recvbuf, sizeof(recvbuf),0); printf("%s", recvbuf); memset(recvbuf, 0, sizeof(recvbuf)); } /*用户输入信息了,开始处理信息并发送*/ if(FD_ISSET(0, &rfds)){ char sendbuf[BUFFER_SIZE]; fgets(sendbuf, sizeof(sendbuf), stdin); send(sock_cli, sendbuf, strlen(sendbuf),0); //发送 memset(sendbuf, 0, sizeof(sendbuf)); } } } close(sock_cli); return 0; }

    UDP网络通信:

        UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。UDP在IP报文的协议号是17。使用UDP可进行 单播 、广播 、组播。

    UDP网络通信流程图:

    示例代码服务端

    #include <stdio.h> #include <iostream> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <stdlib.h> #define SERV_PORT 8000 int main() { /* sock_fd --- socket文件描述符 创建udp套接字*/ int sock_fd = socket(AF_INET, SOCK_DGRAM, 0); if(sock_fd < 0) { perror("socket"); exit(1); } /* 将套接字和IP、端口绑定 */ struct sockaddr_in addr_serv; int len; memset(&addr_serv, 0, sizeof(struct sockaddr_in)); //每个字节都用0填充 addr_serv.sin_family = AF_INET;//使用IPV4地址 addr_serv.sin_port = htons(SERV_PORT);//端口 /* INADDR_ANY表示不管是哪个网卡接收到数据,只要目的端口是SERV_PORT,就会被该应用程序接收到 */ addr_serv.sin_addr.s_addr = htonl(INADDR_ANY); //自动获取IP地址 len = sizeof(addr_serv); /* 绑定socket */ if(bind(sock_fd, (struct sockaddr *)&addr_serv, sizeof(addr_serv)) < 0) { perror("bind error:"); exit(1); } int recv_num; int send_num; char send_buf[20] = "i am server!"; char recv_buf[20]; struct sockaddr_in addr_client; while(1) { printf("server wait:\n"); recv_num = recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_client, (socklen_t *)&len); if(recv_num < 0) { perror("recvfrom error:"); exit(1); } recv_buf[recv_num] = '\0'; printf("server receive %d bytes: %s\n", recv_num, recv_buf); send_num = sendto(sock_fd, send_buf, recv_num, 0, (struct sockaddr *)&addr_client, len); if(send_num < 0) { perror("sendto error:"); exit(1); } } close(sock_fd); return 0; }

    客户端

    #include <stdio.h> #include <string.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <iostream> #define DEST_PORT 8000 #define DSET_IP_ADDRESS "127.0.0.1" int main() { /* socket文件描述符 */ int sock_fd; /* 建立udp socket */ sock_fd = socket(AF_INET, SOCK_DGRAM, 0); if(sock_fd < 0) { perror("socket"); exit(1); } /* 设置address */ struct sockaddr_in addr_serv; int len; memset(&addr_serv, 0, sizeof(addr_serv)); addr_serv.sin_family = AF_INET; addr_serv.sin_addr.s_addr = inet_addr(DSET_IP_ADDRESS); addr_serv.sin_port = htons(DEST_PORT); len = sizeof(addr_serv); int send_num; int recv_num; char send_buf[20] = "hey, who are you?"; char recv_buf[20]; printf("client send: %s\n", send_buf); send_num = sendto(sock_fd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&addr_serv, len); if(send_num < 0) { perror("sendto error:"); exit(1); } recv_num = recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_serv, (socklen_t *)&len); if(recv_num < 0) { perror("recvfrom error:"); exit(1); } recv_buf[recv_num] = '\0'; printf("client receive %d bytes: %s\n", recv_num, recv_buf); close(sock_fd); return 0; }

    关于UDP广播组播相关示例待续...

     

    参考:

    http://www.cnblogs.com/zkfopen/p/9382705.html

    https://www.cnblogs.com/wuyepeng/p/9737583.html

    最新回复(0)