Linux网络编程(五)—— socket编程

本文详细介绍了socket编程的基本函数接口,包括创建套接字的socket()、绑定地址的bind()、服务器的listen()和客户端的connect()、接受请求的accept()、读写操作的read()和write()以及关闭套接字的close()函数,还总结了server和client建立连接时使用的函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

socket编程


基本函数接口


创建套接字的函数socket()

socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。

函数原型声明

#include <sys/types.h>
#include <sys/socket.h>
int socket( int family, int type, int protocol);

正如可以给fopen的传入不同参数值,以打开不同的文件。创建socket的时候,也可以指定不同的参数创建不同的socket描述符,
socket函数的三个参数分别为

  • family ,即协议族,又称为协议域(domain) , 创建的套接字的协议族, AF_XXX
    常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。

    协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。

  • type ,指定socket类型 ,创建的套接字的类型, SOCK_XXX
    常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等(socket的类型有哪些?)。

  • protocol,故名思意,就是指定协议 ,创建的套接字的协议,与type有关
    常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

注意:并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。
protocol为0时,会自动选择type类型对应的默认协议

当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就要调用connect()、listen()时系统会自动随机分配一个端口


绑定地址函数bind()[服务器使用]

通过socket调用创建的套接字必须经过命名(绑定地址)后才能使用。
bind系统调用把addr中的地址分配给与描述符socket关联的未命名套接字,地址结构的长度由·addr_len指定。addraddr_len因地址族**(AF_UNIX、AF_INET等)的不同而不同,bind调用时需要将指向特定地址结构的指针转化为指向通用地址的指针, 即 (struct sockaddr *)
注意,此函数一般
只由服务器使用**,通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。

函数原型声明

#include <sys/socket.h>
int bind( int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:

  • sockfd,即socket描述字
    它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。

  • addr,待绑定的地址
    一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,分别指向前面的。
    参数addr为通用地址结构,一般只需提供固定的端口号,即如下设置

    struct sockaddr_in server_add;      /*  服务器信息  */
    server_add.sin_family = AF_INET;    /*  IPV4地址格式  */
    server_add.sin_addr.s_addr = htonl(INADDR_ANY);  /*  接受任意IP地址的客户连接  */
    server_add.sin_port = htons(port);       /*  port为服务器端指定的端口号,unsigned short 		int类型  */
    
    server_len = sizeof(server_add);
    
    bind(server_sockfd, (struct sockaddr*)&server_add, server_len);
    

服务器listen()和客户端connect()

服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket。
客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。

listen函数原型声明

int listen( int sockfd, int backlog);

参数介绍

  • 参数1: 即为要监听的socket描述字;
  • 参数2: 为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

服务器accpet设置模板代码
参数addr指向的socketadd地址结构用来存放将要连接到的客户的地址,只有accept成功返回时才有效。如果不关心客户地址,可以将addr参数指定为空指针;

int client_sockfd;
struct sockaddr_in client_add;
clinet_len = sizeof(client_add);

client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_add, &client_len);

connect函数原型声明

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

参数

  • 参数1: 即为客户端的socket描述字;
  • 参数1: 第二参数为服务器的socket地址;
  • 参数1: 第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。

客户端connect设置模板代码

	struct sockaddr_in server_add;
	server_add.sin_family = AF_INET;
	serevr_add.sin_addr.s_addr = inet_addr("***.***.***.***");
	//服务器地址,无需htonl转换,因为inet_addr已定义为网络字节序
	server_add.sin_addr.sin_port = htons(port);  //port,int型变量,与服务器端相同的端口号
	len = sizeof(server_add);
	connect(sockfd, (struct sockaddr *)&server_add, len);  //可根据返回值判断连接状态

accept()函数

TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。
TCP客户端依次调用socket()、connect()之后就向TCP服务器发送了一个连接请求。
TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。

函数原型声明

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

参数介绍:

  • 参数1: 服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;
  • 参数2: 指向struct sockaddr *的指针,用于返回客户端的协议地址;
  • 参数3: 协议地址的长度。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。

accept函数返回的是已连接的socket描述字。

一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。

read()、write()等函数

调用网络I/O进行读写操作了,即实现了网咯中不同进程之间的通信!网络I/O操作有下面几组:

read( ) / write( )
recv( ) / send( )
readv( ) / writev()
recvmsg( ) / sendmsg( )
recvfrom( ) / sendto( )

推荐使用recvmsg()/sendmsg()函数,这两个函数是最通用的I/O函数,实际上可以把上面的其它函数都替换成这两个函数。

read函数是负责从fd中读取内容.

  • 当读成功时,read返回实际所读的字节数,如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误。如果错误为EINTR说明读是由中断引起的,如果是ECONNREST表示网络连接出了问题。

write函数将buf中的nbytes字节内容写入文件描述符fd.

  • 成功时返回写的字节数。失败时返回-1,并设置errno变量。 在网络程序中,当我们向套接字文件描述符写时有俩种可能。1)write的返回值大于0,表示写了部分或者是全部的数据。2)返回的值小于0,此时出现了错误。我们要根据错误类型来处理。如果错误为EINTR表示在写的时候出现了中断错误。如果为EPIPE表示网络连接出现了问题(对方已经关闭了。
ssize_t read(int fd, void *buf, size_t count);  
ssize_t write(int fd, const void *buf, size_t count);  
ssize_t send(int sockfd, const void *buf, size_t len, int flags);  

ssize_t recv(int sockfd, void *buf, size_t len, int flags);  
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,  const struct sockaddr *dest_addr,socklen_t addrlen);  
ssize_t recvfrom(int sockfd,void *buf,size_t len, int flags,  struct sockaddr *src_addr, socklen_t *addrlen);  


ssize_t sendmsg(int sockfd,const struct msghdr *msg, int flags);  
ssize_t recvmsg(int sockfd,struct msghdr *msg,int flags);  

close()函数

在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。

close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。

int close(int fd);

close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。

在建立连接时,server和client使用的函数总结

server

  • socket —— 创建服务器监听套接字
    服务器应用程序用系统调用socket创建一个套接字。 它是系统分配给服务器进程的类似文件描述符的资源。

  • bind —— 绑定服务器监听信息到套接字
    服务器进程用系统调用bind命名套接字。 然后服务器进程就开始等待客户连接到这个命名套接字。

  • listen —— 开始监听,接收客户端的TCP连接

  • accept —— 从listen所维护的队列中取出一条已连接的TCP,返回该连接的socket描述字
    服务器通过系统调用accept来接受客户的连接。 accept会创建一个不同于命名套接字的新套接字来与这个特定客户进行通信,而命名套接字则被保留下来继续处理其他客户的连接请求。

client

  • socket —— 创建客户端连接套接字
    调用socket创建一个未命名套接字。

  • connect —— 向指定服务器发起连接请求
    调用connect与服务器建立连接,将服务器的命名套接字作为一个地址。
    然后服务器客户端在连接socket描述字上进行消息通信

  • close —— 关闭打开着的套接字

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值