引言
本片文章是基于一定网络编程基础来实现的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;
}
总结
本片文章到这里就结束了,希望可以帮助大家对两种通信方式有更好的理解~~~
