【Linux C | 网络编程】入门知识:TCP协议、一个最简单的TCP客户端、一个最简单的TCP服务端

本文介绍了Linux环境下的网络编程基础,详细讲解了TCP协议的工作原理,并提供了TCP客户端和服务端的简单示例代码,包括关键函数的使用,适合初学者了解和实践。
摘要由CSDN通过智能技术生成

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍网络编程入门知识:TCP协议、TCP客户端、TCP服务端 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭

本文未经允许,不得转发!!!


在这里插入图片描述

🎄一、TCP协议概述

TCP协议的全称是传输控制协议(Transmission Control Protocol)。TCP是一个面向连接的协议,为用户进程提供可靠的全双工字节流。TCP套接字是一种流套接字( streamsocket)。TCP关心确认、超时和重传之类的细节。大多数因特网应用程序使用TCP。注意,TCP既可以使用IPv4,也可以使用IPv6。


TCP协议的几个特点:

  • 1、TCP是通过连接(connection)来通信的。TCP客户端与某个TCP服务端建立连接,然后通过这个连接与服务端交互数据,最后终止这个连接。
  • 2、TCP是可靠性(reliability)。当TCP向另一端发送数据时,它要求对端返回一个确认。如果没有收到确认,TCP就自动重传数据并等待更长时间。在数次重传失败后,TCP才放弃,如此在尝试发送数据上所花的总时间一般为4~10分钟(依赖于具体实现)。
    TCP会给每个分节关联一个序列号,如果接收到重复数据,可以判定数据重复并丢弃。
  • 3、TCP提供流量控制(flow control)。TCP总是会告诉对端,它一次能够从对端接收多少个字节的数据,这成为通告窗口(advertised window)。该窗口指出接收缓冲区的当前可用字节。
  • 4、TCP是全双工的(full-duplex)。表示在同一个TCP连接上,同一时刻可以同时发送、接收数据。

在这里插入图片描述

🎄二、一个最简单的TCP客户端

✨2.1 tcp客户端步骤和代码

