Tcp——客户端服务器
目录
一、根本概念
二、代码
2.1 ser服务器
2.2 cil客户端
一、根本概念
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节省的传输层通信协议。在TCP/IP模型中,TCP位于IP层之上,应用层之下,负责在网络中的两个主机之间建立连接,进行数据传输,并确保数据的完整性温和序性。
TCP客户端和服务器通过建立连接、进行数据互换和关闭连接的过程进行通信。TCP协议确保了数据的可靠传输,包括数据完整性、次序性、流量控制和拥塞控制。通过利用套接字编程,可以实现TCP客户端和服务器之间的通信。
这张图展示了一个典范的网络通信过程中,服务端和客户端的常用套接字(socket)操作步骤。套接字是网络编程中用于不同主机间通信的端点。
### 服务端
1. **socket()**:创建一个新的套接字。
2. **bind(ip+port)**:将套接字绑定到指定的IP地点和端口号上,以便监听来自该地点和端口的连接请求。
3. **listen()**:使套接字进入监听状态,期待客户端的连接请求。
4. **accept()**:担当客户端的连接请求,建立连接。
5. **recv()**:接收来自客户端的数据。
6. **send()**:向客户端发送数据。
7. **close()**:关闭套接字,竣事通信。
### 客户端
1. **socket()**:创建一个新的套接字。
2. **connect(ip+port)**:向服务端的指定IP地点和端口号发起连接请求。
3. **send()**:向服务端发送数据。
4. **recv()**:接收来自服务端的数据。
5. **close()**:关闭套接字,竣事通信。
在实际的网络通信中,服务端和客户端通过这些步骤进行数据的发送和接收,实现网络通信。服务端通常先启动,期待客户端的连接请求,而客户端则主动发起连接请求,连接成功后进行数据互换。
二、代码
2.1 ser服务器
TCP服务器程序,用于担当客户端的连接请求,并与客户端进行数据互换
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- int main()
- {
- int sockfd = socket(AF_INET,SOCK_STREAM,0);//创建套接子,失败 -1
- if( -1 == sockfd )
- {
- exit(1);
- }
- struct sockaddr_in saddr,caddr;//套接字的地址(也就是ip+port)
- memset(&saddr,0,sizeof(saddr));
- saddr.sin_family = AF_INET;//ipv4地址族
- saddr.sin_port = htons(6000);//网络字节序列(大端)
- saddr.sin_addr.s_addr = inet_addr("127.0.0.1");//点分十进制字符串表示的ip 转换为无符号整形表示的ip地址
- int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
- if( -1 == res)
- {
- printf("bind err\n");
- exit(1);
- }
- res = listen(sockfd,5);//创建监听队列,客户端连接服务器时,要存放客户端的连接信息。 5是监听队列的大小
- if( -1 == res )
- {
- exit(1);
- }
- while( 1 )
- {
- int len = sizeof(caddr);
- int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//接受客户端连接,caddr,用来存放客户端的ip+port
- if( c < 0 )
- {
- continue;
- }
- printf("accept c=%d\n",c);
- char buff[128] = {0};
- int n = recv(c,buff,127,0);//接受客户端发送的数据
- printf("n=%d,recv:%s\n",n,buff);
- send(c,"ok",2,0);//向客户端发送数据
- close(c);
- }
- }
复制代码 1. 包罗头文件
- `sys/socket.h` 和 `netinet/in.h` 是网络编程中常用的头文件,包罗了套接字相干的函数和布局体定义。
- `arpa/inet.h` 包罗了处理惩罚网络地点的函数,如 `inet_addr()` 和 `htons()`。
2. 创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd) {
exit(1);
}
- `socket()` 函数用于创建一个新的套接字。
- 参数 `AF_INET` 指定了地点族为IPv4,`SOCK_STREAM` 指定了套接字类型为面向连接的流套接字(TCP)。
- 如果创建失败,返回 `-1`,程序将退出。
3. 设置服务器地点
定义地点布局体
struct sockaddr_in saddr, caddr;
- struct sockaddr_in 是一个布局体,用于存储IPv4地点和端口号。
- saddr 用于存储服务器的地点信息 包罗了ip地点和端口号。
- caddr 用于存储客户端的地点信息。
初始化saddr布局体
memset(&saddr, 0, sizeof(saddr));
- memset() 函数用于将 saddr 布局体的所有字节初始化为0。
- 这样做可以确保布局体中的所有字段都被清零,避免利用未初始化的值。
设置地点族
saddr.sin_family = AF_INET;
- sin_family 字段用于指定地点族,AF_INET 表示IPv4地点族。
设置端口号
saddr.sin_port = htons(6000);
- sin_port 字段用于指定端口号。
- htons(6000) 函数用于将主机字节序的端口号转换为网络字节序(大端序)。这是必要的,因为网络协议要求端口号利用网络字节序。
- 6000 是服务器监听的端口号。
设置ip地点
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- sin_addr 字段用于指定IP地点。
- inet_addr("127.0.0.1") 函数用于将点分十进制的IP地点转换为网络字节序的二进制格式。
- "127.0.0.1" 是当地回环地点,表示服务器将监听当地回环接口。
4. 绑定套接字
int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
if (res == -1) {
printf("bind err\n");
exit(1);
}
这段代码的目的是确保服务器的套接字能够被正确地绑定到指定的IP地点和端口号上,这是服务器能够监听并担当客户端连接请求的前提。
- bind() 函数是用于将一个套接字绑定到一个特定的网络地点和端口上的函数。
- 第一个参数 sockfd 是之前通过 socket() 函数创建的套接字的文件形貌符。这个文件形貌符是一个整数,用于标识这个套接字。
- 第二个参数 (struct sockaddr*)&saddr 是一个指向 sockaddr 布局体的指针,该布局体包罗了要绑定的地点信息。这里 saddr 是一个 sockaddr_in 类型的布局体,专门用于IPv4地点。通过将 saddr 的地点转换为 struct sockaddr* 类型,可以将其传递给 bind() 函数。
- 第三个参数 sizeof(saddr) 是 saddr 布局体的大小。这个大小告诉 bind() 函数需要多少字节来存储地点信息。sizeof(saddr) 确保了无论 saddr 布局体中包罗多少字段,bind() 函数都能正确地处理惩罚它。
5. 监听连接请求
res = listen(sockfd, 5);
if (-1 == res) {
exit(1);
}
- listen() 函数是用于启动套接字的监听过程的。这个函数告诉操作系统,服务器准备好担当连接请求了。
- sockfd 是之前创建的套接字的文件形貌符,它是服务器用来监听连接请求的“通道”。
- 5 是一个参数,指定了服务器应该维护的最大挂起连接数。挂起连接是指那些已经到达但还没有被服务器担当的连接请求。如果设置为5,就意味着服务器可以同时处理惩罚5个期待担当的连接请求。如果超过5个,新的连接请求可能会被拒绝。
6. 担当客户端连接
while (1) {
int len = sizeof(caddr);
int c = accept(sockfd, (struct sockaddr*)&caddr, &len);
if (c < 0) {
continue;
}
printf("accept c=%d\n", c);
}
- sizeof(caddr) 计算 caddr 布局体占用的字节数,这样程序就知道在 accept() 函数中需要多少空间来存储客户端的地点信息。
- accept() 函数用于担当客户端的连接请求。
- 第一个参数 sockfd 是服务器监听的套接字文件形貌符。
- 第二个参数是指向 caddr 布局体的指针,caddr 用来存储连接请求中包罗的客户端地点信息。
- 第三个参数 &len 是一个指向 len 变量的指针,len 表示 caddr 布局体的大小,accept() 函数会更新这个值,以反映实际接收到的地点信息的大小。
7. 数据互换
char buff[128] = {0};
int n = recv(c, buff, 127, 0);
printf("n=%d,recv:%s\n", n, buff);
send(c, "ok", 2, 0);
close(c);
- 定义一个缓冲区 `buff`,用于存储从客户端接收的数据。
- `recv()` 函数用于从客户端接收数据,并将接收到的数据存储在 `buff` 中。
- 打印接收到的数据的长度和内容。
- `send()` 函数用于向客户端发送数据,这里发送字符串 "ok"。参数 c 是与客户端通信的套接字文件形貌符。
- `close()` 函数用于关闭与客户端的连接。
总结
这段代码实现了一个简单的TCP服务器,它创建一个套接字,绑定到当地地点和端口,监听客户端的连接请求,并与客户端进行数据互换。如果出现错误,程序将打印错误信息并退出。
2.2 cil客户端
客户端程序,用于连接到服务器并进行数据互换
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- int main()
- {
- int sockfd = socket(AF_INET,SOCK_STREAM,0);//创建套接子 ipv4,tcp流式
- if( -1 == sockfd )
- {
- exit(1);
- }
- struct sockaddr_in saddr;//定义套接子地址,(服务器 ip + port)
- memset(&saddr,0,sizeof(saddr));
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(6000);
- saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//连接服务器,
- if( -1 == res )
- {
- printf("connect err\n");
- exit(1);
- }
- printf("input\n");
- char buff[128] = {0};
- fgets(buff,128,stdin);
- send(sockfd,buff,strlen(buff)-1,0);//给服务器发送数据
- memset(buff,0,sizeof(buff));
- recv(sockfd,buff,127,0);//接收服务器返回的数据
- printf("buff=%s\n",buff);
- close(sockfd);
- exit(0);
- }
复制代码 - int res = connect(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
- if (-1 == res) {
- printf("connect err\n");
- exit(1);
- }
复制代码
- connect() 函数用于连接到服务器。
- 参数 sockfd 是之前创建的套接字的文件形貌符。
- 参数 (struct sockaddr*)&saddr 是一个指向 saddr 布局体的指针,包罗了服务器的地点信息。
- 参数 sizeof(saddr) 是 saddr 布局体的大小。
- 如果连接失败,返回 -1,程序将打印错误信息并退出。
- printf("input\n");
- char buff[128] = {0};
- fgets(buff, 128, stdin);
- send(sockfd, buff, strlen(buff) - 1, 0);
- memset(buff, 0, sizeof(buff));
- recv(sockfd, buff, 127, 0);
- printf("buff=%s\n", buff);
复制代码- 提示用户输入数据。
- 利用 fgets() 函数从标准输入读取一行数据,存储在 buff 中。
- 利用 send() 函数将数据发送到服务器。留意,发送的数据长度是 strlen(buff) - 1,因为 fgets() 会将换行符也读取到缓冲区中。
- 利用 memset() 函数清空 buff,为接收数据做准备。
- 利用 recv() 函数从服务器接收数据,最多接收127字节。
- 打印接收到的数据。
- 这段代码实现了一个简单的客户端程序,它连接到当地服务器(127.0.0.1:6000),发送用户输入的数据,并接收服务器返回的数据。如果连接或数据传输失败,程序将打印错误信息并退出。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |