(三)Linux网络编程之socket套接字

socket

socket编程

什么是socket?

  • 网络通信的函数接口
  • 封装了传输层协议
    tcp
    udp

使用浏览器 - http协议

  • 封装的是tcp

1. 套接字概念

在这里插入图片描述
socket通信
服务器端 - 被动
客户端 - 主动

socket编程 - 网络IO编程

  • 读写操作
  • read/write
    文件描述符
  • 创建一个套接字,得到的是文件描述符

套接字:

  • 创建成功,得到一个文件描述符fd
  • fd操作的是一块内存缓冲区

阻塞是打开的文件描述符对应的设备文件的性质,而非函数(read、write)的性质
【如终端、管道】


2、IP地址转换函数

a.指定IP - 字符串(点分十进制)

本地IP转网络字节序 字符串->int(大端方式存储)

#include <arpa/inet.h> //IP地址转换函数
int inet_pton(int af,const char *src,void *dst);

参数:

  • af:地址族协议对应的有AF_INET,AF_INET6等
  • src:要转换的指定的IP
    点分十进制的IP
  • dest:要转换出来的值,其实是整型值

网络字节序转本地IP int->字符串

const char *inet_ntop(int af,const void *src,char *dst,socklen_t size);

参数:

  • af:地址族协议对应的有AF_INET,AF_INET6等
  • src:网路字节序格式的int类型的iP
    点分十进制的IP
  • dest:存储字符串ip的数组的地址
  • size:dst缓冲区大小

3、函数

文件(内核的缓冲区)操作

socket tcp server

服务器端:

  • 创建套接字
int lfd=socket(...);//监听是否有连接到服务器,不是用于通信
  • 绑定本地IP和端口
struct sockaddr_in serv;
serv.port=htons(port);
serv.IP=htonl(INADDR_ANY);
bind(lfd,&serv,sizeof(serv));
  • 监听
listen(lfd,128);//128为同时能监听到的最大连接个数
  • 等待并接收连接请求
struct sockaddr_in client;//客户端的IP和端口
int len=sizeof(client);
int cfd=accept(lfd,&client,&len);
//cfd - 用于通信的
  • 通信

接受数据:read/recv

发送数据:write/send

  • 关闭
close(lfd);
close(cfd);

客户端:

  • 创建套接字
int fd=socket(...);
  • 连接服务器
struct sockaddr_in server;
server.port
server.ip=(int)???
server.family
connect(fd,&server,sizeof(server));
  • 通信

接受数据:read/recv

发送数据:write/send

  • 断开连接
close(fd);

在这里插入图片描述

// tcp_server.c
#include <stdio.h>
#include <ctype.h>//小写转大写
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>

int main(int argc, const char* argv[]) {
    // 创建监听的套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);

    // 绑定
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(8080); //没有使用的端口
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地所有的IP
    // 另一种写法, 假如是127.0.0.1
    // inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr);
    bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

    // 监听
    listen(lfd, 64);

    // 阻塞等待连接请求, 并接受连接请求
    struct sockaddr_in clien_addr;
    socklen_t clien_len = sizeof(clien_addr);
    int cfd = accept(lfd, (struct sockaddr*)&clien_addr, &clien_len);

    char ipbuf[128];
    printf("client iP: %s, port: %d\n", inet_ntop(AF_INET, &clien_addr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)),
           ntohs(clien_addr.sin_port));

    char buf[1024] = {0};
    while(1) {
        // read data, 阻塞读取
        int len = read(cfd, buf, sizeof(buf));
        printf("read buf = %s\n", buf);
        // 小写转大写
        for(int i=0; i<len; ++i) {
            buf[i] = toupper(buf[i]);
        }
        printf("after buf = %s", buf);

        // 大写串发给客户端
        write(cfd, buf, strlen(buf)+1);
    }

    close(cfd);
    close(lfd);

    return 0;
}

//client_tcp.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
int main(int argc, const char *argv[]) {

    // create
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        perror("socket error");
        exit(-1);
    }

    //connect
    struct sockaddr_in c_addr;
    bzero(&c_addr, sizeof(c_addr));
    c_addr.sin_family = AF_INET;
    c_addr.sin_port = htons(8080);
    inet_pton(AF_INET, "127.0.0.1", &c_addr.sin_addr.s_addr);

    int ret = connect(fd, (struct sockaddr*)&c_addr, sizeof(c_addr));
    if (ret == -1) {
        perror("connect error");
        exit(-1);
    }

    while(1) {
        printf("请输入字符串:\n");
        char buf[1024] = {0};
      fgets(buf, sizeof(buf), stdin);
        write(fd, buf, strlen(buf));
        //接收, 阻塞等待
        int len = read(fd, buf, sizeof(buf));
        if (len == -1) {
             perror("read error");
             exit(-1);
        }
        printf("client recv %s\n", buf);

    }

    close(fd);
     return 0;
}


