TCP和UDP的代码实现
引言
本片文章是基于一定网络编程基础来实现的TCP和UDP的基本架构,实现了服务端和客户端的代码
TCP
对于TCP来说,它的优势就是在于对于传递信息十分的准确,因为它存在了三次握手和四次挥手的机制,但是缺点也就是每一次信息的传递要消耗大量的资源,来保证每一次传递信息的准确性。
我们可以简单的模拟一下TCP通信的过程,方便我们代码的构架:
首先作为服务器,我们应该先创建一个套接字(socket()),接受来自客户端那一边的连接请求。但是套接字这个仅仅是一个载体,这个载体就是属于谁,性质是什么,都没有确定,所以服务器必须要绑定(bind())这一个载体,然后当一切准备就绪了之后,服务器就要开始监听(listen()),因为我们创建载体的目的就是为了监听客户端。这个监听是存在一个队列的,也就是如果有多个客户端请求服务器,那么这些客户端就会放在这个等待队列里面,等待这服务器的处理。我们一般设置这个队列的长度是128。
然后当我们服务器从队列里面取出一个任务的时候,accept()就会创建出一个新的套接字,这个套接字和最开始的不是一个东西,这个是专门用来和取出来的客户端进行沟通的,然后沟通的方式就是recv和send,作为数据的收发~~~
然后作为客户端,我们不需要监听,而是connect,因为客户端是连接服务器的。
这个是服务器端,首先就是sin结构体的初始化,我们选择的是ipv4,TCP通信,地址是本机的地址
#include <iostream> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #define SER_PORT 8888 #define SER_IP "192.168.189.134" int main() { int sfd = socket(AF_INET, SOCK_STREAM, 0); if (sfd == -1) { perror("socket error"); return -1; } sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(SER_PORT); sin.sin_addr.s_addr = inet_addr(SER_IP); if(bind(sfd, (struct sockaddr *)& sin, sizeof(sin)) == -1) { perror("bind error"); return -1; } if(listen(sfd, 128) == -1) { perror("listen error"); return -1; } sockaddr_in cin; socklen_t sockLen = sizeof(cin); int newfd = accept(sfd, (struct sockaddr *)& cin, &sockLen); if (newfd == -1) { perror("accept error"); return -1; } char rbuf[1024]; while(1) { bzero(rbuf, sizeof(rbuf)); int res = recv(newfd, rbuf, sizeof(rbuf), 0); // 阻塞接受数据 if(res == -1) { perror("recv error"); return -1; } if(res == 0) { std::cout << "client close" << std::endl; break; } strcat(rbuf,"*_*"); if(send(newfd, rbuf, strlen(rbuf), 0) == -1) { perror("send error"); return -1; } } close(newfd); close(sfd); return 0; }然后对于客户端,我们其实不需要填写cin的任何信息,因为我们只需要连接客户端,然后发送信息,或者接受信息,服务器不需要知道我们客户端的信息,所以bind也是不需要的,不过写了也没错,主要是通过socket这个套接字,来和服务器联系。
#include <iostream> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #define SER_PORT 8888 #define SER_IP "192.168.189.133" #define CLI_PORT 9999 #define CLI_IP "192.168.189.133" int main() { int cfd = socket(AF_INET, SOCK_STREAM, 0); if (cfd == -1) { perror("socket error"); return -1; } sockaddr_in cin; cin.sin_family = AF_INET; cin.sin_port = htons(CLI_PORT); // 客户端的地址可以选择不写,因为客户端是主动连接别人的 if (bind(cfd, (struct sockaddr *)& cin, sizeof(cin)) == -1) { perror("bind error"); return -1; } sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(SER_PORT); sin.sin_addr.s_addr = inet_addr(SER_IP); if(connect(cfd, (struct sockaddr *)& sin, sizeof(sin)) == -1) { perror("connect error"); return -1; } char wbuf[128] = ""; while(1) { bzero(wbuf, sizeof(wbuf)); fgets(wbuf, sizeof(wbuf), stdin); wbuf[strlen(wbuf) - 1] = 0; if (send(cfd, wbuf, strlen(wbuf), 0) == -1) { std::cout << "send error" << std::endl; break; } if (recv(cfd, wbuf, sizeof(wbuf), 0) == -1) { std::cout << "recv error" << std::endl; break; } std::cout << wbuf << std::endl; } close(cfd); return 0; }UDP
UDP的特点就是保证信息传递要快,不是很看重信息的准确性,所以UDP是面向无连接的,也就是说不管你在不在线,只要我知道你的地址,不需要连接,就可以直接把信息发送过去。
所以缺点也就是信息可能会出现错误,这种通信一般就是在广播方面的应用
服务器端这一部分TCP有区别,首先就是必须要传递cin,也就是客户端的信息,但是这个cin不需要初始化,当客户端那边连接了服务器端之后,cin就会接受客户端的数据,然后进行通信。这个过程中如果客户端下线了,服务器端的recvfrom会阻塞等待,sendto仍然会发出信息,因为UDP是面向无连接的,不需要知道客户端是否在线。
#include <myhead.h> #define SER_PORT 8888 #define SER_IP "192.168.189.133" int main(int argc, char const *argv[]) { //1、创建用于通信的套接字文件描述符 int sfd = socket(AF_INET,SOCK_DGRAM,0); if(sfd == -1){ perror("socket error"); return -1; } printf("socket success sfd = %d\n",sfd); //2、绑定ip地址和端口号 struct sockaddr_in sin; sin.sin_addr.s_addr = inet_addr(SER_IP); sin.sin_family = AF_INET; sin.sin_port = htons(SER_PORT); if(bind(sfd,(struct sockaddr*)& sin,sizeof(sin)) == -1){ perror("bind error"); return -1; } printf("bind success\n"); //3、数据收发 char rbuf[128] = ""; //定义容器接受对端的地址信息结构体 struct sockaddr_in cin; socklen_t socklen = sizeof(cin); while(1){ bzero(rbuf,sizeof(rbuf)); //从客户端中读取消息 if(recvfrom(sfd,rbuf,sizeof(rbuf),0,(struct sockaddr*)&cin, &socklen) == -1){ perror("recvfrom error\n"); return -1; } printf("[%s : %d] : %s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),rbuf); //加个笑脸发给对方 strcat(rbuf,"*_*"); //将数据发送给客户端 sendto(sfd,rbuf,strlen(rbuf),0,(struct sockaddr*)&cin,sizeof(cin)); printf("发送成功\n"); } //4、关闭套接字 close(sfd); return 0; }这是客户端的代码,因为没有connect()函数了,所以这里我们发送信息的时候需要传递sin的结构体,保证客户端知道这个数据要传递给谁。但是至于接受数据,其实nullptr就可以,因为接收数据并不需要知道服务器的信息。
#include <iostream> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #define CLI_PORT 9999 #define SER_PORT 8888 #define SER_IP "192.168.189.134" #define CLI_IP "192.168.189.134" int main() { int cfd = socket(AF_INET, SOCK_DGRAM, 0); if (cfd == -1) { perror("socket error"); return -1; } sockaddr_in cin; cin.sin_family = AF_INET; cin.sin_port = htons(CLI_PORT); cin.sin_addr.s_addr = inet_addr(CLI_IP); if (bind(cfd, (struct sockaddr *)& cin, sizeof(cin)) == -1) { perror("bind error"); return -1; } char wbuf[128] = ""; sockaddr_in sin; sin.sin_addr.s_addr = inet_addr(SER_IP); sin.sin_port = htons(SER_PORT); sin.sin_family = AF_INET; while(1) { bzero(wbuf, sizeof(wbuf)); fgets(wbuf, sizeof(wbuf), stdin); wbuf[strlen(wbuf) - 1] = 0; sendto(cfd, wbuf, strlen(wbuf), 0, (struct sockaddr *)& sin, sizeof(sin)); recvfrom(cfd, wbuf, sizeof(wbuf), 0, nullptr, nullptr); std::cout << wbuf << std::endl; } close(cfd); return 0; }总结
本片文章到这里就结束了,希望可以帮助大家对两种通信方式有更好的理解~~~
