【无标题】TCP服务器与客户端的简易搭建与交互

本文介绍了TCP协议的基础知识,包括端口的使用和TCP连接的握手与挥手过程。详细讲解了TCP服务器和客户端的搭建步骤,重点阐述了recv()和send()函数的用途。并给出了多个实例,如字符串长度交换、时间服务器、文件传输等,展示了服务器与客户端的实际交互操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、相关基础

tcp协议:面向连接的,一对一的可靠通信

可靠性:严格的应答机制

每一个数据包都有一个对应的编号

(1)端口:

标识进程 无符号短整型:0~65535 01024被内核使用。用户可指定端口:102465535

字节库:大端库  小端库
网络字节序通常是大端序:大端就是指地址存放高字节
个人pc的字节序通常是小端序:小端就是指地址存放高字节

结论:需要在网络绑定port端口是进行字节序的转换

eg:

#include <stdio.h>

int main()
{
	int a=0x11223344;
	char *p=(char *)&a;
	printf("%p %#x\n",p,*p);
	printf("%p %#x\n",p+1,*(p+1));
	printf("%p %#x\n",p+2,*(p+2));
	printf("%p %#x\n",p+3,*(p+3));
	return 0;
}

输出:
在这里插入图片描述
如图,低字节44存放在FE14低地址中,高字节11存放在FE17高地址中,这就是小端序,我们大部分系统都是小端序。

(2)客户端与服务器的连接(握手)与断开(挥手)

ACK: 应答

SYN:请求

FIN:请求断开

在这里插入图片描述
在这里插入图片描述

2、TCP服务器和客户端的搭建

tcp流程 服务器:socket() --> bind() --> listen() --> accept() --> read/write or recv/send -->close()
客户端:socket() --> connect() --> read/write or recv/send -->close()

这里只介绍两个函数,其他函数说明因为说过了,可以看之间的博客。

recv()—接收
#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

send()—发送
#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

3、实例说明

(1).客户端给服务器发送一个字符串,服务器返回给客户端这个字符串的长度 sprintf(buf, “%d”, strlen(buf));
(2).实现一个时间服务器,客户端发送time,服务器返回当前时间。
time_t val = time(NULL); char *p = ctime(&val); char data[64] = {0}; strcpy(data, p);
(3).如果客户端发送get 1.txt的请求,服务器获取文件内容后,发送给客户端。
(4)服务器与客户端的交互
代码演示:

1、TCP服务器:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.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 buf[64]={0};
	while(1)
	{
		fgets(buf,64,stdin);//获取字符串
		buf[strlen(buf)-1]='\0';去除换行符
		send(connfd,buf,strlen(buf),0);发送到客户端
	}
	close(connfd);//关闭
}

char *getTime(char *buf)//获取时间
{
    struct tm *t_cur;//定义tm结构体,便于自己能得到想要的时间戳格式
    time_t t;
  
    memset(buf,0,sizeof(buf));//清空缓冲区
   
    time(&t);
    t_cur=localtime(&t);
    sprintf(buf,"%d年/%d月/%d日 %d:%d:%d\n",t_cur->tm_year+1900,t_cur->tm_mon+1,t_cur->tm_mday,t_cur->tm_hour,t_cur->tm_min,t_cur->tm_sec);//将自己想要的时间戳格式输入到buf里
    return buf;
}

