TCP 和 UDP -> 传输层的协议:
UDP:用户数据报协议,面向无连接,可以单播,多播,广播, 面向数据报,不可靠
TCP:传输控制协议,面向连接的,可靠的,基于字节流,仅支持单播传输
UDP TCP
是否创建连接 无连接 面向连接
是否可靠 不可靠 可靠的
连接的对象个数 一对一、一对多、多对一、多对多 支持一对一
传输的方式 面向数据报 面向字节流
首部开销 8个字节 最少20个字节
适用场景 实时应用(视频会议,直播) 可靠性高的应用(文件传输)
(1)创建一个用于监听的套接字
- 监听:监听有客户端的连接
- 套接字:这个套接字其实就是一个文件描述符
(2)将这个监听文件描述符和本地的IP和端口绑定(IP和端口就是服务器的地址信息)
- 客户端连接服务器的时候使用的就是这个IP和端口
(3)设置监听,监听的fd开始工作
(4) 阻塞等待,当有客户端发起连接,解除阻塞,接受客户端的连接,会得到一个和客户端通信的套接字(fd)
(5)通信
- 接收数据
- 发送数据
(6)通信结束,断开连接
(1)创建一个用于通信的套接字(fd)
(2)连接服务器,需要指定连接的服务器的 IP 和 端口
(3)连接成功了,客户端可以直接和服务器通信
- 接收数据
- 发送数据
(4) 通信结束,断开连接

- #include
- #include
- #include
// 包含了这个头文件,上面两个就可以省略 - 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); // socket命 名
- - 功能:绑定,将fd 和本地的IP + 端口进行绑定
- - 参数:
- - sockfd : 通过socket函数得到的文件描述符
- - addr : 需要绑定的socket地址,这个地址封装了ip和端口号的信息 - addrlen : 第二个参数结构体
- 占的内存大小
-
- int listen(int sockfd, int backlog); // /proc/sys/net/core/somaxconn
- - 功能:监听这个socket上的连接
- - 参数:
- - sockfd : 通过socket()函数得到的文件描述符
- - backlog : 未连接的和已经连接的和的最大值, 5
-
- int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- - 功能:接收客户端连接,默认是一个阻塞的函数,阻塞等待客户端连接
- - 参数:
- - sockfd : 用于监听的文件描述符
- - addr : 传出参数,记录了连接成功后客户端的地址信息(ip,port)
- - addrlen : 指定第二个参数的对应的内存大小 - 返回值:- 成功 :用于通信的文件描述符 - -1 : 失败
-
- int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- - 功能: 客户端连接服务器
- - 参数:
- - sockfd : 用于通信的文件描述符
- - addr : 客户端要连接的服务器的地址信息
- - addrlen : 第二个参数的内存大小
- - 返回值:
- 成功 0,
- 失败 -1
-
- ssize_t write(int fd, const void *buf, size_t count); // 写数据
- ssize_t read(int fd, void *buf, size_t count); // 读数据
- // TCP 通信的服务器端
-
- #include
- #include
- #include
- #include
- #include
-
- int main() {
-
- // 1.创建socket(用于监听的套接字)
- int lfd = socket(AF_INET, SOCK_STREAM, 0);
-
- if(lfd == -1) {
- perror("socket");
- exit(-1);
- }
-
- // 2.绑定(IP地址和端口)
- struct sockaddr_in saddr;
- saddr.sin_family = AF_INET;
- // inet_pton(AF_INET, "192.168.193.128", saddr.sin_addr.s_addr); //指定具体网卡
- saddr.sin_addr.s_addr = INADDR_ANY; // 0.0.0.0 同时绑定多个网卡,使用哪个都可以
- saddr.sin_port = htons(9999);
- int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
-
- if(ret == -1) {
- perror("bind");
- exit(-1);
- }
-
- // 3.监听
- ret = listen(lfd, 8);
- if(ret == -1) {
- perror("listen");
- exit(-1);
- }
-
- // 4.接收客户端连接
- struct sockaddr_in clientaddr;
- int len = sizeof(clientaddr);
- int cfd = accept(lfd, (struct sockaddr *)&clientaddr, &len);
-
- if(cfd == -1) {
- perror("accept");
- exit(-1);
- }
-
- // 输出客户端的信息
- char clientIP[16];
- inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, clientIP, sizeof(clientIP));
- unsigned short clientPort = ntohs(clientaddr.sin_port);
- printf("client ip is %s, port is %d\n", clientIP, clientPort);
-
- // 5.通信
- char recvBuf[1024] = {0};
- while(1) {
-
- // 获取客户端的数据
- int num = read(cfd, recvBuf, sizeof(recvBuf));
- if(num == -1) {
- perror("read");
- exit(-1);
- } else if(num > 0) {
- printf("recv client data : %s\n", recvBuf);
- } else if(num == 0) {
- // 表示客户端断开连接
- printf("clinet closed...");
- break;
- }
-
- char * data = "hello,i am server";
- // 给客户端发送数据
- write(cfd, data, strlen(data));
- }
-
- // 关闭文件描述符
- close(cfd);
- close(lfd);
-
- return 0;
- }
- // TCP通信的客户端
-
- #include
- #include
- #include
- #include
- #include
-
- int main() {
-
- // 1.创建套接字
- int fd = socket(AF_INET, SOCK_STREAM, 0);
- if(fd == -1) {
- perror("socket");
- exit(-1);
- }
-
- // 2.连接服务器端
- struct sockaddr_in serveraddr;
- serveraddr.sin_family = AF_INET;
- inet_pton(AF_INET, "192.168.193.128", &serveraddr.sin_addr.s_addr);
- serveraddr.sin_port = htons(9999);
- int ret = connect(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
-
- if(ret == -1) {
- perror("connect");
- exit(-1);
- }
-
-
- // 3. 通信
- char recvBuf[1024] = {0};
- while(1) {
-
- char * data = "hello,i am client";
- // 给客户端发送数据
- write(fd, data , strlen(data));
-
- sleep(1);
-
- int len = read(fd, recvBuf, sizeof(recvBuf));
- if(len == -1) {
- perror("read");
- exit(-1);
- } else if(len > 0) {
- printf("recv server data : %s\n", recvBuf);
- } else if(len == 0) {
- // 表示服务器端断开连接
- printf("server closed...");
- break;
- }
-
- }
-
- // 关闭连接
- close(fd);
-
- return 0;
- }
说明:在两个函数编译好后,在会话窗口1,先执行服务器端函数,他会阻塞等待;复制会话在新会话窗口执行客户端,开始通信;