Linux下网络编程之socket

网络编程:编写程序使两台联网的计算机相互交换数据。

socket:被翻译为套接字,它是计算机之间约定好的一种通信方式,是用来连接到因特网的工具。典型的应用:浏览器(客户端)获取用户输入的 URL,向服务器发起请求,服务器分析接收到的 URL,将对应的网页内容返回给浏览器(客户端),浏览器再经过解析和渲染,就将文字、图片、视频等元素呈现给用户。

文件描述符:在Linux中一切都是文件,为了区分打开的文件,Linux都会给每个文件分配一个id整数,这个id就是文件描述符,而在Linux中,网络连接也是一个文件,它也有文件描述符,当我们在创建socket()时,会返回一个文件描述符,有了这个文件符我们就可以像普通文件一样write()、read()等文件操作。

socket有很多种数据传输方式,这里介绍基本的两种方式:

  • 流格式套接字(Stream Sockets)就是“面向连接的套接字”,它基于 TCP 协议,它是一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送。
  • 数据报格式套接字(Datagram Sockets)就是“无连接的套接字”,它基于 UDP 协议,它是数据报套接字是一种不可靠的、不按顺序传递的、以追求速度为目的的套接字。

编写客户端一般基本流程:

  • 创建套接字socket()
  • 向服务器发起请求,连接服务器connect()
  • 接受或者向服务器发送信息write()/read()
  • 关闭套接字close()

编写服务器一般基本流程:

  • 创建套接字socket()
  • 绑定套接字bind()
  • 监听listen()
  • 接受客户端的请求accept()
  • 接受或者向客户端发送信息write()/read()
  • 关闭套接字close()

int socket(int af, int type, int protocol);   //创建,返回一个int类型的文件描述符,失败返回-1

  • af:ip地址类型, 常用的有AF_INETAF_INET6,表示ipv4或者ipv6类型
  • type:数据传输类型,SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)
  • protocol: IPPROTO_TCP 和 IPPTOTO_UDP,表示tcp方式或者udp方式,有时填0,让系统根据type类型自动选择。

int bind(int sock, struct sockaddr *addr, socklen_t addrlen);   //绑定,

  • sock:创建socket()时的文件描述符
  • addr:sockaddr 结构体变量的指针,存放IP地址类型、IP地址、端口号等信息
  • addrlen:sockaddr结构体的大小,可由sizeof()得到该值

int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);  //连接服务器

  • sock:创建socket()时的文件描述符
  • addr:sockaddr 结构体变量的指针,存放IP地址类型、IP地址、端口号等信息
  • addrle:sockaddr结构体的大小,可由sizeof()得到该值

int listen(int sock, int backlog);  //监听

  • sock:创建socket()时的文件描述符
  • backlog:请求队列的最大长度,可以根据你的需求来定,并发量小的话可以是10或者20,如果设置为 SOMAXCONN,就由系统来决定请求队列长度,这个值一般比较大,可能是几百,或者更多。

int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);   //接收客户端,返回一个新的文件描述符来和客户端通信

  • sock:创建socket()时的文件描述符
  • addr:sockaddr 结构体变量的指针,存放IP地址类型、IP地址、端口号等信息
  • addrle:sockaddr结构体的大小,可由sizeof()得到该值

ssize_t write(int fd, const void *buf, size_t nbytes);   //写入数据

  • fd:创建socket()时的文件描述符,或者accept()的文件描述符
  • buf:为要写入的数据的缓冲区地址
  • nbytes:要写入的数据的字节数

ssize_t read(int fd, void *buf, size_t nbytes);   //读出数据

  • fd:创建socket()时的文件描述符,或者accept()的文件描述符
  • buf:为要写入的数据的缓冲区地址
  • nbytes:要写入的数据的字节数

实现客户端和服务器端的demo:

-----------------------------------客户端------------------------------------
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

int main(int argc,char *argv[])
{
    int sock = 0;
    char buff[64] = {0};
    char str[] = "hello,is me client";
    struct sockaddr_in serv_addr;
   
    sock = socket(AF_INET, SOCK_STREAM, 0); //创建套接字
    
    memset(&serv_addr, 0, sizeof(serv_addr)); 
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址,127.0.0.1一般指的是本机地址
    //端口,Web 服务的端口号是 80,FTP 服务的端口号是 21,SMTP 服务的端口号是 25,我们的程序要尽量在 1024~65536 之间分配端口号
    serv_addr.sin_port = htons(2048); 

    connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));  //连接服务器

    read(sock, buff, sizeof(buff));
    printf("Message form server: %s\n", buff);

    write(sock, str, sizeof(str));
   
    close(sock); //关闭套接字
    return 0;
}


-----------------------------------服务器端--------------------------------------
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(int argc,char *argv[])
{
    int serv_sock = 0;  //创建套字的文件描述符
    int clnt_sock = 0; //接收客户端请求的文件描述符
    char buff[64];
    char str[] = "hello,is me server";
    struct sockaddr_in serv_addr;
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size = sizeof(clnt_addr);

    serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  //创建套接字
       
    memset(&serv_addr, 0, sizeof(serv_addr));  
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址,127.0.0.1一般指的是本机地址
    //端口,Web 服务的端口号是 80,FTP 服务的端口号是 21,SMTP 服务的端口号是 25,我们的程序要尽量在 1024~65536 之间分配端口号
    serv_addr.sin_port = htons(2048);  
    bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); //将套接字和IP、端口绑定
   
    listen(serv_sock, 20); //进入监听状态,等待用户发起请求
      
    //接收客户端请求,会阻塞程序执行(后面代码不能被执行),直到有新的请求到来
    clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size); 
  
    write(clnt_sock, str, sizeof(str));
   
    read(clnt_sock, buff, sizeof(buff));
    printf("Message form client: %s\n", buff);
    //关闭套接字
    close(clnt_sock);
    close(serv_sock);
    return 0;
}

程序的执行结果如下:

猜你喜欢

转载自blog.csdn.net/weixin_42432281/article/details/88189146
今日推荐