Linux网络编程——socket、bind、listen、accpet、connect、read和write
Linux网络编程
- 基础理论
- 1、TCP/UDP/端口号
- 2、字节序
- 一、socket服务器与客户端的开发步骤
- 二、具体使用步骤
- 1.socket(创建连接协议)
- 2.bind(地址准备好)
- 3.listen(监听)
- 4.accept(连接)
- 5.read/write/send/recv(数据收发)
- 6.connect
- 三、例子
- 1.服务端——固定IP地址连接
- 2.客服端——固定IP地址连接
- 2.服务端——自由指定地址
- 2.客户端——自由指定地址
- 总结
基础理论
1、TCP/UDP/端口号
管道、消息队列、共享内存、信号、信号量五种进程间通信属于单机通信。网络编程是由地址和数据组成的。地址是IP地址和端口号组成。数据传输需要遵循协议(HTTP、TCP、UDP)。通过端口号对客户端访问,有FTP、HTTP、SOCKET多种服务。
Linux的socket套接字所用协议最多的是TCP/UDP。
TCP:面向连接(举例:A给B打电话,先拨号再接通);
UDP:面向报文(A给B发信息,不关心是否接通成功);
二者比较:TCP应用于数据传输要求精确的场景;UDP应用于大数据传输;
端口号作用:一台拥有IP地址的主机可以提供许多服务。比如:web、FTP、SMTP(电子邮件传输)服务;实际上,通过”IP地址+端口号“来区分不同服务。
举例:
服务器、TCP/UDP、IP地址、端口号的关系
我是xxx(服务器)
是说汉语(TCP/UDP)
我的楼号(IP地址)是xxxx
我的房间号(端口号)是xxxx
我在等着大家来访,敲门(监听)
客户端
获取服务器IP,再获取服务器端口号连接。
2、字节序
字节序是指多字节在计算机存储或者网络传输各字节的存储顺序。
little endian(小端字节序):将低序字节放在低地址(起始地址)。
Big endian(大端字节序): 将高序字节放在低地址(起始地址)。
举例:
假设需要传输的数据为0x01020304(32位)(前面为高序字节,后面为低序字节)
假设内存地址是 4000 & 4001 & 4002 & 4003
下表为低序 小端字节序
假设内存地址 | 二进制 | 十进制 |
---|---|---|
4003 | 0000 0001 | 01 |
4002 | 0000 0010 | 02 |
4001 | 0000 0011 | 03 |
4000 | 0000 0100 | 04 |
下表为高序 大端字节序
假设内存地址 | 二进制 | 十进制 |
---|---|---|
4003 | 0000 0100 | 04 |
4002 | 0000 0011 | 03 |
4001 | 0000 0010 | 02 |
4000 | 0000 0001 | 01 |
PS:X86序列CPU均为小端字节序;网络字节序均为大端字节序。
获取网络字节序和主机字节序:
//头文件
#include <netinet/in.h>
//函数
uint16_t htons(uint16_t host16bitvalue); //返回网络字节序的值
uint32_t htonl(uint32_t host32bitvalue); //返回网络字节序的值
uint16_t ntohs(uint16_t net16bitvalue); //返回主机字节序的值
uint32_t ntohl(uint32_t net32bitvalue); //返回主机字节序的值
//h代表host,n代表net,s代表short(两个字节),l代表long(4个字节)。
//通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。
//有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取.
提示:以下是本篇文章正文内容,下面案例可供参考
一、socket服务器与客户端的开发步骤
开发步骤:
1、创捷套接字。
2、为套接字添加信息(IP地址和端口号)。
3、监听网络连接。
4、监听到有客服端接入,接受一个连接。
5、数据交互。
6、关闭套接字,断开连接。
示意图:
二、具体使用步骤
1.socket(创建连接协议)
函数如下:
//头文件
#include<sys/types.h>
#include<sys/socket.h>
//函数
int socket(int domain, int type, int protocol);
//返回
//success 返回新的套接字的文件描述符
//error 返回 -1
参数
domain:指向所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族)
AF_INET
代表 IPV4因特网域
AF_INET6
代表 IPV6因特网域
AF_UNIX
代表 UNIX域
AF_ROUTE
代表 路由套接字
AF_KEY
代表 密钥套接字
AF_UNSPEC
代表 未指定
type:指定socket类型
SOCK_STREAM
代表 TCP
SOCK_DGEAM
代表 UDP
SOCK_RAW
代表 IP与ICMP
(对底层协议访问)
protocol:通常取为”0“,”0“对于与type类型的对应默认协议。
IPPROTO_TCP
代表 TCP协议
IPPROTO_UDP
代表 UDP协议
IPPROTO_SCIP
代表 SCIP协议
IPPROTO_TIPC
代表 TIPC协议
2.bind(地址准备好)
功能
用于绑定IP地址和端口号到socketfd。
函数如下:
//头文件
#include<sys/types.h>
#include<sys/socket.h>
//函数
int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
//返回
//success 返回 0
//error 返回 -1
参数
sockfd: socket 返回的文件描述符
addr:struct sockaddr的地址,用于设定要绑定服务端的IP和端口号
addrlen:是addr结构的长度,可以设置成sizeof(struct sockaddr)。
其中:
struct sockaddr {sa_family_t sa_family; //协议族char sa_data[14]; //IP+端口号
}
上述结构体参数:
sa_family 用于指定AF_***
表示使用什么协议族的IP。
sa_data 存放IP和端口号。
直接向sa_data中写入IP和端口号有点麻烦,内核提供struct sockaddr_in结构体进行写入,通过**/usr/include/linux/in.h可以看到结构体原型。使用该结构体时需要包含<netinet/in.h>头文件**,且sockaddr_in结构体是专门为tcp/ip协议族使用。
同等替换为:
struct sockaddr_in{sa_family t sin_family;//协议族in_port_t sin_port;//端口号struct in_addr sin_addr;//IP地址结构体unsigned char sin_zero[8];//填充没有实际意义只是为跟sockadd结构在内存中对序这样两者才能相互转换
}
PS:寻找bind的优化结构步骤和命令
cd /user/include/
grep “struct sockaddr_in{” * -nir
vi linux/in.h +184
其中:n是行号 r是递归
bind步骤中需要用到地址转换API:
//头文件
#include<arpa/inet.h>
//函数:把字符串形式的“192.168.1.123”转为网络能识别的格式 。
int inet_aton(const char* straddr,struct in_addr *addrp);
//函数:把网络格式的ip地址转为字符串形式。
char* inet_ntoa(struct in_addr inaddr);
3.listen(监听)
功能
设置能处理的最大连接数,listen()并未开始接受连线,只是设置sockect的listen模式,listen函数只用于服务器端,服务器进程不知道要与谁连接,因此,它不会主动地要求与某个进程连接,只是一直监听是否有其他客户进程与之连接,然后响应该连接请求,并对它做出处理,一个服务进程可以同时处理多个客户进程的连接。
主要就两个功能:将一个未连接的套接字转换为一个被动套接字(监听),规定内核为相应套接字排邦队的最大连接数。
//头文件
#include<sys/types.h>
#include<sys/socket.h>
//函数
int listen(int sockfd, int backlog);
//返回
//success 返回 0
//error 返回 -1
sockfd:是socket系统调用返回的服务器端socket描述符。
backlog:指定在请求队列中允许的最大请求数。
4.accept(连接)
功能
accept函数由TCP服务器调用,用于从已完成连接队列队头返回下个已亮成连接。如果已完成连接队列为空,那么进程被投入睡眠
函数如下:
//头文件
#include<sys/types.h>
#include<sys/socket.h>
//函数
int accept(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
//返回
//success 返回 接受的套接字的描述符
//error 返回 -1
参数
·sockfd:socket系统调用返回的服务器端socket描述符。
addr:用来返回已连接的对端(客户端)的协议地址。
addrled:客户端地址长度。
返回值:该函数的返回值是一个新的套接字描述符,返回值是表示已连接的套接字描述符。而第一个参数是服务器监听套接字描述符。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在。
5.read/write/send/recv(数据收发)
在套接字通信中进行字节读取函数:read(),write()。与I/0中的读取函数略有区别,因为它们输入或输出的字节数处比可能比请求的少。
//头文件
#include <unistd.h>
//函数
ssize_t write(int fd,const void*buf,size_t nbytes);
ssize t read(int fd,void *buf,size_t nbyte);
//返回
//success 返回 均返回读或写的字节个教
//error 返回 -1
第一个函数将buf中的nbytes个字节写入到文件描述符fd中,成功时返回写的字节数。
第二个函数为从fd中读取nbyte个字节到buf中,返回实际所读的字节数。详细应用说明参考使用read write读写socket(套节字)。
网络I/o还有一些函数如:recv()/send(), readv()/writev(),recvmsg()/sendmsg(),recvfrom()/sendto()等。
常用第二套数据收发,在TCP套接字上发送数据函数:有连接
//头文件#include<sys/socket.h>
//函数
ssize_t send(int s,const void *msg,size_t len,int flags);
ssize_t recv(int s,void *buf,size_t len,int flags);
//返回
//success 返回 均返回读或写的字节个教
//error 返回 -1
send函数只能对处于连接状杰的套接字使用。
s为已建立好连接的套接字描述符,即accept函数的返回值。
msg指向存放待发送数据的缓冲区。
len为待发送数据的长度。
flags为控制选顶,一般设置为0。
recv函数包含3要素:套接字s,接收缓冲区buf,长度len
recv从参数所指定的套接字描述符(必须是面向连接的套接字)上接收数据并保存到参数buf所指定的缓冲区,参数len则为缓冲区长度,参数flags为控制选项,一般设置为0。
6.connect
功能
该函数用于绑定之后的客户端,即客户端与服务器建立连接。基本用法和bind一致。
函数如下:
//头文件
#include<sys/types.h>
#include<sys/socket.h>
//函数
int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
//返回
//success 返回 0
//error 返回 -1
参数
sockfd: socket 返回的文件描述符
addr:struct sockaddr的地址,用于设定要绑定客户端的IP和端口号
addrlen:是addr结构的长度,可以设置成sizeof(struct sockaddr)。
小贴士:
Windows 的 cmd 连接 Linux
ping 172.20.10.3(主机地址) 地址连接
telnet 172.20.10.3 8989(主机地址) 连接Linux
三、例子
1.服务端——固定IP地址连接
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>//struct sockaddr_in {// __kernel_sa_family_t sin_family; /* Address family */
// __be16 sin_port; /* Port number */
// struct in_addr sin_addr; /* Internet address *//* Pad to size of `struct sockaddr'. */
// unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
// sizeof(unsigned short int) - sizeof(struct in_addr)];
//};int main()
{int s_fd;int c_fd;char readBuf[128];char *msg="I get your connect";struct sockaddr_in s_addr;struct sockaddr_in c_addr;memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));//1.sockets_fd = socket(AF_INET,SOCK_STREAM,0);if(s_fd == -1){perror("socket");exit(-1);}//2.binds_addr.sin_family = AF_INET;s_addr.sin_port = htons(8988);inet_aton("192.168.1.110",&s_addr.sin_addr);bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listenlisten(s_fd,10);//4.acceptint clen = sizeof(struct sockaddr_in);c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);if(c_fd == -1){perror("accept");}printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));//5.readint n_read = read(c_fd,readBuf,128);if(n_read == -1){perror("read");}else{printf("get message:%d, %s\n",n_read,readBuf);}//6.writewrite(c_fd,msg,strlen(msg));return 0;
}
2.客服端——固定IP地址连接
include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>//struct sockaddr_in {// __kernel_sa_family_t sin_family; /* Address family */
// __be16 sin_port; /* Port number */
// struct in_addr sin_addr; /* Internet address *//* Pad to size of `struct sockaddr'. */
// unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
// sizeof(unsigned short int) - sizeof(struct in_addr)];
//};int main()
{int c_fd;char readBuf[128];char *msg="msg from client";struct sockaddr_in c_addr;memset(&c_addr,0,sizeof(struct sockaddr_in));//1.socketc_fd = socket(AF_INET,SOCK_STREAM,0);if(c_fd == -1){perror("socket");exit(-1);}//2.connectc_addr.sin_family = AF_INET;c_addr.sin_port = htons(8988);inet_aton("192.168.1.110",&c_addr.sin_addr);if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in))){perror("connect");exit(-1);}//3.writewrite(c_fd,msg,strlen(msg));//4.readint n_read = read(c_fd,readBuf,128);if(n_read == -1){perror("read");}else{printf("get message:%d, %s\n",n_read,readBuf);}return 0;
}
2.服务端——自由指定地址
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>//struct sockaddr_in {// __kernel_sa_family_t sin_family; /* Address family */
// __be16 sin_port; /* Port number */
// struct in_addr sin_addr; /* Internet address *//* Pad to size of `struct sockaddr'. */
// unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
// sizeof(unsigned short int) - sizeof(struct in_addr)];
//};int main(int argc, char **argv)//需要自己指定IP地址和端口号
{int s_fd;int c_fd;int mark=0;char readBuf[128];char msg[128]={0};// char *msg="I get your connect";struct sockaddr_in s_addr;struct sockaddr_in c_addr;memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));if(argc != 3){printf("param is error\n");exit(-1);}//1.sockets_fd = socket(AF_INET,SOCK_STREAM,0);if(s_fd == -1){perror("socket");exit(-1);}//2.binds_addr.sin_family = AF_INET;s_addr.sin_port = htons(atoi(argv[2]));inet_aton(argv[1],&s_addr.sin_addr);bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listenlisten(s_fd,10);//4.acceptint clen = sizeof(struct sockaddr_in);while(1){c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);if(c_fd == -1){perror("accept");}printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));mark++;//5.readif(fork() == 0){if(fork() == 0){while(1){sprintf(msg, "welcome NO.%d client",mark);write(c_fd,msg,strlen(msg));sleep(5);}}while(1){memset(readBuf,0,sizeof(readBuf));int n_read = read(c_fd,readBuf,128);if(n_read == -1){perror("read");}else{printf("get message from client:%d, %s\n",n_read,readBuf);}//6.write}break;}}return 0;
}
2.客户端——自由指定地址
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>//struct sockaddr_in {// __kernel_sa_family_t sin_family; /* Address family */
// __be16 sin_port; /* Port number */
// struct in_addr sin_addr; /* Internet address *//* Pad to size of `struct sockaddr'. */
// unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
// sizeof(unsigned short int) - sizeof(struct in_addr)];
//};int main(int argc, char **argv)//需要自己输入地址
{int c_fd;int n_read;char readBuf[128];// char *msg="msg from client";char msg[128]={0};struct sockaddr_in c_addr;memset(&c_addr,0,sizeof(struct sockaddr_in));if(argc != 3){printf("param is error\n");exit(-1);}//1.socketc_fd = socket(AF_INET,SOCK_STREAM,0);if(c_fd == -1){perror("socket");exit(-1);}//2.connectc_addr.sin_family = AF_INET;c_addr.sin_port = htons(atoi(argv[2]));inet_aton(argv[1],&c_addr.sin_addr);if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) ==-1){perror("connect");exit(-1);}//3.writewhile(1){if(fork() == 0){while(1){memset(msg,0,sizeof(msg));printf("input:");gets(msg);write(c_fd,msg,strlen(msg));}}//4.readwhile(1){memset(readBuf,0,sizeof(readBuf));n_read = read(c_fd,readBuf,128);if(n_read == -1){perror("read");}else{printf("get message from service:%d, %s\n",n_read,readBuf);}}}return 0;
}
总结
socket网络编程,总体步骤清晰。服务端所需要用到socket()、bind()、listen()、accept();客户端所需要用到socket()、connect();两者数据传输均需要read()、write()。
bind()中绑定IP地址和端口号,需要使用到内核指定结构体,需要理解接受;connect()和bind()使用方法相同。网络编程需要知道大端和小端字节序的转换,以及网络字节序和主机字节序的获取。
基于socket编程,可以深入进行FTP云盘项目代码编写。
Linux网络编程——socket、bind、listen、accpet、connect、read和write相关推荐
- linux网络编程二:基础socket, bind, listen, accept, connect
linux网络编程二:基础socket, bind, listen, accept, connect 1. 创建socket #include <sys/types.h> #inc ...
- Linux 网络编程——socket 网络编程
文章目录 一.网络基础 TCP/UDP对比 TCP/IP协议族体系 socket IP地址 IP地址转化API inet_addr() inet_aton() inet_ntoa() inet_pto ...
- Linux网络编程,bind:error:Cannot assign requested address,Ubuntu网络桥接
一:问题:刚开始学习Linux网络编程,第一次使用bind函数出现Cannot assign requested address,错误.在网上找了很多帖子但是都没有解决. 二: 问题一:也就是网上大部 ...
- Linux网络编程 | Socket编程(一):Socket的介绍、UDPSocket的封装、UDP服务器/客户端的实现
目录 套接字编程 Sockaddr结构 字节序 地址转换 常用套接字接口 UDP的通信流程 UDPSocket的封装 UDP服务器 UDP客户端 套接字编程 所谓套接字(Socket),就是对网络中不 ...
- Linux网络编程(Socket)
目录 网络编程(Socket)概述 引入 网络编程通识扫盲 socket套接字 套接字描述符 字节序 socket编程步骤 Linux提供的API简析 创建套接字即连接协议[socket](服.客) ...
- 27.Linux网络编程socket变成 tcp 高并发 线程池 udp
好,咱们开始上课了,从今天开始咱们连续讲 8 天的,网络编程这个还是在linux环境下去讲,咱们先看一下咱们这 8 天都讲什么东西,跟大家一块来梳理一下,你先有个大概的印象,这些你也不要记,那么网络编 ...
- Linux网络编程——socket编程
一.socket socket 网络套接字一个文件文件描述符指向一个套接字(该套接字内部由内核借助两个缓冲区实现(接收缓冲区和发送缓冲区))通讯过程中,套接字一定是 [成对] 出现的. 二.网络字节序 ...
- Linux 网络编程socket错误分析
socket错误码: EINTR: 4 阻塞的操作被取消阻塞的调用打断.如设置了发送接收超时,就会遇到这种错误. 只能针对阻塞模式的socket.读,写阻塞的socket时,-1返回,错误号为INTR ...
- Linux网络编程 | Socket编程(二)TCPSocket的封装、TCP服务器多进程、多线程版本的实现
目录 TCP的通信流程 TCPSocket的封装 TCP客户端 TCP服务器 多进程版本 多线程版本 TCP的通信流程 计算机网络 (三) 传输层 :一文搞懂UDP与TCP协议 在这篇博客中,我描述了 ...
最新文章
- 架构设计的真谛:系统与子系统、模块与组件、框架与架构
- 微软MSN推出新一代Live服务 能离线编辑博客
- python自学时间-Python 从入门到精通:一个月就够了!
- Linux下MySQL数据库的备份与还原,mysql快速导入导出数据库实例演示,解决mysql大数据量数据库导出慢的问题
- 学习:组件生命周期(1)
- python 结构数组_Python数组
- php js对话框,JavaScript_js弹出框、对话框、提示框、弹窗实现方法总结(推荐),一、JS的三种最常见的对话框- phpStudy...
- spark相关原理介绍
- bootstrap jasny fileinput插件冲突问题解决
- udp聊天?使用udp+python实现多人聊天室
- 换IP软件用户如何选择?
- arduino学习笔记-库函数解析_LiquidCrystal_i2c使用说明以及lcd1602的驱动
- 文件模式为 rw-r r linux,linux中-rw-rw-r-- l 是什么意思啊,linux 里命令ls -l 后,文件类型权...
- android,java知识点总结 (二)
- 【Python爬虫】IOS苹果商店指定APP评论爬取
- The bean sellergoods.FeignClientSpecification could not be registered. A bean with that name has a
- golang构建htpp服务
- 振荡次数计算机控制系统,计算机控制第四章.ppt
- 「基因组学」使用CAFE进行基因家族扩张收缩分析
- (最详细教程)中国知网caj格式论文如何转化为word,pdf,txt