socket编程
Socket编程
为了实现两台不同的机器能够进行通信,所有要使用到网络编程
IP地址与端口号
- IP地址:用于标识网络上主机的位置,每台网络上的设备都有唯一的ip地址。
- 端口号:用于标识主机上的哪个应用程序,一台主机上运行的很多应用程序,该数据是传送给哪个应用程序使用的通过端口号标识。
TCP与UDP
是数据传输层的两种通信协议,区别如下:
TCP
-
面向有连接,即发送数据之前需要建立连接,
-
传输可靠,适用于文件传输等对数据完整性高的场景
UDP
-
面向无连接,传输不可靠,
-
适用于视频流等对数据传输完整性不高的场景
字节序
字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。由于不同的主机可能存储数据的字节序不同,不同字节序的主机进行数据交互时可能会造成数据错误
字节序有:
- 小端字节序:数据的低位存储在低地址
- 大端字节序:数据的高位存储在低地址
- 网络字节序:大端字节序,网络传输时都是按大端进行传输
字节序转换api:
#include <arpa/inet.h>
//主机字节序转换为网络字节序,返回网络字节序的值
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
//网络字节序转换为主机字节序,返回主机字节序的值
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
- h代表host,n代表net,,s代表short(两个字节),l代表long(4个字节)
- 通过上面4个函数可以实现主机字节序和网络字节序之间的转换
Socket实现流程
网络编程函数
头文件:
#include <sys/socket.h>
socket()
函数原型:int socket(int domain, int type, int protocol);
函数作用:创建网络通信套接字;
返回值:成功时返回套接字(文件描述符),失败返回-1
参数说明:
-
domain:协议族,指定通信时用的协议族;常用选项如下:
AF_UNIX, AF_LOCAL :Local communication,用于本地进程/线程间通信;
AF_INET :IPv4 Internet protocols,用于IPV4网络通信;
AF_INET6 :IPv6 Internet protocols,用于IPV6网络通信; -
type:套接字类型,常用选项如下:
SOCK_STREAM :流式套接字,唯一对应于TCP;
SOCK_DGRAM :数据报套接字,唯一对应于UDP;
SOCK_RAW :原始(透传)套接字; -
protocol:
通常填0,在type类型为SOCK_RAW时,需要该参数。
bind()
函数原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
函数作用:绑定服务器相关信息,IP地址和端口号;
返回值:成功时返回0,失败返回-1。
参数说明:
-
sockfd:通过socket()得到的文件描述符;
-
addr :指向struct sockaddr类型结构体变量的指针,包含了IP地址和端口号;实际使用时,如果是网络编程,一般都是定义struct sockaddr_in类型的变量,然后取该变量的地址强转为struct sockaddr*类型;
//internet协议地址结构: struct sockaddr_in { u_short sin_family; // 协议族, AF_INET,2 bytes u_short sin_port; // 端口号,2 bytes struct in_addr sin_addr; // IP地址结构体,4 bytes char sin_zero[8]; // 8 bytes unused,作为填充 };
struct in_addr { __be32 s_addr; // 32位地址 };
//通用结构体 struct sockaddr { u_short sa_family; // 协议簇族, AF_xx char sa_data[14]; // ip地址+端口 };
-
addrlen:struct sockaddr类型结构体变量所占内存空间大小;
listen()
函数原型:int listen(int sockfd, int backlog);
函数作用:将主动套接字变为被动套接字。
返回值:成功时返回0,失败返回-1。
参数说明:
-
sockfd:通过socket()得到的文件描述符。
-
backlog:指导在请求队列中允许的最大请求数。
accept()
函数原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
函数作用:阻塞等待客户端的连接请求,当由客户端请求连接时,该函数返回已经建立连接的新套接字(文件描述符),用于客户端和服务器通信;
返回值:成功时返回新的文件描述符,后面与客户端通信用的就是该返回的文件描述符,失败返回-1。
参数说明:
- sockfd:通过socket()得到的文件描述符;
- addr :指向struct sockaddr类型结构体变量的指针;用于存放连接过来的客户端的IP地址和端口号,实际使用时,如果不需要客户端的信息,可以直接填NULL;
- addrlen:指向socklen_t类型变量的指针;表示struct sockaddr类型结构体变量所占内存空间大小;
connect()
函数原型:int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
函数作用:客户端调用该函数向服务器发起连接请求;
返回值:成功时返回0,失败返回-1。
参数说明:
- sockfd:通过socket()得到的文件描述符.
- addr :指向struct sockaddr类型结构体变量的指针,用于填充服务端的IP与端口信息;
- addrlen:struct sockaddr类型结构体变量所占内存空间大小;
数据收发
ssize_t write(int fd, const void *buf, size_t nbytes);
ssize_t read(int fd, void *buf, size_t nbytes);
/*说明
*函数均返回读写的字节个数,出错则返回-1
*/
客户端与服务器通信
-
socket_service
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main() { int s_fd; int ret; char readBuf[128]; char writeBuf[128] = {0}; 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.创建套接字 s_fd = socket(AF_INET,SOCK_STREAM,0); if(-1 == s_fd) { perror("socket error"); exit(-1); } //2.绑定ip地址和端口号 s_addr.sin_family = AF_INET; s_addr.sin_port = htons(8989); inet_aton("192.168.137.115",&s_addr.sin_addr); bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in)); //3.监听 listen(s_fd,10); while(1) { //4.接受连接 int clen = sizeof(struct sockaddr_in); int c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen); if(-1 == c_fd) { perror("accept error\r\n"); exit(-1); } printf("get connect:%s\r\n",inet_ntoa(c_addr.sin_addr)); if(fork()==0) { if(fork()==0) { while(1) { memset(writeBuf,0,sizeof(writeBuf)); printf("input:"); gets(writeBuf); write(c_fd,writeBuf,sizeof(writeBuf)); } } while(1) { //5.read memset(readBuf,0,sizeof(readBuf)); ret = read(c_fd, readBuf, 128); if(-1 == ret) { perror("read error\r\n"); exit(-1); } else { printf("get message:%s\r\n",readBuf); } } break; } } return 0; }
-
socket_cilent
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main() { int c_fd; int ret; struct sockaddr_in c_addr; char readBuf[128]; char writeBuf[128] = {0}; //1.创建套接字 c_fd = socket(AF_INET,SOCK_STREAM,0); if(-1 == c_fd) { perror("socket error"); exit(-1); } //设置服务器IP及端口号 c_addr.sin_family=AF_INET; c_addr.sin_port=htons(8989); inet_aton("192.168.137.115",&c_addr.sin_addr); //2.connect if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1) { perror("connetc error"); exit(-1); } while(1) { if(fork()==0) { while(1) { //3.write memset(writeBuf,0,sizeof(writeBuf)); printf("input:"); gets(writeBuf); write(c_fd,writeBuf,strlen(writeBuf)); } } while(1) { //4.read memset(readBuf,0,sizeof(readBuf)); ret = read(c_fd,readBuf,128); if(-1 == ret) { perror("read error"); exit(-1); } else { printf("get message:%s\r\n",readBuf); } } } return 0; }