TCP通信模型(C语言实现)

        大家好,我是练习编程时长两年半的个人练习生昆工第一ikun,今天我们来分享TCP通信模型,并且用C语言实现它。


目录

一、我们将实现三个示例功能:

二、TCP服务器搭建流程

(1)创建套接字 --> socket();

(2)套接字绑定 -->bind() 核心:IP地址与PORT端口

(3)监听客户端连接请求 -->listen

(4)接收客户端连接请求 -->accept


​​​​​​​

一、我们将实现三个示例功能:

1.客户端给服务器发送一个字符串,服务器返回给客户端这个字符串的长度。

2.实现一个时间服务器,客户端发送time,服务器返回当前时间。

3.如果客户端发送get 1.txt的请求,服务器获取文件内容后,发送给客户端。

二、TCP服务器搭建流程

(1)创建套接字 --> socket();

#include  <sys/types.h>         
#include <sys/socket.h>
​
int socket(int family, int type, int protocol);
​
参数:
    family:协议族
        AF_INET:IPv4协议
        AF_INET6:IPv6协议
        AF_LOCAL:UNIX域协议
        AF_ROUTE:路由套接字
        AF_KEY:密钥套接字
    type:套接字类型
        SOCK_STREAM:流式套接字
        SOCK_DGRAM:数据报套接字
        SOCK_RAM:原始套接字
    protocol:0(原始套接字除外)
        
返回值:
        成功返回非负套接字描述符
        失败返回-1;
         

(2)套接字绑定 -->bind() 核心:IP地址与PORT端口

  • 端口:标识进程 无符号短整型: 0 ~ 65535 0 ~ 1024 被内核使用. 用户可指定端口:1025 ~ 65535

    字节序:大端序 与 小端序 
    ​
    网络字节序通常是大端序: 大端就是指低地址存放高字节 
    ​
    个人PC的字节序通常是小端序: 小段就是指低地址存放低字节 
    ​
    结论:需要在网络绑定port端口时进行字节序的转换..... 
    
  • 字节序转换函数:htons、htonl 等....

    可以使用man手册直接查看:man 3 htons 
    ​
    h: 主机host 
    ​
    to: 转换 
    ​
    n: net网络 
    ​
    s: short类型 
    ​
    htons就是把主机字节序转为网络字节序. 
    
  • IP地址:标识主机 ipv4占四个字节.

    点分十进制:"192.168.2.2" 是给人类看的 
    ​
    二进制形式:11000000 10101000 00000010 00000010 是计算机使用的 
    
  • IP地址转换函数

    点分转为二进制: 
    ​
    in_addr_t inet_addr(const char *cp); 
    ​
    cp: ip地址的字符串形式 
    ​
    in_addr_t: 转换后的二进制地址形式
    
        #include <sys/types.h>         
        #include <sys/socket.h>
​
        int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
​
        参数:
            sockfd:套接字描述符
            my_addr:绑定的地址
            addrlen:地址长度
​
        返回值:
                成功返回0;
                失败返回-1;
        

(3)监听客户端连接请求 -->listen

#include <sys/types.h>        
#include <sys/socket.h>
​
int listen(int sockfd, int backlog);
​
参数:
    sockfd:套接字描述符
    backlog:请求队列中允许的最大请求数,大多数系统默认值为5;
    
返回值:
    成功返回0;失败返回-1;
  

(4)接收客户端连接请求 -->accept

        注意:后续的数据通信全部使用通信套接字,不能使用监听套接字来通信

#include <sys/types.h>
#include <sys/socket.h>
​
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
​
参数:
    sockfd:套接字描述符
    addr:用于保护客户端地址,如果不需要客户端信息则传递 NULL
    addrlen:地址长度,如果不需要客户端信息则传递 NULL
    
返回值:
    成功返回用于通信的套接字(连接套接字 或 通信套接字)
    失败返回-1;
    

我们先来创建一个服务器:

#include <strings.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

void *send_data(void *arg)
{
	int connfd = *(int*)arg;
	char s[64] = {0};
	while(1)
	{
		fgets(s, 64, stdin);
		s[strlen(s)-1] = '\0';
		write(connfd, s, 64);
		memset(s, 0, 64);
	}

}

