文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Linux网络编程:Socket套接字编程(Server服务器 Client客户端)

2023-09-04 22:19

关注

文章目录:

一:定义和流程分析

1.定义

2.流程分析 

3.网络字节序

二:相关函数 

IP地址转换函数inet_pton inet_ntop(本地字节序 网络字节序)

socket函数(创建一个套接字)

bind函数(给socket绑定一个服务器地址结构(IP+port))

listen函数(设置最大连接数或者说能同时进行三次握手的最大连接数监听上限)

accept函数(阻塞监听等待客户端建立连接, 成功的话返回一个与客户端成功连接的socket文件描述符)

connect函数(使用现有的socket与服务器建立连接)

三:服务器模型和客户端模型的实现 

Server服务器的实现

Client客户端的实现


1.定义

定义:一个文件描述符指向一个套接字(该套接字内部由内核借助两个缓冲区实现)         在通信过程中, 套接字一定是成对出现的        一种文件类型,伪文件,不占用存储空间,可进行IO操作,可间接看做文件描述符使        Socket本身有“插座”的意思        在Linux环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件    管道, 套接字, 块设备, 字符设备;    套接字: 一个fd可以索引读写两个缓冲区;套接字的作用    套接字是网络通信中的一种端点,它提供了应用层进程利用网络协议交换数据的机制    套接字可以用于不同主机上的应用进程之间进行双向通信,使得它们可以交换数据、同步连接状态、处理错误等    在计算机系统中,套接字通常被实现为文件描述符,用于在网络上进行数据传输    当应用程序打开一个套接字时,操作系统会为它分配一个唯一的文件描述符,以便于进程间通信

2.流程分析 

 

socket():创建一个套接字, 用fd索引bind():绑定IP和portlisten():设置监听上限(同时与Server建立连接数)accpet():阻塞监听客户端连接(传入一个上面创建的套接字, 传出一个连接的套接字)在客户端中的connect()中绑定IP和port,并建立连接(阻塞)

3.网络字节序

小端法:(pc本地存储)高位存高地址。低位存低地址。int a = 0x12345678大端法:(网络存储)高位存低地址。低位存高地址。htonl --> 本地--》 网络 (IP)192.168.1.11 --> string --> atoi --> int --> htonl --> 网络字节序htons --> 本地--》 网络 (port)ntohl --> 网络--》 本地(IP)ntohs --> 网络--》 本地(Port)

用库函数做网络字节序和主机字节序的转换

#includeuint32_t htonl(uint32_t hostlong);//主要针对IPuint16_t htons(uint16_t hostshort);//主要针对portuint32_t ntohl(uint32_t netlong);uint16_t ntohs(uint16_t netshort);

IP地址转换函数inet_pton inet_ntop(本地字节序 网络字节序)

由于如192.168.45.2这种的IP地址为点分十进制表示,需要转化为uint32_t型,有现成的函数(IPv4和IPv6都可以转换) 

//本地字节序(string IP) ---> 网络字节序int inet_pton(int af, const char *src, void *dst);                   af:AF_INET、AF_INET6src:传入,IP地址(点分十进制)dst:传出,转换后的 网络字节序的 IP地址。 返回值:成功: 1异常: 0, 说明src指向的不是一个有效的ip地址。失败:-1//网络字节序 ---> 本地字节序(string IP)const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);af:AF_INET、AF_INET6src: 网络字节序IP地址dst:本地字节序(string IP)size: dst 的大小。返回值: 成功:dst、失败:NULL

socket函数(创建一个套接字)

#include int socket(int domain, int type, int protocol);创建一个 套接字    domain指定使用的协议(IPv4或IPv6)    AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址    AF_INET6 与上面类似,不过是来用IPv6的地址    AF_UNIX 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用    type指定数据传输协议(流式或报式)        SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。    SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。    SOCK_SEQPACKET该协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。    SOCK_RAW socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)    SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序    指定代表协议(一般默认传0)protocol: 0         流式以TCP为代表;        报式以UDP为代表;    返回值:返回指向新创建的socket的文件描述符        成功:返回新套接字所对应文件描述符fd        失败:返回-1并设置errno;

bind函数(给socket绑定一个服务器地址结构(IP+port))

#include  int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);给socket绑定一个 地址结构 (IP+port)sockfd: socket文件描述符struct sockaddr_in servaddr;addr.sin_family = AF_INET;addr.sin_port = htons(8888);addr.sin_addr.s_addr = htonl(INADDR_ANY);    addr: 构造出IP地址加端口号        传入参数(struct sockaddr *)&addr    addrlen: sizeof(addr) 地址结构的大小返回值:成功:0失败:返回-1, 设置errno

listen函数(设置最大连接数或者说能同时进行三次握手的最大连接数监听上限)

#include  #include int listen(int sockfd, int backlog);        //设置同时与服务器建立连接的上限数(同时进行3次握手的客户端数量)    sockfd:        socket文件描述符    backlog:上限数值。最大值 128    排队建立3次握手队列和刚刚建立3次握手队列的链接数和    返回值:成功:0失败:-1 errno

accept函数(阻塞监听等待客户端建立连接, 成功的话返回一个与客户端成功连接的socket文件描述符)

