socket编程

6 阅读5分钟

一、socket编程常用函数

1.socket函数

该函数的作用是创建一个套接字,相当于安装一个电话机

#include <sys/socket.h>
int socket(int domain, int type, int protocol);
//成功时返回文件描述符,失败时返回-1
  • domain参数

    • AF_INET, 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址
    • AF_INET6, 与上面类似,不过是来用IPv6的地址
    • AF_UNIX, 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用
  • type参数

    • SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
    • SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
    • SOCK_RAW socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)
    • SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序
  • protocol参数

    • 传0 表示使用默认协议。

2.bind函数

#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
//成功时返回0,失败时返回-1
  • sockfd参数
    • socket文件描述符
  • myaddr参数
    • 构造出IP地址加端口号
  • addrlen参数
    • sizeof(myaddr)长度

3.listen函数

#include <sys/socket.h>
int listen(int sockfd, int backlog);
//成功时返回0,失败时返回-1
  • sockfd参数
    • socket文件描述符
  • backlog参数
    • 连接请求等待队列的长度,若为5,则队列长度为5,表示最多使5个连接请求进入队列

4. accept 函数

#include <sys/socket.h>
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
//成功时返回文件描述符,失败时返回-1
  • sockfd参数
    • 服务端套接字的文件描述符
  • addr参数
    • 保存发起连接请求的客户端地址信息的变量地址值
  • addrlen参数
    • 的第二个参数addr结构体的长度,但是存放有长度的变量地址。

5.connect函数

#include <sys/socket.h>
int connect(int sock, struct sockaddr *servaddr, socklen_t addrlen);
/*
成功时返回0,失败返回-1
*/
  • sock参数
    • 客户端套接字文件描述符
  • servaddr参数
    • 保存目标服务器端地址信息的变量地址值
  • addrlen参数
    • 以字节为单位传递给第二个结构体参数 servaddr 的变量地址长度

6.write函数

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t nbytes);
/*
成功时返回写入的字节数 ,失败时返回 -1
*/
  • fd参数
    • 显示数据传输对象的文件描述符
  • buf参数
    • 保存要传输数据的缓冲值地址
  • nbytes参数
    • 要传输数据的字节数

7.read函数

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
/*
成功时返回接收的字节数(但遇到文件结尾则返回0),失败时返回-1
*/
  • fd参数
    • 显示数据接收对象的文件描述符
  • buf参数
    • 要保存接收的数据的缓冲地址值。
  • nbytes参数
    • 要接收数据的最大字节数

二、echo服务器示例

socket模型创建流程图

socket模型创建过程转存失败,建议直接上传图片文件

1、服务器端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> //fork,read,write等系统调用
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUFF_SIZE 1024
void error_handling(char *message);

int main(int argc, char *argv[]) {
    

    if (argc!=2) {
        printf("Usage: %s <port>\n", argv[0]);
        exit(1);
    }
    
    // 1. 创建监听套接字
    int serv_sock = socket(PF_INET,SOCK_STREAM,0);// 协议族(IPV4,IPV6),套接字类型(面向连接,面向消息),协议信息(TCP,UDP)
    // 2.将socket()返回值和本地的IP端口绑到一起
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[1])); // 端口号10000,转化为网络字节序,网络传输统一使用大端序
    // INADDR_ANY代表本机的所有IP, 假设有三个网卡就有三个IP地址
    // 这个宏可以代表任意一个IP地址
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(serv_sock, (struct sokaddr*) &addr, sizeof(addr)) == -1)
        error_handling("bind() error");

    // 3.设置监听
    if (listen(serv_sock, 5) == -1)
        error_handling("linsten() error");
   
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size = sizeof(clnt_addr);
    
    while(1) {
        // 4. 阻塞并等等待客户端连接
        int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
        if (clnt_sock == -1) 
            error_handling("accpet() error");

        // 5.和客户端通信
        // 接收数据
        char buf[1024];
        int str_len;
        memset(buf, 0, sizeof(buf));
        while ((str_len = read(clnt_sock, buf, BUFF_SIZE)) != 0)
            write(clnt_sock, buf, str_len);
        
        close(clnt_sock);
    }
    
    close(serv_sock);
    return 0;
}

void error_handling(char *message) {
    fputs(message, stderr);
    fputs("\n", stderr);
    exit(1);
}

二、客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> //fork,read,write等系统调用
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUFF_SIZE 1024
void error_handling(char *message);

int main(int argc, char *argv[]) {

    int sock;
    struct sockaddr_in serv_addr;
    char message[BUFF_SIZE];
    int str_len;
    if (argc!=2) {
        printf("Usage: %s <IP> <port>\n", argv[0]);
        exit(1);
    }
    // 1.创建客户端套接字
    sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        error_handling("socket() error");
    }

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 将IP地址字符串转化为32位整数类型
    serv_addr.sin_port = htons(atoi(argv[1]));
    // 2.向服务器端发送连接请求
    if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) 
        error_handling("connect() error");
    // 3. 与服务端通信
    while (1) {
        char buf[1024];
        printf("输入要发送的内容,按q退出:");
        fgets(buf, BUFF_SIZE, stdin);
        if (buf[0] == 'q') break;
        str_len=write(sock, buf, sizeof(buf));
        int recv_len = 0;
        while (recv_len<str_len) {
            int cnt_len = read(sock, buf, BUFF_SIZE - 1);
            if (cnt_len == -1)
                error_handling("read() error");
            recv_len += cnt_len;
        }
        
        buf[str_len]=0;
        printf("Message form server:%s\n", buf);
        close(sock);
    }
    
    return 0;
}

void error_handling(char *message) {
    fputs(message, stderr);
    fputs("\n", stderr);
    exit(1);
}