网络编程学习1(实现一个简单的socket通信)

本文详细介绍了TCP Socket通信的基本原理,包括如何通过IP地址和端口号标识网络进程,以及TCP连接的建立过程。同时,通过C++代码展示了服务端如何使用socket API进行监听和接收客户端连接,并通过fork函数创建子进程实现多客户端并发通信。客户端代码展示了如何发起连接并发送数据。
摘要由CSDN通过智能技术生成

socket通信实现及原理

1.基本原理

  • socket即为套接字,在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一的标识网络通讯中的一个进程,“IP地址+TCP或UDP端口号”就为socket。
  • 在TCP协议中,建立连接的两个进程(客户端和服务器)各自有一个socket来标识,则这两个socket组成的socket pair就唯一标识一个连接。
  • socket本身就有“插座”的意思,因此用来形容网络连接的一对一关系,为TCP/IP协议设计的应用层编程接口称为socket API。
    下面来了解下TCP的通信时序:
    在这里插入图片描述
    可以看到客户端是不进行bind的,当客户端没有自己进行bind时,系统随机分配给客户端一个端口号,并且在分配的时候,操作系统会做到不与现有的端口号发生冲突。但如果自己进行bind,客户端程序就很容易出现问题,假设在一个PC机上开启多个客户端进程,如果是用户自己绑定了端口号,必然会造成端口冲突,影响通信。

2.代码实践

下面直接通过程序来学习:
服务端代码:

//
// Created by viets on 2022/4/16.
//
#include "iostream"
#include "sys/socket.h"
#include "netinet/in.h"
#include "arpa/inet.h"
#include "unistd.h"

int startup(int port, const char* ip_Addr) {
    int listenFd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenFd < 0) {
        std::cout << "Create Socket Return ERROR!" << std::endl;
        exit(1);
    }

    struct sockaddr_in sock_in;
    sock_in.sin_family = AF_INET;
    sock_in.sin_port = htons(port);
    sock_in.sin_addr.s_addr = inet_addr(ip_Addr);

    if (bind(listenFd, (struct sockaddr*)(&sock_in), sizeof(sock_in)) < 0) {
        std::cout << "Socket Message Bind ERROR!" << std::endl;
        exit(2);
    }

    if (listen(listenFd, 5) < 0) {    //允许最大连接数为5
        std::cout << "Listen ERROR!" << std::endl;
    }

    std::cout << "Server StartUP SUCCESS!" << std::endl;

    return listenFd;

}

int main() {
    int port = 2000;
    char* ip_Addr = "0.0.0.0";

    int listenFd = startup(port, ip_Addr);  //startup创建文件描述符

    struct sockaddr_in remote;
    socklen_t len = sizeof(struct sockaddr_in);
    while (true) {
        int n_listenFd = accept(listenFd, (struct sockaddr*)&remote, &len);
        if (n_listenFd < 0) {
            std::cout << "ACCEPT ERROR!" << std::endl;
            continue;
        }
        printf("Get A Client. IP:%s, PORT:%d\n",inet_ntoa(remote.sin_addr), ntohs(remote.sin_port));
        char buf[1024] = {0};
        while (true) {
            ssize_t _s = read(n_listenFd, buf, sizeof(buf)-1);
            if (_s > 0) {
                buf[_s] = 0;
                printf("Client send message: %s\n", buf);
            } else {
                printf("Client is quiet!\n");
                break;
            }
        }

    }
    return 0;
}

上述服务端的代码,实际创建了两个socket返回两个文件描述符,一个是listenFd,其用于承接ip地址及端口的信息;一个是n_listenFd,用于数据的收发。
但是这样实现只能进行单进程通信,也就是说每次只能使一个客户端连接上进行数据通讯,这显然不符合服务器的基本要求。所以可以通过fork函数创建子进程,将监听的socket和数据收发的socket进行分离。代码如下:

//
// Created by viets on 2022/4/17.
//
#include "iostream"
#include "sys/socket.h"
#include "netinet/in.h"
#include "arpa/inet.h"
#include "unistd.h"

int startup(int port, const char* ip_Addr) {
    int listenFd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenFd < 0) {
        std::cout << "Create Socket Return ERROR!" << std::endl;
        exit(1);
    }

    struct sockaddr_in sock_in;
    sock_in.sin_family = AF_INET;
    sock_in.sin_port = htons(port);
    sock_in.sin_addr.s_addr = inet_addr(ip_Addr);

    if (bind(listenFd, (struct sockaddr*)(&sock_in), sizeof(sock_in)) < 0) {
        std::cout << "Socket Message Bind ERROR!" << std::endl;
        exit(2);
    }

    if (listen(listenFd, 5) < 0) {    //允许最大连接数为5
        std::cout << "Listen ERROR!" << std::endl;
    }

    std::cout << "Server StartUP SUCCESS!" << std::endl;

    return listenFd;

}


int main() {

    int port = 2000;
    char* ip_Addr = "0.0.0.0";

    int listenFd = startup(port, ip_Addr);

    struct sockaddr_in remote;
    socklen_t len = sizeof(struct sockaddr_in);

    while (true) {
        int messageFd = accept(listenFd, (struct sockaddr*)&remote, &len);
        if (messageFd < 0) {
//            std::cout << "Accept Socket ERROR!" << std::endl;
            continue;
        }
        std::cout << "Accept Socket Success!" << std::endl;

        pid_t pid = fork();
        if (pid < 0) {
            std::cout << "Create Sub-process FAILED!" << std::endl;
            return 1;
        } else if (pid == 0) {
            //子进程负责信息socket的数据收发
            close(listenFd);
            char buf[1024];
            while (true) {
                ssize_t _s = read(messageFd, buf, sizeof(buf));
                if (_s > 0) {
                    std::cout << "Receive Message: "<< buf << std::endl;
                } else {
                    std::cout << "Client is quiet!" << std::endl;
                    break;
                }
            }
        } else {
            //父进程负责监听
            close(messageFd);
        }

    }
    return 0;
}

客户端代码:

//
// Created by viets on 2022/4/16.
//
#include "sys/socket.h"
#include "iostream"
#include "stdio.h"
#include "netinet/in.h"
#include "arpa/inet.h"
#include "unistd.h"
#include "string.h"

int main() {

    int port = 2000;
    char* ip_Addr = "172.31.105.110";


    int clientFd = socket(AF_INET, SOCK_STREAM, 0);
    if (clientFd < 0) {
        std::cout << "Create Client Socket FAILED!" << std::endl;
        return 1;
    }

    struct sockaddr_in sock_info;
    sock_info.sin_family = AF_INET;
    sock_info.sin_port = htons(port);
    sock_info.sin_addr.s_addr = inet_addr(ip_Addr);
    socklen_t len = sizeof(struct sockaddr_in);

    if (connect(clientFd, (struct sockaddr*)&sock_info, len) < 0) {
        std::cout << "Connect ERROR!" << std::endl;
        return 2;
    }
    std::cout << "Connect SUCCESS!"<< std::endl;
    char* buf = "Hello Server!\n";
    std::cout << strlen(buf) << std::endl;
    while (true) {
        std::cout << "Sending: " << buf << std::endl;
        int len = write(clientFd, buf, strlen(buf));
        sleep(2);
        if (len < 0) {
            std::cout << "Message Send ERROR!" << std::endl;
            break;
        }
    }
    close(clientFd);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值