Level6day3 作业

1. 已知TCP编程中已经通过socket()函数获取到了套接字fd, 请使用绑定函数bind()函数对fd进行”绑定”

附:bind()函数的原型如下:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

/*2.1 填充struct sockaddr_in结构体变量 */
    bzero((void*)&mysockaddr, sizeof(mysockaddr));
    mysockaddr.sin_family = AF_INET;
    mysockaddr.sin_port = htons(SERV_PORT);//网络字节序的端口号   
    mysockaddr.sin_addr.s_addr = htonl(INADDR_ANY);

/*2.2 绑定 */
    result = bind(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr));
    if (result != 0) {
        perror("bind");
        return -1;
    }    
  1. 请编写基于TCP协议的简单客户端和服务器交互的代码。(作业要求:做作业的时候不要再翻看视频上的教程,对函数理解不明白的全部通过man手册去查看,
    使用makefile编译,然后将测试的记录和结果添加到readme.txt文件中提交上来,代码实现完成测试通过后再提交作业,网络部分学习不写代码不测试看不出问题的,
    良好的习惯帮助你们快速成长。)
/*
    server project 2020/8/31
*/

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

#define SERV_PORT 5050
#define SERV_IP_ADDR "192.168.0.158"    //此处IP请自行修改成本机IP
int main()
{
    int fd = -1, fd_client;
    int result = 0;
    struct sockaddr_in mysockaddr;
    struct sockaddr_in clientscokaddr;
    socklen_t clientaddrlen;
    char botton[64];

    /* 1. 创建socket fd */
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
        perror("socket");
        return -1;
    }

    /*2. 绑定 */
    /*2.1 填充struct sockaddr_in结构体变量 */
    bzero((void*)&mysockaddr, sizeof(mysockaddr));
    mysockaddr.sin_family = AF_INET;
    mysockaddr.sin_port = htons(SERV_PORT);//网络字节序的端口号
#if 1    
    //mysockaddr.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);
    mysockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
#else
    result = inet_pton(AF_INET, SERV_IP_ADDR, (void*)&mysockaddr.sin_addr);
    if (result != 1) {
        perror("inet_pton");
        return -1;
    }
#endif
    /*2.2 绑定 */
    result = bind(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr));
    if (result != 0) {
        perror("bind");
        return -1;
    }

    /*3. 调用listen()把主动套接字变成被动套接字 */
    result = listen(fd, 5);
    if (result != 0) {
        perror("listen");
        return -1;
    }

    /*4. 阻塞等待客户端连接请求 */
#if 0
    bzero((void*)&clientscokaddr, sizeof(clientscokaddr));
    fd_client = accept(fd, NULL, NULL);
    if (-1 == result) {
        perror("accpet");
        return 0;
    }
#else
    /*优化2:通过程序获取刚建立连接的socket的客户端的IP地址和端口号*/
    bzero((void*)&clientscokaddr, sizeof(clientscokaddr));
    fd_client = accept(fd, (struct sockaddr*)&clientscokaddr, &clientaddrlen);
    if (-1 == result) {
        perror("accpet");
        return 0;
    }
    char ipv4_addr[16];
    if (!inet_ntop(AF_INET, (void*)&clientscokaddr.sin_addr, ipv4_addr, clientaddrlen)) {
        perror("inet_ntop");
        return -1;
    }

    printf("Clinet(%s:%d) is connected!\n", ipv4_addr, ntohs(clientscokaddr.sin_port));     //此处有疑问 为何取到的IP地址和端口号为0.0.0.0:0

#endif
    FILE* pf = fopen("1.txt", "a+");
    if (pf <= 0) {
        perror("pf");
        return -1;
    }
    int len = 0,i=0;
    for (i; i < 3;i++) {
        result = read(fd_client, botton, 63);
        if (result < 0) {
            perror("read");
            break;
        }
        printf("message is:%s\n", botton);
        len = strlen(botton);
        result = fwrite(botton,len,1,pf);
        if (result != 1) {
            perror("fwrite");
            return -1;
        }
        sleep(10);
    }
    fclose(pf);
    close(fd);
    close(fd_client);
    return 0;
}
/*
    client project 2020/8/31
*/

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

#define SERV_PORT 5050
#define SERV_IP_ADDR "192.168.0.158"    //此处请自行修改为本机IP

int main()
{
    int fd = -1;
    int result = 0;
    struct sockaddr_in mysockaddr;
    char MESSAGE[] = "hELLo wOLd?\n";
    /* 1. 创建socket fd */   
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
        perror("socket");
        return -1;
    }
    /*2. 绑定 */
    /*2.1 填充struct sockaddr_in结构体变量 */
    bzero((void*)&mysockaddr, sizeof(mysockaddr));
    mysockaddr.sin_family = AF_INET;
    mysockaddr.sin_port = htons(SERV_PORT);
#if 0    
    mysockaddr.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);
#else
    result = inet_pton(AF_INET, SERV_IP_ADDR, (void*)&mysockaddr.sin_addr);
    if (result != 1) {
        perror("inet_pton");
        return -1;
    }
#endif

    result = connect(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr));
    if (result != 0) {
        perror("connect");
        return -1;
    }
    printf("Client staring...OK!\n");
        
    while (1) {
        do {
            result = write(fd, (void*)MESSAGE, sizeof(MESSAGE));
        } while (result < 0 && EINTR == errno);   //判断server是否阻塞了连接 详见附录1         
        sleep(10);
    }
     
    close(fd);
    return 0;
}

Makefile_server:
gcc -o server *.cpp -lpthread -Wall

Makefile_client:
gcc -o client *.cpp -lpthread -Wall

效果展示:


附录

    ssize_t write(int fd,const void *buf,size_t nbytes)

      write函数将buf中的nbytes字节内容写入文件描述符fd。成功时返回写的字节数,失败时返回-1,并设置errno变量。
    在网络程序中,当我们向套接字文件描述符写时有俩种可能:

    1) write的返回值大于0,表示写了部分或者是全部的数据;
    2) 返回的值小于0,此时出现了错误,我们要根据错误类型来处理。

    如果错误为EINTR表示在写的时候出现了中断错误。如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接)。
    本文中应表示为socket server写入阻塞