数据发到网络环境通常通过交换机,再由交换机转发至路由器 但凡提到套接字,必定有两端:数据的发送端和接收端, ip地址在网络环境中可以唯一的表示一台主机,ip+端口号可以在网络环境中唯一标识一个进程。 socket通信原理: 套接字是linux操作系统中的一种文件类型----伪文件 linux中有7种文件类型:其中普通文件,目录文件,软链接三种文件类型占用内存,字符设备,块设备,管道,套接字 管道,fd[0]读端,fd[1]写端,半双工,如同对讲机 socket乃是双向全双工,一个文件描述符,两个缓冲区(一个读,一个写) 对于数字8910.67 低地址存高位----大端法按照内存地址顺序由近及远依次存入8,9,1,0,.6.7 低地址存低位----小端法(与上边相反)x86就是如此,小端法存储,源于windows基于Intel x86平台 理论上是这样,不同操作系统可能不尽相同。 网络数据流采用了大端字节序,操作系统采用了小端法存储,这样一来网卡发送的数据字节序发生了变化,这样一来,接收端在解包的过程中无法正确解析IP地址等关键信息,这就需要能转换网络字节序和主机字节序的系列函数 uint32_t htonl(uint32_t hostlong) uint16_t htons(uint16_t hostshort) uint32_t ntohl(uint32_t netlong) uint16_t ntohs(uint16_t netshort) 由于点分十进制是我们表达IP的惯用手法,但在网络环境中,这种方法显然无法满足要求,这就需要一组函数完成两种(点分十进制和网络字节序)规制的相互转换 int inet_pton(int domain,char * src, void* des) 第一个参数代表net版本类型 ipv4,或是ipv6 char * inet_ntop(int domain,const void * src, char* des socklen_t size) 描述ipv4地址协议的结构体(sockaddr_in) 典故与现实,早期描述ipv4协议的结构体是sockaddr,网络协议绑定函数bind()参数列表中就是这种结构体类型指针,然而定义ipv4协议类型结构体又只能使用 sockaddr_in类型,所以,为了兼容bind()函数要强转类型: struct sockaddr_in sockipv4; bind(struct sockaddr* &sockipv4) linux 结构体查看命令 man 7 ip socket int socket(domain,type,protocol) domain: ipv4 ipv6 type:流式协议,报式协议…… protocol: 默认0 成功,返回整型文件描述符,失败返回-1 int bind(int sockfd,const struct sockaddr* addr,socklen_t addrlen);往socket上绑定ip和端口号,成功返回0,失败返回-1 int listen()//指定监听上线数,允许同时建立连接(而非保持)的数量(可能允许的数量还未达到支持上限) int accept(int fd,struct sockaddr* addr, socklen_t * size)//返回一个新的文件描述符,这一新的文件描述符,作为socket的accept端, 参数1:指定(我们创建的)套接字(以便基于此后续建立accept套接字) 参数2:传出参数,与我建立连接的客户端的地址信息。 参数3:传入传出参数,传入的读一次,传出写一次 返回:一个全新的socket文件描述符,用来同客户端进行通信 int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen) 参数1:客户端新建的socket 参数2:传入参数,指定服务器的地址信息,含ip地址和端口号。 参数3:传入参数,传入的addr大小。
- 一个服务端的实现---功能:把客户端的输入字符转为小写
#include <stdio.h> #include <unistd.h> #include <sys/socket.h> #include <stdlib.h> #include <netinet/in.h> #define SERV_PORT 6666//1024-65535 #define BUFSIZE 1024 int main() { struct sockaddr_in serv_addr,client_addr; int sfd,cfd,n; char buf[BUFSIZE]; sfd = socket(AF_INET,SOCK_STREAM,0); serv_addr.sin_family=AF_INET; serv_addr.sin_port=htons(SERV_PORT); serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); socklen_t lenofserv_addr = sizeof(serv_addr); bind(sfd,(const struct sock_addr*)(&serv_addr),lenofserv_addr); listen(sfd,128); socklen_t lenofclient_addr = sizeof(client_addr); cfd=accept(sfd,(const struct sockaddr*)(&client_addr),&lenofclient_addr); while(1)//while循环,要写在accept()函数后面,否则除非不断开启客户端,转换完大小写后就会阻塞等待客户端请求 { n = read(cfd,buf,sizeof(buf)); for(int i=0;i<n;i++) { buf[i] = tolower(buf[i]); } write(cfd,buf,n); } close(sfd); close(cfd); printf("Hello World! "); return 0; }
- 编译,启动,warning警示,
- 如果你还没有写好一个客户端,可以开启一个shell终端,输入命令(当然,你的ip和端口号可能与我不同)
nc 127.0.0.1 6666
然后尽情使用吧