本文链接:https://blog.csdn.net/upupday19/article/details/78916142
传统的TCP/IP通信过程依赖于socket,位于应用层和传输层之间,使得应用程序可以进行通信。相当于港口城市的码头,使得城市之间可以进行货物流通。服务器和客户端各有不同的通信流程。
一、服务器
1、建立连接阶段
- 调用socket(),分配文件描述符,即监听套接字
- 调用bind(),将套接字与本地IP地址和端口绑定
- 调用listen(),监听特定端口,socket()创建的套接字是主动的,调用listen使得该文件描述符为监听套接字,变主动为被动
- 调用accept(),阻塞等待客户端连接
2、数据交互阶段
- 调用read(),阻塞等待客户端发送的请求,收到请求后从read()返回,处理客户端请求
- 调用write(),将处理结果发送给客户端,然后继续调用read()等待客户端请求
3、关闭连接
- 当read()返回0的时候,说明客户端发来了FIN数据包,即关闭连接,也会调用close()关闭连接套接字和监听套接字
二、客户端
1、建立连接阶段
- 调用socket(),分配文件描述符
- 调用connect(),向服务器发送建立连接请求
2、数据交互阶段
- 调用write(),将请求发送给服务器
- 调用read(),阻塞等待服务器应答
3、关闭连接
- 当没有数据发送的时候,调用close()关闭连接套接字,即关闭连接,向服务器发送FIN数据报
三、TCP通信过程
四、C语言代码
服务器端
-
/* File name: server.c*/
-
//服务器端
-
#include<stdio.h>
-
#include<stdlib.h>
-
#include<string.h>
-
#include<errno.h>
-
#include<unistd.h>
-
#include<sys/types.h>
-
#include<sys/socket.h>
-
#include<netinet/in.h>
-
-
#define MAXLINE 4096
-
#define PORT 8000
-
-
int main(
void){
-
//定义服务器监听套接字和连接套接字
-
int listen_fd =
-1, connect_fd =
-1;
//初始化为-1
-
struct sockaddr_in servaddr;
//定义服务器对应的套接字地址
-
//服务器接收和发送缓冲区
-
char sendbuf[MAXLINE], recbuf[MAXLINE];
-
-
//初始化套接字地址结构体
-
memset(&servaddr,
0,
sizeof(servaddr));
-
servaddr.sin_family = AF_INET;
//IPv4
-
servaddr.sin_port = htons(PORT);
//设置监听端口
-
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//表示接收任意IP的连接请求
-
-
//创建套接字
-
if((listen_fd = socket(AF_INET, SOCK_STREAM,
0)) ==
-1){
-
//如果创建套接字失败,返回错误信息
-
//strerror(int errnum)获取错误的描述字符串
-
printf(
"create socket error: %s(error: %d)\n", strerror(errno), errno);
-
exit(
0);
-
}
-
-
//绑定套接字和本地IP地址和端口
-
if(bind(listen_fd, (
struct sockaddr*)&servaddr,
sizeof(servaddr)) ==
-1){
-
//绑定出现错误
-
printf(
"bind socket error: %s(error: %d)\n", strerror(errno), errno);
-
exit(
0);
-
}
-
-
//使得listen_fd变成监听描述符
-
if(listen(listen_fd,
10) ==
-1){
-
printf(
"listen socket error: %s(error: %d)\n", strerror(errno), errno);
-
exit(
0);
-
}
-
-
//accept阻塞等待客户端请求
-
printf(
"等待客户端发起连接\n");
-
-
while(
1){
-
if((connect_fd = accept(listen_fd, (
struct sockaddr*)
NULL,
NULL)) ==
-1){
-
printf(
"accept socket error: %s(error: %d)\n", strerror(errno), errno);
-
continue;
-
}
-
-
//可以一直保持连接
-
while(
1){
-
//读取客户端发来的信息
-
ssize_t len = read(connect_fd, recbuf,
sizeof(recbuf));
-
if(len <
0){
-
if(errno == EINTR){
-
continue;
-
}
-
exit(
0);
-
}
-
-
printf(
"接收客户端的请求:%s\n", recbuf);
-
-
//向客户端发送信息
-
printf(
"回复客户端信息:");
-
fgets(sendbuf,
sizeof(sendbuf), stdin);
-
write(connect_fd, sendbuf,
sizeof(sendbuf));
-
}
-
-
//关闭连接套接字
-
close(connect_fd);
-
}
-
-
//关闭监听套接字
-
close(listen_fd);
-
}
客户端
-
/* File name: client.c */
-
//客户端
-
#include<stdio.h>
-
#include<stdlib.h>
-
#include<string.h>
-
#include<errno.h>
-
#include<unistd.h>
-
#include<sys/types.h>
-
#include<sys/socket.h>
-
#include<arpa/inet.h>
-
-
#define MAXLINE 4096
-
#define PORT 8000
-
-
int main(
void){
-
//定义客户端套接字
-
int sockfd =
-1;
-
//定义想连接的服务器的套接字地址
-
struct sockaddr_in servaddr;
-
-
//发送和接收数据的缓冲区
-
char sendbuf[MAXLINE], recbuf[MAXLINE];
-
-
//初始化服务器套接字地址
-
memset(&servaddr,
0,
sizeof(servaddr));
-
servaddr.sin_family = AF_INET;
//IPv4
-
servaddr.sin_port = htons(PORT);
//想连接的服务器的端口
-
servaddr.sin_addr.s_addr = inet_addr(
"127.0.0.1");
//服务器的IP地址
-
-
//创建套接字
-
if((sockfd = socket(AF_INET, SOCK_STREAM,
0)) ==
-1){
-
printf(
"create socket error: %s(error: %d)\n", strerror(errno), errno);
-
exit(
0);
-
}
-
-
//向服务器发送连接请求
-
if(connect(sockfd, (
struct sockaddr*)&servaddr,
sizeof(servaddr)) ==
-1){
-
//连接失败
-
printf(
"connect socket error: %s(error: %d)\n", strerror(errno), errno);
-
exit(
0);
-
}
-
-
while(
1){
-
//向服务器发送信息
-
printf(
"向服务器发送信息:");
-
fgets(sendbuf,
sizeof(sendbuf), stdin);
-
write(sockfd, sendbuf,
sizeof(sendbuf));
-
-
//从服务器接收信息
-
ssize_t len = read(sockfd, recbuf,
sizeof(recbuf));
-
if(len <
0){
-
if(errno == EINTR){
-
continue;
-
}
-
exit(
0);
-
}
-
-
printf(
"服务器回应:%s\n", recbuf);
-
}
-
-
//关闭套接字
-
close(sockfd);
-
-
}