C语言TCP编程流程

C语言TCP编程流程

服务器:

​ 创建套接字socket()

​ 将套接字与服务器网络信息结构体绑定bind()

​ 将套接字设置为监听状态listen()

​ 阻塞等待客户端的连接请求accept()

​ 进行通信recv()/send()

​ 关闭套接字close()

客户端:

​ 创建套接字socket()

​ 发送客户端连接请求connect()

​ 进行通信send()/recv()

​ 关闭套接字close()

一、创建TCP套接字

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <string.h>
#include <stdlib.h>

int main()
{
    //通过socket函数创建一个TCP套接字
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    printf("sockefd = %d\n",sockfd);
}

二、TCP客户端-connect、send、recv

connect函数

int connect(int sockfd,const struct sockaddr*addr,socklen_t len);

功能

  • 主动跟服务器建立连接

参数

  • sockfd: 套接字
  • addr: 连接的服务器地址结构
  • len:地址结构体长度

返回值

  • 成功:0
  • 失败:其他

注意:

  1. connect建立连接之后不会产生新的套接字
  2. 连接成功之后才可以传输TCP数据
  3. 头文件:#include <sys/socket.h>

send函数

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

功能

  • 用于发送数据

参数

  • sockfd: 已建立连接的套接字
  • buf: 发送数据的地址
  • nbytes:发送缓冲区数据的大小(以字节为单位)
  • flags:套接字标志(常为0)

返回值

  • 成功:返回发送的字节数
  • 失败:其他

头文件

  • include <sys/socket.h>

注意:

  1. 不能用TCP协议发送0长度的数据包

recv函数

ssize_t recv(int sickfd,void*buf,size_t nbytes,int flags);

功能

  • 用于接收网络数据

参数

  • sockfd:套接字
  • buf:接收网络数据缓冲区的地址
  • nbytes:接收缓冲区的大小(以字节为单位)
  • flags:套接字标志(0表示阻塞,MSG_DONTWAIT表示非阻塞)

返回值

  • 成功:返回接收到的套接字
  • 失败:-1
  • 如果发送端关闭文件描述符或者关闭进程,则recv函数会返回0

头文件

  • include <sys/socket.h>

client实例

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

#define N 128
int main(int argc,char *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr,"Usage:%s [ip] [port]\n",argv[0]);
        exit(1);
    }

    //第一步:通过socket函数创建一个TCP套接字
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    printf("sockefd = %d\n",sockfd);

    //第二步:发送客户端连接请求

    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));
    socklen_t addr_len = sizeof(serveraddr);

    if(connect(sockfd,(struct sockaddr*)&serveraddr,addr_len) == -1)
    {
        perror("fail to connect");
        exit(1);
    }

    while(1)
    {
    //第三步:进行通信
    //发送数据

    char buf[N] = "";
    fgets(buf,N,stdin);
    buf[strlen(buf) -1] = '\0';
    if(send(sockfd,buf,N,0) == -1)
    {
        perror("fail to send");
        exit(1);
    }

    //接收数据
    char text[N] = "";
    if(recv(sockfd,text,N,0) == -1)
    {
        perror("fail to recv");
        exit(1);
    }

    printf("from server:%s\n",text);
    }

    //第四步:关闭套接字文件描述符
    close(sockfd);

}

三、TCP服务器-bind、listen、accept

服务器需要具备条件

  1. 具备一个可以确知的地址
  2. 让操作系统知道是一个服务器,而不是客户端
  3. 等待连接的到来

对于面向连接的TCP协议来说,连接的建立才真正意味着数据通信的开始

bind()函数的使用

int bind(int sockfd,const struct sockaddr *my_addr,socklen_t addrlen);

功能:

  • 将本地协议地址与sockfd绑定

参数

  • sockfd: 套接字
  • my_addr: 指向特定协议的地址结构指针
  • addrlen: 该地址结构的长度

返回值

  • 成功: 返回0
  • 失败: -1

listen函数

int listen(int sockfd,int backlog);

功能

  • 将套接字主动修改为被动
  • 使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接

参数

  • sockfd:监听套接字
  • backlog:能同时服务器的套接字连接队列长度

返回值

  • 成功:返回0
  • 失败:-1

头文件

  • include <sys/socket.h>

accept函数

int accept(int sockfd,struct sockaddr* cliaddr,socklent_t *addrlen);

功能

  • 从已连接的队列中取出一个已经建立的连接,如果没有任何连接作用,则进入睡眠等待(阻塞)

参数

  • sockfd:socket监听套接字
  • cliaddr:用于存放客户端套接字地址结构
  • addrlen:套接字地址结构体长度的地址

返回值

  • 成功:返沪已连接的套接字(只要有客户端连接,就会产生新的文件描述符,这个新的文件描述符专门与指定的客户端通信的)
  • 失败:-1

注意

  • 返回的是已连接的套接字,这个套接字代表当前这个连接

server示例

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

#define N 128

int main(int argc,char *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr,"Usage:%s [ip] [port]\n",argv[0]);
        exit(0);
    }

    //第一步:创建套接字
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    //第二部:将套接字与服务器网络信息结构绑定
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));

    if(bind(sockfd,(struct sockaddr*)&serveraddr,addrlen) == -1)
    {
        perror("fail to bind");
        exit(1);
    }

    //第三步:将套接字设置为被动监听状态
    if(listen(sockfd,10) == -1)
    {
        perror("fail to listen");
        exit(1);
    }

    //第四步:阻塞等待客户端的连接请求
    int acceptfd;
    struct sockaddr_in clientaddr;
    

    if((acceptfd = accept(sockfd,(struct sockaddr*)&clientaddr,&addrlen)) == -1)
    {
        perror("fail to accept");
        exit(1);
    }

    //打印连接的客户端信息
    printf("ip:%s,port:%d\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));

    //第五步:进行通信
    //tcp服务器与客户端通信时,需要使用accept函数的返回值
    char buf[N] = "";
    if(recv(acceptfd,buf,N,0) == -1)
    {
        perror("fail to recv");
        exit(1);
    }

    printf("from client:%s\n",buf);

    strcat(buf,"*_*");

    if(send(acceptfd,buf,N,0) == -1)
    {
        perror("fail to send");
        exit(1);
    }

    close(acceptfd);
    close(sockfd);

    return 0;
}

client客户端与server服务器运行结果图

image-20220604131332470

posted @ 2022-06-04 13:16  LiuTBaby  阅读(659)  评论(0编辑  收藏  举报