网络连接-TCP通信连接

1. TCP

全双工通信、面向连接、可靠

TCP(即传输控制协议):是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)。

高可靠原因: 1. 三次握手、四次挥手

2. 序列号和应答机制

3. 超时,错误重传机制

4. 拥塞控制、流量控制(滑动窗口)

适用场景

适合于对传输质量要求较高的通信

在需要可靠数据传输的场合,通常使用TCP协议

MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议

2. TCP流程流程

服务器:
   1.创建流式套接字(socket())------------------------>  有手机
   2.指定本地的网络信息(struct sockaddr_in)----------> 有号码
   3.绑定套接字(bind())------------------------------>绑定电话
   4.监听套接字(listen())---------------------------->待机
   5.链接客户端的请求(accept())---------------------->接电话
   6.接收/发送数据(recv()/send())-------------------->通话
   7.关闭套接字(close())----------------------------->挂机

客户端:
   1.创建流式套接字(socket())----------------------->有手机
   2.指定服务器的网络信息(struct sockaddr_in)------->有对方号码
   3.请求链接服务器(connect())---------------------->打电话
   4.发送/接收数据(send()/recv())------------------->通话
   5.关闭套接字(close())--------------------------- >挂机

3. 要求

1.  客户端连接成功后进入循环发送状态,从终端获取用户输入并发送,当用户输入“quit”字符后退出循环并关闭客户端

2.  客户端输入quit退出后,服务器不退出,等待下一个客户端连接

循环服务器:一个服务器可以连接多个客户端,但是不能同时连接

3.  地址和端口都通过参数传入

4.  自动获取本机地址

5.  增加来电显示功能:显示客户端连入的地址

4. 服务器端

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

// 服务器:
//    1.创建流式套接字  socket()
//    2.指定本地的网络信息  struct sockaddr_in
//    3.绑定套接字  bind()
//    4.监听套接字  listen()
//    5.链接客户端的请求  accept()
//    6.接收/发送数据  recv()/send()
//    7.关闭套接字  close()

int main(int argc, char const *argv[])
{

    int res;

    // 1.创建流式套接字
    int serverfd = socket(AF_INET, SOCK_STREAM, 0);
    if (serverfd < 0)
    {
        perror("serverfd socket error");
        ;
        return -1;
    }
    printf("server socket scuess\n");

    // 2.指定本地的网络信息  struct sockaddr_in
    //  bind 第二个参数 struct sockaddr *addr
    // 定义自己的ip地址,用ipv4的地址

    // 绑定自己的地址
    // 定义结构体变量 myaddr
    struct sockaddr_in myaddr;

    // 长度
    socklen_t addrlen = sizeof(myaddr);

    memset(&myaddr, 0, addrlen);
    myaddr.sin_family = AF_INET; // ipv4族
    // 结构体成员变量是sin_addr ,sin_addr是struct in_addr 结构体类型的,成员变量为 s_addr,
    // 网络ip地址需要转成机器识别的类型;
    // myaddr.sin_addr.s_addr = inet_addr("192.168.52.197");
    myaddr.sin_addr.s_addr = INADDR_ANY;
    // 端口
    myaddr.sin_port = htons(atoi(argv[1]));

    // 3.绑定套接字  bind()
    // 绑定自己的地址
    // int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    // 需要强转成通用结构体类型的地址
    res = bind(serverfd, (struct sockaddr *)&myaddr, addrlen);
    if (res < 0)
    {
        perror("bind error");
        return -1;
    }
    printf("bind scuess\n");

    // 4.监听套接字  listen()
    // int listen(int sockfd, int backlog);
    res = listen(serverfd, 5);
    if (res < 0)
    {
        perror("listen error");
        return -1;
    }
    printf("listen scuess\n");

    // 5.链接客户端的请求  accept()
    // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    // 定义代表客户端的结构体变量
    struct sockaddr_in acceptaddr;
    int acceptfd;
    while (1)
    {
        acceptfd = accept(serverfd, (struct sockaddr *)&acceptaddr, &addrlen);
        if (acceptfd < 0)
        {
            perror("accept error");
            return -1;
        }
        printf("acceptfd: %d \n", acceptfd);
        printf("新的连接过来了\n");
        printf("ip = %s, port = %d\n", inet_ntoa(acceptaddr.sin_addr),
               ntohs(acceptaddr.sin_port));

        // 6.接收/发送数据  recv()/send()

        char buf[128] = "";
        ssize_t num;
        while (1)
        {
            // 每次接收前清空buf
            memset(buf, 0, sizeof(buf));
            num = recv(acceptfd, buf, sizeof(buf), 0);
            // num为接收到的字符个数
            if (num < 0) // 接收失败
            {
                perror("recv error");
                return -1;
            }
            else if (num == 0) // 客户端退出
            {
                perror("client exit");
                break;
            }
            else // 正常接收到字符
            {
                printf("buf: %s \n", buf);
            }
        }
    }

    close(serverfd);
    close(acceptfd);

    return 0;
}

5. 客户端

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

// 客户端:
//    1.创建流式套接字      socket()
//    2.指定服务器的网络信息    struct sockaddr_in
//    3.请求链接服务器   connect()
//    4.发送/接收数据    send()/recv()
//    5.关闭套接字   close()

int main(int argc, char const *argv[])
{

    // 1.创建流式套接字
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if (clientfd < 0)
    {
        perror("clientfd socket error");
        ;
        return -1;
    }
    printf("client socket scuess\n");

    // 2.指定服务器的网络信息    struct sockaddr_in

    // 定义结构体变量 server_addr
    struct sockaddr_in server_addr;

    server_addr.sin_family = AF_INET; // ipv4族
    // 结构体成员变量是sin_addr ,sin_addr是struct in_addr 结构体类型的,成员变量为 s_addr,
    // 网络ip地址需要转成机器识别的类型;
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    // 端口
    server_addr.sin_port = htons(atoi(argv[2]));

    // 3.请求链接服务器   connect()
    // 长度
    socklen_t addrlen = sizeof(server_addr);

    // 客户端的socket连接服务器
    // int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    // 网络地址为 struct sockaddr_in 结构体类型 ,需要强转为 struct sockaddr * addr 类型
    // 返回值为0,连接成功;返回值为-1,连接失败
    int ret = connect(clientfd, (struct sockaddr *)&server_addr, addrlen);
    if (ret < 0)
    {
        perror("connect err");
        return -1;
    }
    printf("connect scuess\n");

    // 4.发送/接收数据    send()/recv()
    // 一旦连接服务器成功以后,从终端获取用户输入的数据,然后发送给服务器

    char buf[128] = "";
    ssize_t num;
    while (1)
    {
        // 每次发送前清空数组
        memset(buf, 0, sizeof(buf));
        // 从终端读取内容
        num = read(1, buf, sizeof(buf));
        if (buf[strlen(buf) - 1] == '\n')
        {
            buf[strlen(buf) - 1] = '\0';
        }
        if (strcmp(buf, "quit") == 0)
        {
            break;
        }

        // 将终端读取的内容写到套接字中
        write(clientfd, buf, num);
        // 发送内容到服务器
        send(clientfd, buf, num, 0);
    }

    close(clientfd);

    return 0;
}

6. 运行结果

服务器端

客户机端

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值