Linux网络编程基础API【socket】

    xiaoxiao2025-02-04  47

    文章目录

    创建socket命名socket监听socket接受连接发起连接关闭连接代码示例

    创建socket

    linux的思想是一切东西皆为文件,socket也不例外,他就是个可读可写可控制可关闭的文件描述符。接下来我们看看如何创建一个socket。

    #include<sys/types.h> #include<sys/socket.h> int sockfd=socket(int domain,int type,int protocol); //创建套接字 assert(sockfd!=-1); //判断套接字是否创建成功 若失败返回-1.

    【1】domain指定系统使用的哪个协议簇,如下图所示 【2】type参数指定的是服务类型,服务类型主要有SOCK_STREAM服务(流服务)SOCK_UGRAM(数据报)服务,对于TCP/IP协议簇来说,值为SOCK_STREAM时表示传输层使用TCP协议,值为SOCK_UGRAM时表示传输层使用UDP协议

    【3】protocol参数是指在前两个参数构成的协议集合下,再选一个具体的协议,不过这个值通常都是唯一的,因为前两个参数已经完全决定了它的值。几乎在所有情况下,他都应该被设置成0,表示使用默认的协议。

    返回值:select系统调用成功时返回一个文件描述符,失败时返回-1并且设置errno

    命名socket

    创建socket时我们给定了地址簇,但是并未指定该地址簇具体使用哪个socket地址。 将一个socket文件描述符与socket地址绑定成为给socket命名 在一个服务器程序中,我们通常要命名socket因为只有命名后客户端才能够知道如何连接它 而客户端一般不用命名socket,而是采取匿名方式即使用操作系统默认分配的socket地址。

    命名socket使用bind系统调用

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

    bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符,length参数表示该地址的长度

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

    监听socket

    socket被命名后还不能马上接受连接,因为他还需要一个监听队列用来存放待处理的客户端请求。

    #include<sys/socket.h> int listen(int sockfd,int backlog);

    sockfd指的是被监听的socket backlog表示内核监听队列能接受的最大的监听数目,如果监听队列的长度超过backlog,服务器就不会再接收新的客户端连接,客户端也将收到ECONNREFUSED错误信息 一般情况下把参数backlog的参数值设为5。

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

    接受连接

    accept()系统调用表示从listen的监听队列中接受一个连接

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

    sockfd参数是执行过系统调用listen的监听socket。 addr参数用来获取被接收连接的远端socket地址,该socket地址的长度由addrlen指出。 accept成功时返回一个新的连接socket,该socket唯一标识了被接受的这个连接。 服务器可以通过读写这个socket来与被接收连接对应的客户端进行通信。

    返回值:accept失败时返回-1并设置errno。

    发起连接

    如果说服务器通过listen系统调用来被动接受连接,那么connect()系统调用则是主动与服务器建立连接。

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

    sockfd由socket系统调用返回一个socket serv_addr参数是服务器监听的socket地址 addrlen指定这个地址的长度

    connect成功时返回0,一旦成功建立连接,sockfd就唯一标识了这个连接。 客户端就可以通过读写这个sockfd来与服务器通信。

    connect失败时返回-1并设置errno 其中两种常见的errno如下:

    ECONNREFUSED:目标端口不存在,连接被拒绝。ETIMEDOUT:连接超时。

    关闭连接

    关闭连接实际上就是关闭相对应的socket,可以通过如下的关闭普通文件描述符的系统调用来完成

    #include<unistd.h> int close(int sockfd);

    sockfd表示待关闭的socket。

    注意:close并非总是立即关闭一个socket连接,而是将sockfd的引用计数减一,只有当sockfd的引用计数为0时才会真正关闭这个连接,在多进程程序中,一次fork系统调用默认将父进程中打开的sockfd引用计数加一,因此我们必须在父进程和子进程中都对sockfd进行close()操作才可以完全的将连接关闭。

    如果无论如何都要中止这个sockfd的连接,而不单单是将他的引用计数减一,则可以使用shutdown系统调用(相对于close(),他就是专门为网络编程设计的)

    #include<sys/socket.h> int shutdown(int sockfd,int howto);

    其中sockfd表示待关闭的socket howto有三个可取的值,指明了shutdown的操作。如下:

    SHUT_RD 表示关闭读端,并且该socket的接收缓冲区中的数据都将被丢弃,程序不能再针对该sockfd文件描述符进行执行操作。SHUT_WR 表示关闭写端,sockfd的发送缓冲区中的数据会在真正关闭连接前全部发送出去 ,程序不能再针对该sockfd文件描述符进行执行操作。SHUT_RDWR 同时关闭sockfd上的读和写

    由此可见:close只能将socket的读和写同时关闭,而shutdown可以分边关上socket的读或者写或者都关上。 返回值:shutdown成功时返回0,失败时返回-1并设置errno。

    代码示例

    server.c

    #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<assert.h> #include<sys/socket.h> #include<arpa/inet.h> #include<netinet/in.h> int main() { int sockfd=socket(AF_INET,SOCK_STREAM,0); //创建套接字 assert(sockfd!=-1); //判断是否创建成功 struct sockaddr_in saddr,caddr; //创建套接字地址并初始化 memset(&saddr,0,sizeof(saddr)); saddr.sin_family=AF_INET; saddr.sin_port=htons(6000); saddr.sin_addr.s_addr=inet_addr("127.0.0.1"); int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr)); //给套接字命名 assert(res!=-1); listen(sockfd,5);//监听socket 最大监听数目为5 表示已经完成三次握手的队列长度 while(1) { int len=sizeof(caddr); int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//接受连接 sockfd为监听套接字 if(c<0) //c为链接套接字 { continue; } printf("accept c=%d,ip=%s,port=%d\n",c,inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port)); //输出连接信息 while(1) { char buff[128] = {0}; int n = recv(c,buff,127,0); if(n <= 0) { break; } printf("buff=%s\n",buff); send(c,"OK",2,0); } printf("one client over\n"); close(c); } return 0; }

    client.c

    #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<assert.h> #include<sys/socket.h> #include<arpa/inet.h> #include<netinet/in.h> int main() { int sockfd=socket(AF_INET,SOCK_STREAM,0); //创建套接字 assert(sockfd!=-1); struct sockaddr_in saddr,caddr; memset(&saddr,0,sizeof(saddr)); saddr.sin_family=AF_INET; saddr.sin_port=htons(6000); saddr.sin_addr.s_addr=inet_addr("127.0.0.1"); int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr)); //发起连接 assert(res!=-1); //判断是否连接成功 while(1) { char buff[128] = {0}; printf("Please Input:"); fgets(buff,128,stdin); if(strncmp(buff,"end",3) == 0 ) { break; } send(sockfd,buff,strlen(buff),0); memset(buff,0,128); recv(sockfd,buff,127,0); printf("buff[%d]:%s\n",n,buff); printf("\n"); } close(sockfd); //关闭文件描述符 exit(0); }

    运行结果如下:

    最新回复(0)