Linux从入门到开发实战(C/C++)Day08-Socket网络编程

socketFd:是一个描述符号,表示进程中的网络设备
        分两种:
            1.socket函数创建,创建的socketFd接口对应服务器端的网络设备,用来接受客户端连接
            2.accept函数返回值,创建的socketFd接口对应客户端(一个socket对应一个客户端)
                对socketFd进行读,从客户端接收数据
                        写,发送数据到客户端

        tcp客户端则是只有socket函数创建的socketFd,对应服务器(注意与上面两种进行区分)
            对socketFd进行读,从服务器接收数据
                        写,发送数据到服务器

    协议地址簇:用于区分主机、进程
        三个成员:
            family    通信协议
            addr    ip地址    区分主机
            端口号    区分进程
    绑定:把 socketfd 和 地址协议簇 绑到一起
    监听:绑定后准备好accept
    accept:形成稳定的数据传输通道
        阻塞式等待和有名管道类似

tcp:有连接
        建立连接:三次握手
            1.客户端向服务器发送连接请求(报文SYN)
            2.服务器收到SYN之后向客户端发送收到指令ACK和连接请求SYN
            3.客户端向服务器发送收到指令ACK
        断开连接:四次挥手
            1.客户端向服务器发送断开请求 FIN
            2.服务器收到客户端发来的FIN后回复收到指令ACK
            3.服务器检查是否存在没有收发完的数据
                如果有,先收发完,然后再向客户端发送断开指令/请求 FIN
            4.客户端收到服务器发来的FIN后,检查是否存在没有收发完的数据
                如果有,先收发完,然后回复ACK
        服务器主动断开只需要第3、4步
        如果因为网络问题双方被动断开,会延迟等待一段时间(期间保持连接时候的状态),之后若再没回复连接就主动断开
        tcp的11种状态:
            CLOSED        初始状态    服务器准备建立连接前和断开连接后
            LISTEN        监听状态    服务器绑定监听后的状态
            SYN_RCVD    服务器接收SYN之后,发送ACK之前
            SYN_SENT    客户端发送SYN后,接收SYN和ACK之前

            ESTABLISHED    连接状态    服务器和客户端都有的一个状态,建立稳定的数据传输通道时的状态

            FIN_WAIT1    服务器收到FIN之后,发送ACK之前
            FIN_WAIT2    服务器发送FIN之后,接受ACK之前
            CLOSE_WAIT    客户端接收FIN之后,发送ACK之前
            LAST_ACK    客户端接收ACK之后
            CLOSEING    双方同时发送FIN,等待接收ACK

            TIME_WAIT    等待延迟
tcp编程模型
            服务器                                                    客户端
        1.创建socket                                      1.创建socket(socket)
        2.确定服务器协议地址簇                    2.获取服务器地址簇(struct sockaddr)
        3.绑定(bind)
        4.监听(listen)
        5.接受客户端连接(accept)             3.连接服务器(connect)
        6.通信                                                 4.通信
                      ==收(recv)发(send)都可以==
        7.断开连接                                          5.断开连接(close)

    UDP:无连接
    编程模型:
                server                        client
            1.创建socket                1.创建socket
                    socket    第二个参数变为SOCK_DGRAM
            2.协议地址簇                2.协议地址簇
            3.绑定
            4.通信                           3.通信
                通常使用recvfrom、sendto
                recvfrom == recv + accep    除了接收数据外,还可获取对方协议地址簇
                sendto == send + connect    除了发送数据外,还可以连接到对方(精准发送)
            5.close                        4.close
            
    tcp聊天室:
        1.服务器要能接受多个客户端连接
            如果按照之前的tcp测试代码,可以发现s可以连接c1,当c2打开时提示连接服务器成功,但是s收不到c2发来的信息
                原因:server只accept了一次,所以只有c1对应的fd被保存了下来
                如果先循环多次接收多个客户端连接
                    那么就会卡在连接状态,到不了通信循环
                    解决思路:
                        1)父子进程
                            行不通,因为子进程只是拷贝父进程代码和进程上下文,父子进程socket函数的返回值是不同的两个
                        2)多进程
                            弊端:多进程开销相对很大
                        3) 多线程
                            可行,但有弊端:第四步搞不定,且做不到高并发
                        
        2.服务器能收到客户端发送的消息
            因为scanf会阻塞,所以用select实现同时接受连接和通信
        3.服务器能转发给所有客户端新消息(业务)
            与2同理
        4.服务器能知道客户端断开了连接

tcp相关代码

//server.c
#include <stdio.h>
#include <unistd.h> //linux操作系统标准头文件
#include <string.h> //memcpy
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h> //umask
#include <time.h>
#include <sys/time.h>
#include <dirent.h> //遍历目录
#include <fcntl.h>  //文件映射虚拟内存
#include <sys/mman.h>
#include <wait.h>
#include <signal.h> //SIGCHLD
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int clientFd;
int serverFd;
void hand(int s)
{
    if (2 == s)
    {
        // 7.断开连接
        close(clientFd);
        close(serverFd);
        printf("断开连接\n");
        exit(1);
    }
}

