socket套接字编程(1)——基本函数

TCP交互流程:

服务器:1. 创建socket;2. 绑定socket和端口号;3. 监听端口号;4. 接收来自客户端的连接请求;5. 从socket中读取字符;6. 关闭socket。

客户端:1. 创建socket;2. 连接制定计算机的端口;3. 向socket中写入信息;4. 关闭socket。

创建socket:

socket函数

int socket (int __family, int __type, int __protocol);

__family是协议域,也称协议族。常见的有AF_INET(ipv4)。

__type指定socket类型。SOCK_STREAM即TCP协议,SOCK_DGRAM即UDP协议。

__protocol指定协议。

该函数返回的socket描述字存在于协议族空间中,但是并没有一个具体的地址。如果想要给它赋予一个地址,就必须调用bind()函数,否则系统就在调用connect()和listen()时自动随机分配一个端口。

这里注意:type和protocol并不能随意组合。当protocol为0时,会自动选择type类型对应的默认协议。

创建socket的样例代码如下:

    //创建TCP套接字
    //AF_INET:网络连接,ipv4
    //SOCK_STREAM:TCP连接
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd<0) {
        std::cout<<"create socket error!"<<std::endl;
        return 0;
    }
    std::cout<<"create socket: "<<fd<<std::endl;

绑定socket和端口号:

bind函数

int bind (int, const struct sockaddr *__my_addr, socklen_t __addrlen);

第一个参数是socket描述字。(我不理解为啥这儿没有参数名)

__my_addr是指向要绑定给该socket的协议地址。这个地址结构根据socket创建时的地址协议族(family)的不同而不同。

__addrlen对应的是地址的长度。

如果该函数执行成功,就返回0,否则为SOCKET_ERROR。

    //命名套接字
    struct sockaddr_in myaddr;
    memset((void *)&myaddr, 0, sizeof(myaddr));
    //关于htonl和htons,参考以下网页:ntohs, ntohl, htons,htonl的比较和详解
    //https://blog.csdn.net/haoxiaodao/article/details/73162663
    myaddr.sin_family = AF_INET;
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    myaddr.sin_port = htons(6666);
    if (bind(fd, (struct sockaddr*)&myaddr, sizeof(myaddr)) < 0) {
        std::cout<<"name socket error!"<<std::endl;
        return 0;
    }
    std::cout<<"name socket"<<std::endl;

监听端口号:

作为一个服务器,在调用socket()和bind()之后就会调用listen()来监听这个socket。为了能够在套接字上接受进入的连接,服务器程序必须创建一个队列来保存未处理的请求。

listen函数

int listen (int, int __n);

第一个参数即socket描述子

__n为队列的大小。

    //创建监听队列
    if (listen(fd, 5) < 0) {
        std::cout<<"listen failed"<<std::endl;
        return 0;
    }

接收来自客户端的连接请求:

当TCP服务器监听到了连接请求之后,就会调用accept()函数接收请求,这样连接就建立好了。

accept函数

int accept (int, struct sockaddr *__peer, socklen_t *);

第一个是socket描述子,第二个是用来接收的客户端地址,第三个是地址的大小。注意第三个是指针类型,所以要事先构造好大小的变量,然后传地址进去。

另外,《后台开发核心技术与应用实践》中的例子,第二个和第三个都传的NULL。我的理解是,如果不需要接收这两个量,就可以传一个空值进去。

accept函数会返回一个新的socket描述子,这个新的描述子代表了服务端和客户端的连接。后面可以用于读取数据以及关闭连接。

   //等待并接受连接
    const int MAXBUF = 4096;
    char buff[MAXBUF];
    struct sockaddr_in client_addr;
    int client_addr_len = sizeof(client_addr);
    int client_fd;
    while (1) {
        client_fd = accept(fd, (struct sockaddr*)&client_addr, &client_addr_len);
        if (client_fd < 0) {
            std::cout<<"connect error"<<std::endl;
            continue;
        }
        //接收数据
        //关闭套接字
    }

未完,待补全。

猜你喜欢

转载自www.cnblogs.com/wangzhao765/p/9161589.html