一.前言
TCP和UDP的区别
UDP : 用户数据报协议,面向无连接,可以单播,多播,广播,面向数据报,不可靠,适用于音视频传输
TCP : 传输控制协议,面向连接的协议,可靠的,基于字节流,仅支持单播传输,点对点传输
UDP TCP
是否创建连接 无 有
是否可靠 不可靠 可靠
连接的对象个数 一对一 一对多 多对多 一对一
传输的方式 面向数据报 面向字节流
首部开销 8个字节 最少20个字节
适用场景 实时性要求比较高的场景(qq 电话会议 直播) 可靠性高的应用(文件传输)
TCP客户端和服务端的通信流程
TCP客户端:
1.socket : 创建一个用于通信的套接字fd
2.connect : 连接服务器,需要指定连接的服务器的ip和端口
3.send : 发送数据
4.recv : 接收数据
5.close : 断开连接TCP服务端
1.socket
2.bind:将监听文件描述符和本地的ip和端口绑定(ip和端口就是服务器的地址信息)--客户端连接服务器的时候适用的就是这个ip和端口
3.listen : 监听fd的开始工作
4.accept : 阻塞等待,当有客户端发起连接,接触阻塞,接收客户端的连接,得到一个和客户端通信的套接字fd
4.send : 发送数据
5.recv : 接收数据
6.close : 通信结束,断开连接
相关的socketAPI函数介绍
socket函数
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> //包含了这个头文件,上面两个就可以省略
int socket(int domain, int type. int protocol)
-功能:创建一个套接字
-参数:
-domain:协议族
AF_INET IPV4
AF_INET6 IPV6
AF_UNIX,AF_LOCAL : 本地套接字通信(进程间通信)
-type:通信过程中适用的协议类型
SOCK_STREAM : 流式协议
SOCK_DGRAM : 报式协议
-protocol:具体的一个协议,一般为: 0
-SOCK_STREAM : 流式协议默认使用TCP
-SOCK_DGRAM : 报式协议默认适用UDP
-返回值:
成功: 返回文件描述符
失败: -1
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
-功能:绑定,将fd和本地的ip端口绑定
-参数:
-sockfd
-addr: 需要绑定的socket地址,这个地址封装了ip和端口号
-addrlen: 结构体占有的内存大小
-返回值:
成功: 0
失败: -1
int listen(int sockfd, int backlog); 会有两个队列:已经连接队列,未连接队列
-功能:监听这个socket上的连接
-sockfd : 通过socket()函数得到的文件描述符
-backlog: 未连接的和已经连接的和的最大值(给个5即可)(4096) // 通过 cat /proc/sys/net/core/somaxconn 查看
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
-功能:接受客户端连接,默认是一个阻塞的函数,阻塞等待客户端连接进来
-参数:
-sockfd : 通过socket()函数得到的文件描述符
-addr : 传出参数,记录了连接成功后客户端的地址信息(ip port)
-addrlen: 指定第二个参数的对应的内存大小
-返回值:
-成功:返回用于通信的文件描述符
-失败:-1
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
-功能:客户端口连接服务器
-参数:
-sockfd : 用于通信的文件描述符,socket()创建
-addr : 客户端要连接的服务器的地址信息
-addrlen : 第二个参数的内存大小
-返回值:
成功: 0
失败: -1
二.TCP客户端和服务端通信实例代码
服务端
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define PORT 8000
#define IP "127.0.0.1"
/*
TCP 通信的服务器端
*/
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0); //创建用于监听的套接子
if(sockfd == -1)
{
perror("create socket");
exit(-1);
}
//绑定
struct sockaddr_in server;
server.sin_family = AF_INET;
// inet_pton(AF_INET,"127.0.0.1", server.sin_addr.s_addr);
server.sin_addr.s_addr = INADDR_ANY; //0.0.0.0
server.sin_port = htons(PORT);
if(bind(sockfd,(const struct sockaddr *)&server,sizeof(server)) == -1)
{
perror("bind");
exit(-1);
}
//3.监听
if(listen(sockfd,8) == -1)
{
perror("listen");
exit(-1);
}
//接收客户端连接
struct sockaddr_in clientaddr;
int len = sizeof(clientaddr);
int cfd = accept(sockfd,(struct sockaddr*)&clientaddr,&len);
if(cfd == -1)
{
perror("accept");
exit(-1);
}
//输出客户端信息,获取过来的是网络字节序
char cip[16] = {0};
inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,cip,sizeof(cip));
unsigned short cport = ntohs(clientaddr.sin_port);
printf("client ip is %s, client port is %d\n",cip,cport);
//获取客户端信息
char recvbuf[1024] = {0};
char *send_data = "hello, i am server";
while(1)
{
memset(recvbuf,0,sizeof(recvbuf));
int fd = read(cfd,recvbuf,sizeof(recvbuf));
if(fd == -1)
{
perror("read");
}
else if(fd > 0)
{
printf("recv client data : %s\n",recvbuf);
}
else if(fd == 0)
{
printf("客户端断开连接\n");
break;
}
//给客户端发送数据
fd = write(cfd,send_data,strlen(send_data));
if(fd == -1)
{
perror("write");
}
}
//关闭文件
close(cfd);
close(sockfd);
return 0;
}
客户端
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1)
{
perror("create socket");
exit(-1);
}
//2.连接服务器
struct sockaddr_in client;
client.sin_family = AF_INET;
client.sin_addr.s_addr = inet_addr("127.0.0.1");
client.sin_port = htons(8000);
int ret = connect(sockfd,(const struct sockaddr*)&client,sizeof(client));
if(ret == -1)
{
perror("connect");
exit(-1);
}
//进行通信
char *send_data = "hello, i am client";
char recvbuf[1024] = {0};
while(1)
{
int len = write(sockfd,send_data,strlen(send_data));
if(len == -1)
{
perror("write");
}
sleep(1);
memset(recvbuf,0,sizeof(recvbuf));
int fd = read(sockfd,recvbuf,sizeof(recvbuf));
if(fd == -1)
{
perror("read");
}
else if(fd > 0)
{
printf("recv server data : %s\n",recvbuf);
}
else if(fd == 0)
{
printf("服务器端断开连接\n");
break;
}
}
//关闭连接
close(sockfd);
return 0;
}
三.使用TCP实现数据回射
服务端
// #include <stdio.h>
// #include <arpa/inet.h>
// #include <string.h>
// #include <unistd.h>
// #include <stdlib.h>
// int main()
// {
// int sockfd = socket(AF_INET,SOCK_STREAM,0);
// if(sockfd == -1)
// {
// perror("create sockfd");
// exit(-1);
// }
// struct sockaddr_in server;
// server.sin_family = AF_INET;
// server.sin_addr.s_addr = INADDR_ANY;
// server.sin_port = htons(8000);
// int len = sizeof(server);
// int ret = bind(sockfd,(const struct sockaddr*)&server,len);
// if(ret == -1)
// {
// perror("bind");
// exit(-1);
// }
// ret = listen(sockfd,8);
// if(ret == -1)
// {
// perror("listen");
// exit(-1);
// }
// struct sockaddr_in client;
// len = sizeof(client);
// int clientfd = accept(sockfd,(struct sockaddr*)&client,&len);
// if(clientfd == -1)
// {
// perror("accept");
// exit(-1);
// }
// //建立连接成功
// char client_ip[16] = {0};
// inet_ntop(AF_INET,(const void*)&client.sin_addr.s_addr,client_ip,sizeof(client_ip));
// unsigned short port = ntohs(client.sin_port);
// printf("接收到一个连接,新的连接的地址:%s,端口:%d\n",client_ip,port);
// //读
// char recvbuf[1024];
// while(1)
// {
// memset(recvbuf,0,sizeof(recvbuf));
// len = read(clientfd,recvbuf,sizeof(recvbuf));
// if(len == -1)
// {
// perror("read");
// break;
// }
// else if(len > 0)
// {
// printf("接收到了客户端发来的数据:%s\n",recvbuf);
// printf("下面进行数据回射\n");
// len = write(clientfd,recvbuf,strlen(recvbuf));
// if(len == -1)
// {
// perror("write");
// break;
// }
// }
// else if(len == 0)
// {
// printf("客户端断开连接\n");
// break;
// }
// }
// close(clientfd);
// close(sockfd);
// return 0;
// }
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1)
{
perror("socket");
return -1;
}
//绑定
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_port = htons(8000);
int len = sizeof(server);
if(bind(sockfd,(const struct sockaddr*)&server,len) == -1)
{
perror("bind");
return -1;
}
//监听
if(listen(sockfd, 8) == -1)
{
perror("listen");
return -1;
}
//阻塞接收
struct sockaddr_in recv_message;
len = sizeof(recv_message);
int recv_fd = accept(sockfd,(struct sockaddr*)&recv_message,&len);
if(recv_fd == -1)
{
perror("accept");
return -1;
}
char client_ip[16] = {0};
inet_ntop(AF_INET,(const void *)&recv_message.sin_addr.s_addr,client_ip,sizeof(client_ip));
unsigned short client_port = ntohs(recv_message.sin_port);
printf("接收到新的客户端连接,他的ip:%s,端口是:%d\n",client_ip,client_port);
//接收
char recv_buff[1024];
while(1)
{
memset(recv_buff,0,sizeof(recv_buff));
len = read(recv_fd,recv_buff,sizeof(recv_buff));
if(len == -1)
{
perror("read");
break;
}
else if(len > 0)
{
printf("接收到客户端数据:%s\n",recv_buff);
len =write(recv_fd,recv_buff,strlen(recv_buff));
if(len == -1)
{
perror("write");
break;
}
}
else if(len == 0)
{
printf("客户端断开连接\n");
break;
}
}
close(sockfd);
close(recv_fd);
}
客户端
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1)
{
perror("socket");
exit(-1);
}
struct sockaddr_in client;
client.sin_family = AF_INET;
client.sin_addr.s_addr = inet_addr("127.0.0.1");
client.sin_port = htons(8000);
if(connect(sockfd,(const struct sockaddr*)&client,sizeof(client)) == -1)
{
perror("connect");
exit(-1);
}
//写
char *client_send_data;
char client_recv_buf[1024];
int len;
while(1)
{
printf("亲爱的用户,请发送你要发送给服务器的数据:");
scanf("%s",client_send_data);
len = write(sockfd,client_send_data,strlen(client_send_data));
if(len == -1)
{
perror("write");
break;
}
sleep(1);
memset(client_recv_buf,0,sizeof(client_recv_buf));
len = read(sockfd,client_recv_buf,sizeof(client_recv_buf));
if(len == -1)
{
perror("read");
break;
}
else if(len > 0)
{
printf("接收到服务器端的回射数据:%s\n",client_recv_buf);
}
else if(len == 0)
{
printf("服务器断开连接\n");
break;
}
}
close(sockfd);
return 0;
}