int main()
{
    signal(2, hand);
    // 1.创建socket
    serverFd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == serverFd)
    {
        printf("创建socket失败%m\n");
        return -1;
    }
    printf("创建socket成功\n");

    // 2.确定服务器协议地址簇
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // ip地址 注意字符串转网络字节序
    addr.sin_port = htons(9999);                   // 端口号 用10000左右的 注意大小端转换

    // 3.绑定(bind)
    int r = bind(serverFd, (struct sockaddr *)&addr, sizeof addr);
    if (-1 == r)
    {
        printf("绑定失败%m\n");
        close(serverFd);
        return -1;
    }
    printf("绑定成功\n");

    // 4.监听(listen)
    r = listen(serverFd, 100);
    if (-1 == r)
    {
        printf("监听失败%m\n");
        close(serverFd);
        return -1;
    }
    printf("监听成功\n");

    // 5.接受客户端连接(accept)
    struct sockaddr_in cAddr={0};//用来接收客户端的协议地址簇
    int len = sizeof cAddr;
    clientFd = accept(serverFd, (struct sockaddr*)&cAddr, &len);
    if (-1 == clientFd)
    {
        printf("接受客户端连接失败%m\n");
        close(serverFd);
        return -1;
    }
    printf("接受客户端连接成功 %d %s %u\n",clientFd,inet_ntoa(cAddr.sin_addr),cAddr.sin_port);

    // 6.通信
    char buff[1024];
    int n=0;
    char temp[1024];
    while (1)
    {
        r = recv(clientFd, buff, 1023, 0);
        if (r > 0)
        {
            buff[r] = 0; // 添加字符串结束符号
            printf("接受到%d字节消息:%s\n", r, buff);
            sprintf(temp,"%d-%s",n++,buff);
            send(clientFd,temp,strlen(temp),0);
        }
    }

    return 0;
}
//client.c
int clientFd;
void hand(int s)
{
    if (2 == s)
    {
        // 5.断开连接
        close(clientFd);
        printf("断开连接\n");
        exit(1);
    }
}

int main()
{
    signal(2, hand);
    // 1.创建socket
    clientFd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == clientFd)
    {
        printf("创建socket失败%m\n");
        return -1;
    }
    printf("创建socket成功\n");

    // 2.确定服务器协议地址簇
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // ip地址 注意字符串转网络字节序
    addr.sin_port = htons(9999);                   // 端口号 用10000左右的 注意大小端转换

    // 3.连接服务器
    int r = connect(clientFd, (struct sockaddr *)&addr, sizeof addr);
    if (-1 == r)
    {
        printf("连接服务器失败%m\n");
        close(clientFd);
        return -1;
    }
    printf("连接服务器成功\n");

    // 4.通信
    char buff[1024];
    char temp[1024];
    while (1)
    {
        printf("请输入要发送的内容:");
        scanf("%s", buff);
        r = send(clientFd, buff, strlen(buff), 0);
        printf("发送%d字节数据到服务器\n",r);
        r=recv(clientFd,temp,1023,0);
        if (r > 0)
        {
            printf("服务器回复%d字节消息:%s\n", r, temp);
        }
    }

    return 0;
}

udp相关代码

//server.c
int serverFd;
void hand(int s)
{
    if (2 == s)
    {
        // 7.断开连接
        close(serverFd);
        printf("断开连接\n");
        exit(1);
    }
}

int main()
{
    signal(2, hand);
    // 1.创建socket
    serverFd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == serverFd)
    {
        printf("创建socket失败%m\n");
        return -1;
    }
    printf("创建socket成功\n");

    // 2.确定服务器协议地址簇
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("111.67.205.228");
    addr.sin_port = htons(9999);

    // 3.绑定(bind)
    int r = bind(serverFd, (struct sockaddr *)&addr, sizeof addr);
    if (-1 == r)
    {
        printf("绑定失败%m\n");
        close(serverFd);
        return -1;
    }
    printf("绑定成功\n");

    // 4.通信
    char buff[1024];
    int n = 0;
    char temp[1024];
    struct sockaddr_in cAddr = {0};
    int len = sizeof cAddr;
    while (1)
    {
        // r = recv(serverFd, buff, 1023, 0);
        r = recvfrom(serverFd, buff, 1023, 0, (struct sockaddr *)&cAddr, &len);
        if (r > 0)
        {
            buff[r] = 0; // 添加字符串结束符号
            printf("接受到%d字节消息:%s\n", r, buff);
            memset(temp,0,1024);
            sprintf(temp, "%d-%s", n++, buff);
            // printf("%s\n",temp);
            sendto(serverFd, temp, strlen(temp), 0, (struct sockaddr *)&cAddr, len);
        }
    }

    return 0;
}
//client.c
int clientFd;
void hand(int s)
{
    if (2 == s)
    {
        // 5.断开连接
        close(clientFd);
        printf("断开连接\n");
        exit(1);
    }
}

int main()
{
    signal(2, hand);
    // 1.创建socket
    clientFd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == clientFd)
    {
        printf("创建socket失败%m\n");
        return -1;
    }
    printf("创建socket成功\n");

    // 2.确定服务器协议地址簇
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("111.67.205.228");
    addr.sin_port = htons(9999);

    // 4.通信
    char buff[1024];
    char temp[1024];
    while (1)
    {
        printf("请输入要发送的内容:");
        scanf("%s", buff);
        int r = sendto(clientFd, buff, strlen(buff), 0,(struct sockaddr*)&addr,sizeof addr);
        printf("发送%d字节数据到服务器\n", r);
        memset(temp,0,1024);
        r = recv(clientFd, temp, 1023, 0);
        if (r > 0)
        {
            printf("服务器回复%d字节消息:%s\n", r, temp);
        }
    }

    return 0;
}

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值