首页 > 系统相关 >2024-02-29-Linux高级网络编程(4-TCP编程)

2024-02-29-Linux高级网络编程(4-TCP编程)

时间:2024-03-01 09:59:36浏览次数:26  
标签:02 socket int 编程 TCP server client 接字 include

4. TCP编程

4.1 TCP介绍

  1. 面向连接的流式协议;可靠、出错重传、且每收到一个数据都要给出相应的确认
  2. 通信之前需要建立链接
  3. 服务器被动链接,客户端是主动链接
image-20240229160146428

TCP编程流程

服务器:
1. 创建套接字 socket()
2. 将套接字与服务器网络信息结构体绑定 bind()
3. 将套接字设置为监听状态 listen()
4. 阻塞等待客户端的连接请求 accept()进行通信 recv()/send()
5. 关闭套接字 close()

客户端:
1. 创建套接字 socket()
2. 发送客户端连接请求 connect()
3. 进行通信 send()/recv()
4. 关闭套接字 close()

4.2 TCP编程-socket

#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain,int type, int protocol);
功能:创建一个套接字,返回一个文件描述符;
参数:
	domain:通信域,协议族
		AF_UNIX		本地通信
		AF_INET		ipv4网络协议
		AF_INET6	ipv6网络协议
		AF_PACKET 	底层接口
	type:套接字的类型
		SOCK_STREAM 	流式套接字(tcp)
		SOCK_DGRAM		数据报套接字(udp)
         SOCK_RAW		原始套接字(用于链路层)
    protocol: 附加协议,如果不需要,则设置为0
 返回值:
        成功:文件描述符
        失败:-1
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    int socket_id;
    if ((socket_id = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("FAIL TO CREAET TCP SOCKET");
        exit(1);
    }
    printf("socket_id = %d \n", socket_id);
    return 0;
}

输出结果

socket_id = 3 

4.3 TCP客户端 -connect、send、recv

4.3.1 connect函数

#include<sys/types.h>
#include<sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
                   
功能:给服务器发送客户端的连接请求;
参数:
	sockfd:文件描述符,socket函数的返回值
	addr:要连接的服务器的网络信息结构体(需要自己设置);
	addrlen:add的长度返回值:
成功:0
失败:-1

4.3.2 send函数

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

功能:发送数据
参数:
	sockfd:文件描述符
		   客户端:socket函数的返回值;
		   服务器:accept函数的返回值;
    buf:发送的数据
    len:buf的长度
    flags:标志位
        0 阻塞
        MSG DONTWAIT 非阻塞
返回值:	
    成功:发送的字节数
    失败:-1

4.3.3 recv函数

#include <sys/types.h>
#include <sys/socket.h>
 ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能:接收数据
参数:	
	sockfd:文件描述符
		客户端:socket函数的返回值
		服务器:accept函数的返回值
    buf:保存接收到的数据
    len:buf的长度
    flags:标志位
			0 阻塞。
			MSG DONTWAIT 非阻塞
返回值:
	成功:接收的字节数
	失败:-1
注意:如果发送端关闭文件描述符或者关闭进程,则recv函数会返回0

4.4 TCP服务端- bind、listen、accept

4.4.1 bind函数

#include<sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:将套接字与网络信息结构体绑定;
参数:
	sockfd:文件描述符,socket的返回值;
	addr:网络信息结构体,通用结构体(一般不用)struct sockaddr
		网络信息结构体 sockaddr_in
        	#include <netinet/in.h>
        	struct sockaddr_in
	addrlen:addr的长度
返回值:
    成功:0
    失败:-1

4.4.2 listen函数

#include <sys/socket.h>
int listen(intsockfd, int backlog)
功能: 将套接字由主动修改为被动
	使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接;
参数:
	sockfd: socket监听套接字
	backlog: 连接队列的长度,即同一时间允许客户端连接的个数;
返回值:
    成功:返回 0
    失败:其他

4.4.3 accept函数

#include <sys/types.h>         
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:
	阻塞等待客户端的连接请求
参数:
	sockfd:文件描述符,socket函数的返回值;
	addr:接收到的客户端的信息结构体(自动填充,定义变量即可);
	addrlen:addr的长度
返回值:
	成功:新的文件描述符(只要有客户端连接,就会产生新的文件描述符; 这个新的文件描述符专门与指定的客户端进行通信的)
	失败:-1

4.5 close 函数、三次握手、四次挥手

4.5.1 close函数

  1. 使用 close 函数即可关闭套接字
    关闭一个代表已连接套接字将导致另一端接收到一个0长度的数据包
  2. 做服务器时
    1. 关闭监听套接字将导致服务器无法接收新的连接,但不会影响已经建立的连接
    2. 关闭 accept返回的已连接套接字将导致它所代表的连接被关闭,但不会影响服务器的监听
  3. 做客户端时
    关闭连接就是关闭连接,不意味着其他

4.5.2 三次握手

image-20240229172704257

4.5.3 四次挥手

image-20240229173029130

