Linux网络编程2

TCP编程

顺序图

socket() 函数

socket()函数用于创建一个新的套接字。它是进行网络编程的第一步,因为所有的网络通信都需要通过套接字来进行。

原型:

#include <sys/socket.h>  

int socket(int domain, int type, int protocol);

    domain:指定协议族,对于TCP/IP网络,它通常是AF_INET(IPv4)或AF_INET6(IPv6)。
    type:指定套接字类型,对于TCP连接,它通常是SOCK_STREAM。
    protocol:指定协议,通常对于AF_INET和SOCK_STREAM,协议为0,表示使用TCP协议。

返回值:成功时返回一个非负整数(套接字描述符),失败时返回-1,并设置errno。

bind() 函数

bind()函数用于将套接字与特定的IP地址和端口号绑定起来。这样,服务器就可以在这个特定的IP地址和端口上监听客户端的连接请求。

原型:

#include <sys/socket.h>  

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

    sockfd:通过socket()函数创建的套接字描述符。
    addr:指向sockaddr结构的指针,该结构包含IP地址和端口号。对于IPv4,通常使用sockaddr_in结构。

addr:采用struct sockaddr的结构体地址,通用结构体

struct sockaddr

{

        sa_family_t sa_family;

        char sa_data[4];

}

struct sockaddr_in

{         基于Internel通信结构体

        as_family_t sin_family;

        in_port_t sin_port;

        struct in_addr sin_addr;

        sin_zero , //填充字节,需清零

}

struct in_addr

{

        uint32_t s_addr;

}

    addrlen:addr参数指向的地址结构的长度。

返回值:成功时返回0,失败时返回-1并设置errno。

举个例子

其中注释

  • 使用socket()函数创建一个TCP套接字,并将其描述符存储在server_fd中。
  • 使用memset()函数将server_addr结构清,以确保所有未明确设置的字段都被初始化为0。
  • 设置server_addr结构的sin_familyAF_INET,表示使用IPv4地址。
  • sin_addr.s_addr设置为INADDR_ANY,这表示服务器将监听所有可用的网络接口
  • 使用htons()函数将端口号主机字节序转换为网络字节序,并将其存储在sin_port中。
  • 使用bind()函数将套接字server_fdserver_addr指定的地址和端口绑定
  • 注意:示例中没有实现监听连接和接受连接的代码,这些通常通过listen()accept()函数来完成。
  • 最后,使用close()函数关闭套接字以释放资源。

关于使用bind函数的结构体

bind()函数的调用中,你需要将sockaddr_in结构体的地址(即一个指向该结构体的指针)作为参数传递。你通过取地址操作符&来获取这个地址,并将其强制转换为struct sockaddr *类型(因为bind()函数的原型要求这个参数是struct sockaddr *类型,这是一个更通用的套接字地址结构体指针,sockaddr_in是它的一个特例)。

同时,你需要传递结构体的大小作为bind()函数的第三个参数。这是为了确保函数能够正确地解释传递给它的地址信息。你使用sizeof(server_addr)来获取这个大小。

listen() 函数

listen() 函数用于将套接字设置为监听状态,以接受连接请求。一旦套接字被设置为监听状态,它就可以接受来自客户端的连接请求。

函数原型:

#include <sys/socket.h> 

int listen(int sockfd, int backlog);

    sockfd:是之前通过socket()函数创建的套接字描述符。
    backlog:指定了内核应该为相应套接字排队的最大连接个数。当多个客户端同时尝试连接时,这个参数限制了可以处于半连接状态(即收到SYN包,但还未收到客户端确认的ACK包)的TCP连接的数量。注意,实际可排队的连接数可能会小于请求数,这取决于系统限制。(内核中的服务器的套接字fd会维护2个链表:1正在三次握手的客户端链表(数量=2*backlog+1)2已经完成三次握手分配好了的newfd)。如:listen(fd,5);//表示系统允许11(5*2+1)个客户同时进行三次握手

返回值:成功时返回0;失败时返回-1,并设置errno以指示错误。

accept() 函数

accept() 函数用于接受一个连接。当套接字处于监听状态时,accept() 函数会阻塞(除非套接字被设置为非阻塞模式),直到一个连接请求到达。一旦连接被接受,accept() 会创建一个新的套接字描述符,用于与连接的客户端进行通信,而原始的套接字描述符(即传递给listen()的那个)则继续用于监听新的连接请求。

函数原型:

#include <sys/socket.h> 

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

    sockfd:是之前通过socket()函数创建的并已经通过listen()设置为监听状态的套接字描述符。
    addr:是一个指向sockaddr结构的指针,该结构用于返回连接客户端的地址信息(可以用作查找客户机)。如果不需要客户端的地址信息,可以设置为NULL。
    addrlen:是一个指向socklen_t的指针,用于传入addr结构的大小,并在函数返回时更新为实际返回的地址信息的大小。

返回值:成功时返回一个新的套接字描述符,用于与连接的客户端通信;失败时返回-1,并设置errno以指示错误。

案例(四个函数一起使用):

关于上述的最后输出的printf("Connection accepted from %s:%d\n"inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

inet_ntoa()ntohs() 是两个用于网络编程的函数,它们分别用于将网络字节序(network byte order)转换为主机字节序(host byte order)的不同表示形式(socket函数介绍中有提到)。

inet_ntoa()用于将网络地址(通常是IPv4地址)从网络字节序的二进制形式转换为点分十进制字符串表示(例如,将 192.168.1.1 的二进制形式转换为文本字符串 "192.168.1.1")。

ntohs() 则是专门用于将无符号短整型(通常是端口号)从网络字节序转换为主机字节序。

通过上述学习的函数就可以成功写出一个简单的服务器端的代码,如下

如若要写客户端的代码,还需学习一个connect()函数

connect()函数

在网络编程中,connect 函数是一个非常重要的函数,它用于客户端程序来建立一个到服务器的连接。这个函数通常在套接字(socket)编程中使用,特别是在使用TCP协议时。connect 函数尝试将客户端的套接字与服务器上的套接字(或称为端口)建立连接。
函数原型:

在C语言(以及许多其他支持网络编程的语言)中,connect 函数的原型通常如下所示:

#include <sys/types.h>  
#include <sys/socket.h> 

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明

    sockfd:这是由socket函数返回的套接字描述符,代表客户端的套接字。
    addr:这是一个指向sockaddr结构(或其变体,如sockaddr_in用于IPv4)的指针,该结构包含了服务器的地址和端口信息。
    addrlen:这是addr参数所指向的结构的长度,以字节为单位。这允许connect函数知道它应该读取多少字节的地址信息。

返回值:

    成功时,connect函数返回0。
    出错时,返回-1,并设置全局变量errno以指示错误类型。

代码如下

实现功能如下

即一发一收,实现数据传递

这个代码可以被优化

下图修改了获取地址,通过这样可以自动搜索地址,无需宏定义地址

原代不能获取客户端的信息,修改之后通过accept()函数获取了客户端的地址与端口号信息

也能实现同样的功能

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值