int main(int argc, char *argv[])
{ 
    int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建套接字
    if(sockfd<0)
    {
        perror("socket");
        return -1;
    }
    //初始化地址和端口
    struct sockaddr_in ser={0};
    int len=sizeof(ser);
    ser.sin_family=AF_INET;
    ser.sin_port=htons(9898);
    ser.sin_addr.s_addr=htonl(INADDR_ANY);//ser.sin_addr.s_addr=inet_addr("0");自动路由,寻找ip
    
    int on=1;
    //端口复用函数,为了解决端口号被系统占用的情况
    int res=setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    if(res==-1)
    {
    	perror("setsockopt");
    	return -1;
    }
    
    int ret=bind(sockfd,(struct sockaddr *)&ser,len);//绑定套接字
    if(ret<0)
    {
        perror("bind");
        return -1;
    }
    ret=listen(sockfd,10);//监听套接字
    if(ret<0)
    {
        perror("bind");
        return -1;
    }
   
    
    struct sockaddr_in clientaddr={0};
    int n=sizeof(clientaddr);
    while(1)
    {    
	    printf("wait for the client\n");
	    //等待客户端连接
	    //建立连接请求
	    int connfd=accept(sockfd,(struct sockaddr *)&clientaddr,&n);
	    if(connfd<0)
	    {
		perror("accept");
		return -1;
	    }
	    printf("client ip:%s client port:%d\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
	    
	    pthread_t pid;//创建子进程,进行双向交互
	    pthread_create(&pid,NULL,send_data,(void *)&connfd);
	    pthread_detach(pid);
	    
	    //收发数据(重点)
	    char buf[64]={0};
	    while(1)
	    {
		int m=recv(connfd,buf,64,0);//接收数据
		if(m<0)
		{
		    perror("read");
		    return -1;
		}
		else if(m==0)//客户端关闭了
		{
		    close(connfd);
		    printf("客户端已关闭  请重新连接客户端!\n");
		 
		    break;
		}
		
		else 
		{
			if(strcmp(buf,"time\n")==0)//匹配时间戳
			{
				sprintf(buf,"%s",getTime(buf));
				printf("rev success\n");
				send(connfd,buf,sizeof(buf),0);
				memset(buf,0,64);//buf清零
				
			}
			else if(strcmp(buf,"get 1.txt\n")==0)//获取文件内容
			{
				char data[64]={0};
				
				FILE *fp=fopen("1.txt","r");
				fread(data,sizeof(data),1,fp);
				send(connfd,data,strlen(data),0);
			}
			else
			{
				printf("%s",buf);//除了上面两种情况,输出
				sprintf(buf," %ld",strlen(buf)-1);
				printf("rev success\n");
				
				send(connfd,buf,sizeof(buf),0);
				memset(buf,0,64);//buf清零
			}
			memset(buf,0,64);
		}
	    }
	    
    }
    close(sockfd);//关闭套接字
    
    return 0;
} 

TCP客户端//除了流程外,代码意思大致相同,就不说明了

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

void *recv_data(void *arg)
{
	int sockfd=*(int *)arg;
	char buf[64]={0};
	while(1)
	{
		int n=recv(sockfd,buf,sizeof(buf),0);
		if(n<=0)
		{
			perror("pid read");
			return NULL;
		}
		
		printf("%s",buf);
	}
	//close(sockfd);
}

int main(int argc, char *argv[])
{ 
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
    {
        perror("socket");
        return -1;
    }
    struct sockaddr_in clientaddr={0};
    int len=sizeof(clientaddr);
    clientaddr.sin_family=AF_INET;
    clientaddr.sin_port=htons(9898);
    clientaddr.sin_addr.s_addr=htonl(INADDR_ANY);//0.0.0.0
    
    int ret=connect(sockfd,(struct sockaddr *)&clientaddr,len);
    if(ret<0)
    {
        perror("connect");
        return -1;
    }
    
    pthread_t pid;
    pthread_create(&pid,NULL,recv_data,(void *)&sockfd);
    pthread_detach(pid);
    //收发数据(重点)
    char buf[64]={0};
    //char str[64]={0};
    while(1)
    {
       fgets(buf,64,stdin);//获取输入的字符串
       if(strcmp(buf,"time\n")==0)
       {
	       send(sockfd,buf,strlen(buf),0);
	       memset(buf,0,64);//buf清零
	       recv(sockfd,buf,64,0);
	       printf("nowtime is %s",buf);
	       memset(buf,0,64);  
       }
       else if(strcmp(buf,"get 1.txt\n")==0)
       {
       	recv(sockfd,buf,64,0);
       	printf("%s",buf);
       	memset(buf,0,64);
       }
       else
       {
    
	       send(sockfd,buf,strlen(buf),0);
	       memset(buf,0,64);//buf清零
	       recv(sockfd,buf,64,0);
	       printf("sizeof is %s\n",buf);
	       memset(buf,0,64);
       }
    }
    close(sockfd);
    return 0;
} 

1.txt内容:
在这里插入图片描述

运行结果;
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值