编写TCP客户端的步骤:

  • 1、创建TCP套接字socket
  • 2、指定TCP服务端ip和端口,bzero、inet_pton
  • 3、建立与TCP服务端的连接,connect
  • 4、交互数据
  • 5、关闭套接字
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main()
{
	// 1、创建TCP套接字socket
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd<0)
		perror("socket error" );
	
	// 2、准备服务端ip和端口
	struct sockaddr_in servaddr;
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons (10086);
	if (inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr) <= 0) // 设置本机IP为服务端IP
		perror("inet_pton error");
	
	// 3、连接 connect
	if (connect(sockfd,(struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
		perror("connect error" );
	
	// 4、交换数据
	printf("TcpCli sockfd=%d, send msg\n",sockfd);
	write(sockfd, "Hello,I am tcp client", strlen("Hello,I am tcp client"));
	
	char recvline[256];
	int n = 0;
	while ( (n = read (sockfd, recvline, sizeof(recvline))) > 0)
	{
		recvline[n] = 0 ;/*null terminate */
		printf("recv[%s]\n",recvline);
	}
	
	if (n < 0)
		perror("read error" );
	
	// 5、关闭
	close(sockfd);

	return 0;
}

✨2.2 tcp客户端相关函数

  • socket 函数

    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    int socket(int domain, int type, int protocol);
    

    socket函数创建一个套接字描述符。man手册描述,socket创建一个通信端点并返回一个描述符。

    • 参数domain指定通信域,使用了AF_INET,表示是IPv4 Internet protocols协议的。
    • 参数type指定套接字类型,使用``,表示TCP协议的套接字(提供有序、可靠、双向、基于连接的字节流。可以支持带外数据传输机制)。
    • 参数protocol指定了要与套接字一起使用的特定协议。通常,在给定的协议族中,只有一个协议支持特定的套接字类型,在这种情况下,协议可以指定为0。
  • connect 函数

    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    

    connect 函数将文件描述符sockfd引用的套接字连接到addr指定的地址。addrlen参数指定addr的大小。addr中地址的格式由套接字sockfd的地址空间决定;

在这里插入图片描述

🎄三、一个最简单的TCP服务端

✨2.1 tcp服务端步骤和代码

编写TCP服务端的步骤:

  • 1、创建TCP套接字socket
  • 2、准备服务端ip和端口;
  • 3、绑定,bind
  • 4、监听,listen
  • 5、接受客户端连接,accept
  • 6、与客户端进行数据交互
  • 7、关闭套接字
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main()
{
	// 1、创建TCP套接字socket
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd<0)
		perror("socket error" );
	
	// 2、准备服务端ip和端口
	struct sockaddr_in servaddr;
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons (10086);
	servaddr.sin_addr.s_addr = INADDR_ANY; // 指定ip地址为 INADDR_ANY,这样要是服务器主机有多个网络接口,服务器进程就可以在任一网络接口上接受客户端的连接
	
	// 3、绑定 bind
	if (bind(sockfd,(struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
		perror("bind error" );
	
	// 4、监听 listen
	if(listen(sockfd, 10) != 0)
		perror("listen error");
	
	printf("TcpSer sockfd=%d, start listening\n",sockfd);
	char recvline[256];
	while(1)
	{
		// 5、接受客户端连接
		int connfd = accept(sockfd, NULL, NULL);
		
		// 6、与客户端交换数据
		int n = read(connfd, recvline, sizeof(recvline));
		if(n>0)
		{
			recvline[n] = 0 ;/*null terminate */
			printf("recv connfd=%d [%s]\n",connfd,recvline);
		}
		write(connfd, "Hello,I am tcp server", strlen("Hello,I am tcp server"));
		close(connfd);
	}
	
	// 7、关闭
	close(sockfd);

	return 0;
}

socketbindlisten这3个调用步骤是任何TCP服务器准备所谓的监听描述符(listening descriptor)的正常步骤。


✨2.2 tcp服务端相关函数

tcp服务端相关函数有:socket、bind、listen、accept。socket函数可以参照上面 2.2 小节。

  • bind 函数
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
    bind函数将addr指定的地址分配给文件描述符sockfd引用的套接字。addrlen指定addr指向的地址结构的大小。
  • listen 函数
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    int listen(int sockfd, int backlog);
    
    listen函数将sockfd引用的套接字标记为被动套接字,标记后,可以使用accept函数接受传入连接请求的套接字。
    • backlog参数定义了sockfd的挂起连接队列可能增长到的最大长度。如果连接请求在队列已满时到达,则客户端可能会接收到带有ECONNREFUSED指示的错误,或者,如果底层协议支持重传,则可以忽略该请求,以便稍后重新尝试连接成功。
  • accept 函数
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
    accept函数用于基于连接的套接字类型(SOCK_STREAM、SOCK_SEQPACKET)。它提取侦听套接字的挂起连接队列中的第一个连接请求sockfd,创建一个新的连接套接字,并返回一个引用该套接字的新文件描述符。新创建的套接字未处于侦听状态。原始套接字sockfd不受此调用的影响。

在这里插入图片描述

🎄四、总结

👉本文主要介绍Linux下网络编程的基础知识,先是简单介绍一下TCP协议,然后给出一个TCP客户端、TCP服务端最简单的例子,最后介绍创建TCP客户端、TCP服务端需要用到的函数。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个简单TCP客户端服务端的通信的示例代码,支持推送指定长度的数据。请注意,此示例代码仅供参考,可能需要根据实际情况进行修改和优化。 服务器端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #define BUF_SIZE 100 int main(int argc, char *argv[]) { int server_socket, client_socket; struct sockaddr_in server_addr, client_addr; char buffer[BUF_SIZE]; int str_len, i; if (argc != 2) { printf("Usage: %s <port> \n", argv[0]); exit(1); } server_socket = socket(PF_INET, SOCK_STREAM, 0); if (server_socket == -1) { printf("Server socket creation failed\n"); exit(1); } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(atoi(argv[1])); if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { printf("Server bind failed\n"); exit(1); } if (listen(server_socket, 5) == -1) { printf("Server listen failed\n"); exit(1); } printf("Server is waiting for client\n"); socklen_t client_addr_size = sizeof(client_addr); client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_size); if (client_socket == -1) { printf("Server accept failed\n"); exit(1); } printf("Client connected\n"); while (1) { str_len = read(client_socket, buffer, BUF_SIZE); if (str_len == 0) { break; } printf("Received message: %s\n", buffer); write(client_socket, buffer, str_len); } close(client_socket); close(server_socket); return 0; } ``` 客户端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #define BUF_SIZE 100 int main(int argc, char *argv[]) { int client_socket; struct sockaddr_in server_addr; char buffer[BUF_SIZE]; int str_len, i; if (argc != 4) { printf("Usage: %s <IP> <port> <length> \n", argv[0]); exit(1); } client_socket = socket(PF_INET, SOCK_STREAM, 0); if (client_socket == -1) { printf("Client socket creation failed\n"); exit(1); } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(argv[1]); server_addr.sin_port = htons(atoi(argv[2])); if (connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { printf("Client connect failed\n"); exit(1); } printf("Connected to server\n"); memset(buffer, 'A', atoi(argv[3])); write(client_socket, buffer, atoi(argv[3])); printf("Sent message: %s\n", buffer); str_len = read(client_socket, buffer, BUF_SIZE); printf("Received message: %s\n", buffer); close(client_socket); return 0; } ``` 运行服务端代码: ``` $ ./server 12345 Server is waiting for client ``` 运行客户端代码: ``` $ ./client 127.0.0.1 12345 10 Connected to server Sent message: AAAAAAAAAA Received message: AAAAAAAAAA ``` 说明: - 服务端代码监听指定端口,并在有客户端连接时接受客户端发送的消息并回复。 - 客户端代码连接指定服务器,并发送指定长度的数据,然后等待服务器回复。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wkd_007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值