我正在参加「掘金·启航计划」。

1. socket编程的基本概念

socket就是插座,运行在计算机中的两个程序通过socket建立起一个通道,数据在通道中传输。 socket将复杂的TCP/IP协议族隐藏了起来,我们用好socket相关函数就可完成网络通信。

分为 流socket 基于TCP协议的 数据报socket 基于UDP协议的 两种。

2.简单的socket通信流程

程序部分要用到的头文件:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
//添加了这个头文件就不需要sys/socket那个了
#include <arpa/inet.h> 

step1:创建流式socket,socket()

    /*创建监听的套接字,一般都是用tcp的*/
    int fd = socket(AF_INET,SOCK_STREAM,0);
    //判断是否创建成功,创建不成功打印错误信息 
    if(fd == -1){
        perror("socket 创建失败");
        return -1;

step2:指定用于通信的ip和端口bind()

    /*将套接字与本地ip、端口进行绑定 */
    struct sockaddr_in saddr; //因为这个结构体初始化简单
    //初始化saddr,三个成员
    //sa_family_t sin_family;  地址族协议: AF_INET		
    //in_port_t sin_port;         端口, 2字节-> 大端
    //struct in_addr sin_addr;  IP地址, 4字节 -> 大端 
    saddr.sin_family = AF_INET; // IPv4
    saddr.sin_port = htons(9999); // 端口
    saddr.sin_addr.s_addr = INADDR_ANY; //0.0.0.0;可以绑定本地任意ip地址
    //绑定 绑定失败会返回-1,若绑定失败要打印错误信息
    int ret = bind(fd, (struct sockaddr*)&saddr, sizeof(saddr));
    if(ret == -1){
        perror("socket绑定失败");
        return -1;

step3:把服务器设为监听模式listen()

    ret = listen(fd, 128);
        if(ret == -1){
        perror("监听失败");
        return -1;

step4:接受客户端端连接accept()

    /*阻塞并等待客户端的连接*/
    struct sockaddr_in caddr; //方便取数据
    int addrlen = sizeof(caddr);
    int cfd = accept(fd,(struct sockaddr*)&caddr, &addrlen);
    if(cfd == -1){
        perror("accept失败");
        return -1;

step5:接收/发送数据,recv()/send()

    /*建立连接成功后,打印客户端ip和端口信息*/
    //保存到caddr中的ip和端口都是大端到,我们需要先转成小端再输出
    char ip[32];
    printf("客户端的IP: %s, 端口: %d\n",inet_ntop(AF_INET,&caddr.sin_addr.s_addr,ip,sizeof(ip)),ntohs(caddr.sin_port));
    /* 5.通信*/
    while (1)
        //接收数据
        char buff[1024];
        int len = recv(cfd,buff,sizeof(buff),0);//flags: 特殊的属性,一般不使用,指定为 0
        if(len > 0){
            printf("client say: %s\n", buff);
            send(cfd, buff, len, 0);
        }else if(len == 0){
            printf("客户端已断开连接...\n");
            break;
        }else{
            //到这里说明函数调用失败
            perror("recv error");
            break;

step6:关闭socket连接,释放资源close()

    //关闭文件描述符
    close(fd); //用于监听的
    close(cfd); //用于通信的

step1:创建流式socket,socket()这里与服务端一样,int fd = socket(AF_INET,SOCK_STREAM,0);不再赘述了。
step2:向服务器发起连接请求,connect()

    /*连接服务器的ip和port */
    struct sockaddr_in saddr; //因为这个结构体初始化简单
    //初始化saddr,三个成员
    //   sa_family_t sin_family;  地址族协议: AF_INET		
    //   in_port_t sin_port;         端口, 2字节-> 大端
    //   struct in_addr sin_addr;  IP地址, 4字节 -> 大端  
    saddr.sin_family = AF_INET; // IPv4
    saddr.sin_port = htons(9999); // 端口,服务器绑定的9999端口那客户端连接的话也要连接到这个端口
    //需要将本机ip地址转成一个大端地址, //客户端要指定一个ip地址了
    inet_pton(AF_INET,"172.18.45.233",&saddr.sin_addr.s_addr); //将ip存到saddr
    // saddr.sin_addr.s_addr = INADDR_ANY; //客户端要指定一个ip地址了
    //绑定 绑定失败会返回-1,若绑定失败要打印错误信息
    int ret = connect(fd, (struct sockaddr*)&saddr, sizeof(saddr));
    if(ret == -1){
        perror("socket连接失败");
        return -1;

step3:发送/接收数据,send()/recv()

 /*建立连接成功后,直接进行通信*/
    /*进行套接字通信*/
    int number = 0;
    while (1)
        /*先发数据再接数据*/
        //发送数据
        char buff[1024]; //数据先初始化到buff
        sprintf(buff, "你好,hello, world, %d..\n",number++);
        send(fd, buff, strlen(buff)+1, 0);
        //接收数据
        memset(buff,0,sizeof(buff));//先清空buff中的数据
        int len = recv(fd, buff,sizeof(buff),0);//flags: 特殊的属性,一般不使用,指定为 0
        if(len > 0){
            printf("server say: %s\n", buff);
        }else if(len == 0){
            printf("服务器已断开连接...\n");
            break;
        }else{
            //到这里说明函数调用失败
            perror("recv error");
            break;
        sleep(1); //这里只是让客户端发数据发的慢一些,每1s发一次

step4:关闭socket连接,释放资源close()

    //关闭文件描述符
    close(fd); //用于监听的
  •