4、socket函数封装
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <errno.h>

//错误输出函数
void perr_exit(const char *s)
{
        perror(s);
        exit(-1);
}

//参数和accept一样,名字也相似
//主要做错误信息的打印
int Accept(int fd,struct sockaddr *sa,socklen_t salenptr)
{
        int n;

again:
        if((n=accept(fd,sa,salenptr))<0)
        {
                //ECONNABORTED 发生在重传(一定次数)失败后,强制关闭套接字
                //EINTR 进程被信号中断(阻塞过程中)
                if((errno==ECONNABORTED)||(errno==EINTER))
                {
                        goto again;
                }
                else{
                        perr_exit("accept error");
                }
        }
        return n;
}

int Bind(int fd,const struct sockaddr *sa,socklen_t salen)
{
        int n;
        if((n=bind(fd,sa,salenptr))<0)
        {
                perr_exit("bind error");
        }
        return n;
}

int Connect(int fd,const struct sockaddr *sa,socklen_t salen)
{
        int n;
        if((n=connect(fd,sa,salenptr))<0)
        {
                perr_exit("connect error");
        }
        return n;
}

int Listen(int fd,int backllog)
{
        int n;
        if((n=listen(fd,backlog))<0)
        {
                perr_exit("listen error");
        }
        return n;
}

int Socket(int family,int type,int protocol)
{
        int n;
        if((n=socket(family,type,protocol))<0)
        {
                perr_exit("socket error");
        }
        return n;
}

ssize_t Read(int fd,void *ptr,size_t nbytes)
{
        ssize_t n;
again:
        if((n=read(fd,ptr,nbytes))==-1)
        {
                if(errno==EINTER)
                        goto again;
                else
                        return -1;
        }
        return n;
}

ssize_t Write(int fd,const void *ptr,size_t nbytes)
{
        ssize_t n;
again:
        if((n=write(fd,ptr,nbytes))==-1)
        {
                if(errno==EINTER)
                        goto again;
                else
                        return -1;
        }
        return n;
}

int Close(int fd)
{
        int n;
        if((n=close(fd))==-1)
                perr_exit("close error");

        return n;
}

ssize_t Readn(int fd,void *vptr,size_t n)
{
        char *ptr;
        size_t nleft;  //unsigned int 剩余被读取的字节数
        ssize_t nread; //int 实际读到的数据

        ptr=vptr;
        nleft=n;//未读的字节数

        while(nleft>0)
        {
                if((nread=read(fd,ptr,nleft))<0)
                {
                        if(errno==EINTER)
                                nread=0;
                        else
                                return -1;
                }
                else if(nread==0)
                        break;
                nleft-=nread;
                ptr+=nread;
        }
        return n-nleft;
}

ssize_t Writen(int fd,void *vptr,size_t n)
{
        const char *ptr;
        size_t nleft;
        ssize_t nwritten;

        ptr=vptr;
        nleft=n;

        while(nleft>0)
        {
                if((nwritten=write(fd,ptr,nleft))<=0)
                {
                        if(nwrite<0&&errno==EINTER)
                                nread=0;
                        else
                                return -1;
                }
                nleft-=nwritten;
                ptr+=nwritten;
        }
        return n;
}

static ssize_t my_read(int fd,char*ptr)
{
        static int read_cnt;
        static char *read_ptr;
        static char read_buf[100];

        if(read_cnt<=0)
        {
again:
                if((read_cnt=read(fd,read_buf,sizeof(read_buf)))<0)
                {
                        if(errno==EINTER)
                                goto again;
                        return -1;
                }
                else if(read_cnt==0)
                        return 0;
                read_ptr=read_buf;
        }
        read_cnt--;
        *ptr=*read_ptr++;

        return 1;
}

ssize_t ReadLine(int fd,void *vptr,size_t maxlen)
{
        ssize_t n,rc;
        char c,*ptr;
        ptr=vptr;

        for(n=1;n<maxlen;n++)
        {
                if((rc=my_read(fd,&c))==1)
                {
                        *ptr++=c;
                        if(c=='\n')
                                break;
                }
                else if(rc==0)
                {
                        *ptr=0;
                        return n-1;
                }
                else
                        return -1;
        }
        *ptr=0;
        return n;
}

补充:
https://www.cnblogs.com/wangcq/p/3520400.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值