int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("socket");
        return -1;
    }
    
    //端口复用函数
    int on = 1;
    int k = setsockopt(sockfd, SOL_SOCKET,SO_REUSEADDR, &on, sizeof(on));
    if(k == -1)
    {
    	perror("setsockopt");
    	return -1;
    }
    
    
    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons(11111);
    ser.sin_addr.s_addr = inet_addr("0.0.0.0");

    int ret = bind(sockfd, (struct sockaddr *)&ser, sizeof(ser));
    if(ret == 0)
    {
        printf("绑定成功\n");
    }
    else
    {
        perror("bind");
        return -1;
    }

    ret = listen(sockfd, 5);
    if(ret == 0)
    {
        printf("监听成功\n");
    }
    else
    {
        perror("listen");
        return -1;
    }

    struct sockaddr_in client;
    int n = sizeof(client);

    int connfd;
    while(1)
    {
        connfd = accept(sockfd, (struct sockaddr *)&client, &n);
        if(connfd == -1)
        {
            perror("accept");
            return -1;
        }
        else
        {
            printf("连接成功\n");
        }

        printf("client ip:%s\nclient port:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

        pthread_t pid;
        pthread_create(&pid, NULL, send_data, (void *)&connfd);
        pthread_detach(pid);

        

while(1)
{
	char buf[64] = {0};
        ret = read(connfd, buf, 64);
        if(ret < 0)
        {
            perror("read");
            return -1;
        }
        else if(ret == 0)
        {
            close(connfd);
            break;
        }
        	printf(">>:%s\n", buf);
       

            if(strcmp(buf, "time") == 0)
            {              	
               
                time_t now = time(NULL);
                char *p = ctime(&now);
                char data[64];
                strcpy(data, p);
                write(connfd, data, strlen(data));
                 
                memset(buf, 0, 64);
                memset(data, 0, 64);
            }

           else if(strncmp(buf, "get", 3) == 0)
            {
            	
                int i, j = 0;
                char str[64] = {0};
                for(i = 4; i <= strlen(buf)-1; i++)
                {
                    str[j] = buf[i];
                    j++;
                }
                memset(buf, 0, 64);
                int fd = open(str, O_RDONLY);
                while((ret = read(fd, buf, 64)) != 0)
                {
                    write(connfd, buf, ret);
                    memset(buf, 0, 64);
                }
   
                write(connfd, "over", 64);
                memset(buf, 0, 64);
                 memset(str, 0, 64);
                close(fd);
            }
            else
            {
            char arr[64];
             
            sprintf(arr, "%ld", strlen(buf));
            write(connfd, arr, strlen(arr));
            memset(buf, 0, 64);
            memset(arr, 0, 64);
             
            }

    }
    }

    close(sockfd);
    

    return 0;
} 

 

 再来创建一个客户端:

#include <strings.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>

void *recv_data(void *arg)
{
	int sockfd = *(int *)arg;
	char s[64] = {0};
	while(1)
	{
		int ret = read(sockfd, s, 64);
		if(ret < 0)
		{
			perror("read");
			return NULL;
		}
		printf("%s\n", s);
		memset(s, 0, 64);
	}

}
int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("socket");
        return -1;
    }
    
    pthread_t pid;
    pthread_create(&pid, NULL, recv_data, (void *)&sockfd);
    pthread_detach(pid);

    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons(11111);
    //ser.sin_addr.s_addr = inet_addr("0.0.0.0");
    ser.sin_addr.s_addr = htonl(INADDR_ANY);


    int ret = connect(sockfd, (struct sockaddr *)&ser, sizeof(ser));
    if(ret == -1)
    {
        perror("connect");
        return -1;
    }

    char buf[64];
    while(1)
    {
        fgets(buf, 64, stdin);
        buf[strlen(buf)-1] = '\0';
        write(sockfd, buf, 64);
        
        if(strcmp(buf, "time") == 0)
        {
        	char data[64];
            read(sockfd, data, 64);
            printf(">>:%s\n", data);
            memset(buf, 0, 64);
            memset(data, 0, 64); 
        }
        else if(strncmp(buf, "get", 3) == 0)
        {
        	char str[64];
            while(read(sockfd, str, 64) != 0)
            {
            if(strcmp(str, "over") == 0)
            {
 		memset(str, 0, 64);        
             break;
            }
            printf(">>:%s\n", str);
            memset(buf, 0, 64);
            memset(str, 0, 64); 
            }
        }
        else
        {	char arr[64] = {0};
            read(sockfd, arr, 64);
            printf(">>:%s\n", arr);
            memset(buf, 0, 64);
            memset(arr, 0, 64); 
        }
    }
    close(sockfd);

    return 0;
}

 运行结果如下:

 

  • 10
    点赞
  • 98
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
作为AI语言模型,我无法实际编写程序,但我可以提供一些概念性的思路和建议,希望对你有所帮助。 BMC (Baseboard Management Controller) 和 ME (Management Engine) 是两种用于服务器管理的技术。BMC 通常是一个独立的芯片,它可以通过网络或串口连接到主机,提供电源管理、远程控制、诊断等功能。ME 则是 Intel CPU 上的一个内置子系统,它可以在主机关闭或操作系统崩溃的情况下,通过网络连接进行远程管理和维护。 在 C 语言中,你可以使用一些库和 API 来实现和 BMC/ME 的通信。以下是一些可能有用的工具和技术: 1. IPMI (Intelligent Platform Management Interface):这是一种标准化的接口,用于 BMC 和管理软件之间的通信IPMI 定义了一组命令、数据结构和协议,可以通过网络或串口进行传输。在 C 语言中,你可以使用 IPMI 库来封装和解析 IPMI 消息,例如 ipmitool、FreeIPMI 等。 2. MEI (Management Engine Interface):这是 Intel 提供的一种接口,用于 ME 和操作系统之间的通信。MEI 可以通过 PCI、SMBus、SPI 等总线进行传输。在 C 语言中,你可以使用 Intel MEI SDK 来访问 MEI 接口,例如 MEI Driver、MEI Client Library 等。 3. Serial 端口:BMC 通常会提供一个串口来进行本地调试和管理。你可以使用 C 语言中的串口库,如 termios、libserial、serialport 等,来读写串口数据。 4. 网络套接字:BMC 和 ME 可以通过网络进行远程管理。你可以使用 C 语言中的网络库,如 socket、libcurl、libevent 等,来建立 TCP 或 UDP 连接,发送和接收数据。 注意:BMC 和 ME 的通信需要一定的权限和认证机制,以确保安全性和可靠性。在实际应用中,你需要了解具体的协议和规范,并遵循相应的安全措施。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值