#include  #include int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);    sockfd:    socket文件描述符    addr:成功与Sever建立连接的那个**客户端**的地址结构;    传出参数,返回链接客户端地址信息(IP地址+端口号)    addrlen:传入传出参数(值-结果),传入sizeof(addr)大小,函数返回时返回真正接收到地址结构体的大小    ​    socklen_t clit_addr_len=sizeof(addr);​    入: 传入addr的大小;​    出: 客户端addr的实际大小;    返回值:    ​    成功: 返回能与客户端进行通信的socket对应的文件描述符;​    失败: 返回-1并设置errno;//我们的服务器程序结构是这样的while (1) {cliaddr_len = sizeof(cliaddr);connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);n = read(connfd, buf, MAXLINE);......close(connfd);}

connect函数(使用现有的socket与服务器建立连接)

#include  #include int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);    sockdf:socket文件描述符    struct sockaddr_in srv_addr;// 服务器地址结构srv_addr.sin_family = AF_INET;srv_addr.sin_port = 9527 跟服务器bind时设定的 port 完全一致。inet_pton(AF_INET, "服务器的IP地址",&srv_adrr.sin_addr.s_addr);        addr:    传入参数,指定服务器端地址信息,含IP地址和端口号    addrlen:    传入参数,服务器地址结构的长度sizeof(addr)大小    返回值:   ​成功返回0;​    失败返回-1并设置errno;如果不使用`bind()`函数绑定客户端的地址结构, 会采用**"隐式绑定"**;

Server服务器的实现

server:1. socket()创建socket2. bind()绑定服务器地址结构3. listen()设置监听上限4. accept()阻塞监听客户端连接5. read(fd)读socket获取客户端数据6. 小--大写toupper()7. write(fd)8. close();

代码逻辑

#include #include #include #include #include #include #include #include #include  #define SERV_PORT 9527//端口号 int main(int argc, char *argv[]){    int link_fd=0;//建立连接的socket文件描述符    int connect_fd=0//用于通信的文件描述符    int ret=0;//用于检查是否出错    char buf[BUFSIZ];//缓冲区    char client_IP[1024]//存入客户端IP字符串    int num=0;//读出的字节数        struct sockaddr_in serv_addr;                    // 定义服务器地址结构 和 客户端地址结构serv_addr.sin_family=AF_INET;                    // IPv4serv_addr.sin_port=htons(SERV_PORT);             // 转为网络字节序的 端口号serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);     // 获取本机任意有效IP            struct sockaddr_in clint_addr;    socklen_t clint_addr_len=sizeof(clint_addr);     // 获取客户端地址结构大小               //IPv4,按照顺序基于字节流的连接,指定代表协议link_fd=socket(AF_INET,SOCK_STREAM,0);if(link_fd==-1)sys_err("socket error");        //socket文件描述符link_fd,IP地址加端口号ret=bind(link_fd,(const struct sockaddr*)&serv_addr,sizeof(serv_addr));if(ret==-1)sys_err("bind error");            ret=listen(link_fd,128);     if(ret==-1)        sys_err("listen error");        //文件描述符,与Sever建立连接的客户端的地址结构,返回真正接收到地址结构体的大小connect_fd=accept(link_fd,(struct sockaddr*)&clint_addr,&clint_addr_len);    if(connect_fd==-1)sys_err("accept error");             printf("client IP:%s,client port:%d",  //`client_IP`是前面定义的客户端IP字符串的缓冲区, 大小为1024           inet_ntop(AF_INET,&clint_addr.sin_addr.s_addr,client_IP,sizeof(client_IP)),//网络字节序 ---> 本地字节序ntohs(clint_addr.sin_port)//根据accept传出参数,获取客户端 ip 和 port  );                       while(1){        //5. read(fd)读socket获取客户端数据num=read(connect_fd,buf,sizeof(buf));    // 读客户端数据write(STDOUT_FILENO,buf,num);            // 写到屏幕查看         //6. 小--大写toupper()for(i=0;i

测试命令 

`nc 127.0.0.1 9527`        //脑残命令: 向这个服务发送信息并打印回执

Client客户端的实现

client:1. socket()创建socket2. connect();与服务器建立连接3. write()写数据到 socket4. read()读转换后的数据5. 显示读取结果6. close()

代码逻辑

#include #include #include #include #include #include #include #include  #define SERV_PORT 9527 void sys_err(const char* str){perror(str);exit(1);} int main(int argc, char *argv[])){int client_fd=0;int ret=0;int num=0;int cnt=10;char buf[BUFSIZ]; //connect的参数2填入服务器的文件描述符!struct sockaddr_in serv_addr;serv_addr.sin_family=AF_INET;serv_addr.sin_port=htons(SERV_PORT);    // 本地字节序(string IP) ---> 网络字节序inet_pton(AF_INET,"127.0.0.1",(void*)&serv_addr.sin_addr.s_addr);       client_fd=socket(AF_INET,SOCK_STREAM,0);if(client_fd==-1)sys_err("socket error");     ret=connect(client_fd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));if(ret!=0)sys_err("connect error");    //业务逻辑while(--cnt){        //3. write()写数据到 socketwrite(client_fd,"fuckyou\n",8);        //4. read()读转换后的数据。num=read(client_fd,buf,sizeof(buf));         //5. 显示读取结果write(STDOUT_FILENO,buf,num);sleep(1);}     //6. close()close(client_fd); return 0;}

来源地址:https://blog.csdn.net/liu17234050/article/details/132371881

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-人工智能
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