4.6 TCP并发服务器

TCP原本不是并发服务器,TCP服务器同一时间只能与一个客户端进行通信。

TCP不能实现并发的原因:TCP服务端有两个阻塞函数accept和recv,所以导致运行一个函数的时候,另一个函数无法执行;这样使得TCP无法保证一边连接客户端,一边与其他客户端通信。

那么,如何实现TCP并发服务器呢?

  1. 使用多进程实现TCP并发服务器
  2. 使用多线程实现TCP并发服务器

4.6.1 多进程实现并发

02_mulit_process_tcp_server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/wait.h>

#define MAX_BUFFER_SIZE 1024
#define PORT 8080
#define MAX_CONNECTIONS 5

void handle_client(int client_socket) {
    char buffer[MAX_BUFFER_SIZE];
    ssize_t num_bytes;

    while ((num_bytes = recv(client_socket, buffer, MAX_BUFFER_SIZE, 0)) > 0) {
        buffer[num_bytes] = '\0';
        printf("Received message: %s\n", buffer);

        // 处理客户端请求,这里简单地将收到的消息原样发送回去
        send(client_socket, buffer, num_bytes, 0);
    }

    if (num_bytes == 0) {
        printf("Client disconnected\n");
    } else {
        perror("recv");
    }

    close(client_socket);
}

int main() {
    int server_socket, client_socket;
    struct sockaddr_in server_address, client_address;
    socklen_t client_address_size;
    pid_t child_pid;
    int status;

    // 创建套接字
    server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (server_socket == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置服务器地址和端口
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = INADDR_ANY;
    server_address.sin_port = htons(PORT);

    // 绑定套接字到指定地址和端口
    if (bind(server_socket, (struct sockaddr *)&server_address, sizeof(server_address)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    // 监听连接请求
    if (listen(server_socket, MAX_CONNECTIONS) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    printf("Server listening on port %d\n", PORT);

    while (1) {
        // 接受客户端连接请求
        client_address_size = sizeof(client_address);
        client_socket = accept(server_socket, (struct sockaddr *)&client_address, &client_address_size);
        if (client_socket == -1) {
            perror("accept");
            exit(EXIT_FAILURE);
        }

        // 创建子进程来处理客户端请求
        child_pid = fork();
        if (child_pid == -1) {
            perror("fork");
            exit(EXIT_FAILURE);
        }

        if (child_pid == 0) {
            // 子进程
            close(server_socket);
            handle_client(client_socket);
            exit(EXIT_SUCCESS);
        } else {
            // 父进程
            close(client_socket);
            // 回收子进程资源,避免僵尸进程
            waitpid(-1, &status, WNOHANG);
        }
    }

    // 关闭服务器套接字
    close(server_socket);

    return 0;
}

03_multi_process_tcp_client.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

#define MAX_BUFFER_SIZE 1024
#define SERVER_IP "127.0.0.1"
#define PORT 8080

int main() {
    int client_socket;
    struct sockaddr_in server_address;
    char buffer[MAX_BUFFER_SIZE];
    ssize_t num_bytes;

    // 创建套接字
    client_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (client_socket == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置服务器地址和端口
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = inet_addr(SERVER_IP);
    server_address.sin_port = htons(PORT);

    // 连接到服务器
    if (connect(client_socket, (struct sockaddr *)&server_address, sizeof(server_address)) == -1) {
        perror("connect");
        exit(EXIT_FAILURE);
    }

    printf("Connected to server\n");

    while (1) {
        printf("Enter a message (or 'q' to quit): ");
        fgets(buffer, MAX_BUFFER_SIZE, stdin);

        // 移除换行符
        buffer[strcspn(buffer, "\n")] = '\0';

        // 检查是否退出客户端
        if (strcmp(buffer, "q") == 0) {
            break;
        }

        // 发送消息给服务器
        if (send(client_socket, buffer, strlen(buffer), 0) == -1) {
            perror("send");
            exit(EXIT_FAILURE);
        }

        // 接收服务器的响应
        num_bytes = recv(client_socket, buffer, MAX_BUFFER_SIZE - 1, 0);
        if (num_bytes == -1) {
            perror("recv");
            exit(EXIT_FAILURE);
        } else if (num_bytes == 0) {
            printf("Server disconnected\n");
            break;
        }

        buffer[num_bytes] = '\0';
        printf("Server response: %s\n", buffer);
    }

    // 关闭套接字
    close(client_socket);

    return 0;
}

image-20240229175602593

4.6.2 多线程实现并发

void * thread_fun(){
    
}

scoketfd = socket();
bind();
listen();
while(1){
    accept();
    //只要有客户端,就创建子线程与之通信
    pthread_create(,,thread_fun,);
    pthread_detach();
    
}

4.7 web服务器介绍

web服务器又称为www服务器、网站服务器等

4.7.1 特点

  1. 使用HTTP协议与客户机浏览器进行交流
  2. 不仅能够存储信息,还支持用户通过web服务器运行脚本和程序
  3. 服务器一般部署在UNIX、Linux或者window等操作系统上,

4.7.2 HTTP协议

概念:一种详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议
特点:

  1. 支持C/S架构
  2. 简单快速:客户向服务器请求服务时,只需传送请求方法和路径,常用方法:GET、POST
  3. 无连接: 限制每次连接只处理一个请求
  4. 无状态: 即如果后续处理需要前面的信息,它必须重传,这样可能导致每次连接传送的数据量会增大

4.7.3 Web编程开发

image-20240301093438729

标签:02,socket,int,编程,TCP,server,client,接字,include
From: https://www.cnblogs.com/hasaki-yasuo/p/18046267

相关文章

  • AC475B 2024省选联测26 排列
    题意对于所有满足\(1\lea<b\len\)的\((a,b)\)的排列,需要满足:对于\(1\lea<b<c\len\),\((a,c)\)处在\((a,b)\)和\((b,c)\)之间。另外再给出\(m\)个限制,形如\((a,b,c,d)\)要求\((a,b)\)在\((c,d)\)的前面。Sol其实这道题没有那么hard......
  • HEOI2024省选游记
    day0没让不跑操的同学帮忙带着包所以就直接背着跑的操,相当难受吃完早饭就拿手机来机房了不得不说看得出来huge这届确实打算换一种教学思路以来就先强调了一堆意料之外的东西包括但不限于不让玩游戏(高二的颓我不管,就管你们)必须带本书看,whk的都行分配房间不让自......
  • 20240229-日记
    可能是因为周一周二加班后遗症,今天的工作进度也不怎么样,最近又临近房子退租,搬家的各个事项又很繁琐。今天一个同事离职的lastday,请团队总共五个小兵一起吃饭,怎么说呢,也没有想象中的推杯换盏,也不会推心置腹。社会是真正极其复杂的,只要我自己做到不害别人,不内耗自己,努力争取自己最......
  • LWIP RAW接口TCP与UDP部分函数解析
    RAWTCP接口tcp_input()函数voidtcp_input(structpbuf*p,structnetif*inp) --->staticerr_ttcp_process(structtcp_pcb*pcb) --->staticvoidtcp_receive(structtcp_pcb*pcb) --->>TCP_EVENT_RECV(pcb,recv_data,ERR_OK,err);//调用用户注册......
  • 【专题】2024食品行业预制菜趋势报告PDF合集分享(附原数据表)
    原文链接:https://tecdat.cn/?p=35240原文出处:拓端数据部落公众号预制菜行业经历了由默默无闻到备受争议,再到如今逐渐获得大众接受的历程。随着《中央一号文件》的推动,除夕节假日的利好安排,以及行业内对半年内有望出台国家标准的期待,预制菜行业吸引了众多头部主播的关注和数亿网......
  • 【专题】2022工业互联网平台发展指数报告PDF合集分享(附原数据表)
    原文链接:https://tecdat.cn/?p=33647这份报告合集是基于中国工业产业升级和智能制造的大背景而展开的。报告合集分析了工业互联网平台市场的发展阶段、平台玩家的产品和服务的底层逻辑以及变化趋势,并探讨了补贴减少、数据归属权之争、标准化与盈利模式、ChatGPT等因素对工业互联......
  • [IOI2021] 分糖果
    Khong阿姨正在给附近一所学校的学生准备\(n\)盒糖果。盒子的编号分别为\(0\)到\(n-1\),开始时盒子都为空。第\(i\)个盒子\((0\leqi\leqn-1)\)至多可以容纳\(c[i]\)块糖果(容量为\(c[i]\))。Khong阿姨花了\(q\)天时间准备糖果盒。在第\(j\)天\((0\leqj......
  • 2023 3 月 HL 集训总结
    犹豫了一下,还是把对应题目的总结放到对应的天数里面,到时候比较好找3.5晚自习上luogu测了一下春测分数,100+85+100+15=300,不太好,暴力分应该是100+100+100+40=340的说,高分应该在380左右。自己仔细想了想T2,在自己算法的基础上用聪明一点的爆搜过掉了,挺可惜的。然后想了较......
  • 2023 7 月总结
    经典的,日期在当天的题可能并不是当天做的,这时为了分类的考虑。总结绝大部分都是在8月份写的,算是把每一个题都做了两遍了。7.3太久没碰OI了,感觉非常手生。「CF1394D」BoboniuandJianghusolvebymyself.思维含量不大,但是讲究了一个简洁与否的问题,但是我做得很不好,特别......
  • 2023 8 月总结
    7.31模拟赛17:40入场,T1一眼SB8:10T2不会,md8:30还是不会/fn/fn/fn8:45过T19:00T3求大。9:20T2插不动,艹10:00T2纯sb11:00成功罚坐1h,崽种!11:45还在罚坐,崽种!感觉这场体验不太好,感觉题目质量不行。T1一眼秒了,看到至少一次很容易二项式反演,看到\(l\le10^9\)很......