淘宝建设网站,虚拟主机评测,wordpress安装显示英文,免费建设外贸网站端口 #xff1a;主机上一个应用程序的代号#xff08;端口不变#xff09; 为什么不用PID来表示一个应用 因为PID会变化#xff0c;而端口是不变的 套接字进程间通信——跨越主机 1、主机字节序列和网络字节序列 主机字节序列分为大端字节序和小端字节序#xff0c;不同… 端口 主机上一个应用程序的代号端口不变 为什么不用PID来表示一个应用 因为PID会变化而端口是不变的 套接字进程间通信——跨越主机 1、主机字节序列和网络字节序列 主机字节序列分为大端字节序和小端字节序不同的主机采用的字节序列可能不同。大 端字节序是指一个整数的高位字节存储在内存的低地址处低位字节存储在内存的高地址 处。小端字节序则是指整数的高位字节存储在内存的高地址处而低位字节则存储在内存的 低地址处。 在两台使用不同字节序的主机之间传递数据时可能会出现冲突。所以在将数 据发送到网络时规定整形数据使用大端字节序所以也把大端字节序成为网络字节序列。对 方接收到数据后可以根据自己的字节序进行转换 Linux 系统提供如下 4 个函数来完成主机字节序和网络字节序之间的转换 # include netinet/in.h
uint32_t htonl(uint32_t hostlong);//长整型的主机字节转网络字节序
uint32_t ntohl(uint32_t netlong);//长整型的网络字节序转主机字节序
uint32_t htons(uint16_t hostshort);//短整型的主机字节转网络字节序列
uint32_t ntonhs(uint16_t hostshort);//短整型的网络字节序列转主机字节序列2.套接字地址结构 2.1通用socket地址结构 socket网络编程接口中表示socket地址是结构体sockaddr,其定义如下 # include bits/socket.h
struct sockaddr//通用套接字地址结构
{sa_family_t sa_family;char sa_data[14];
} sa_family成员是地址族类型sa_family_t的变量。地址族类型通常与协议族类型对应。常见的协议族和对应的地址族如下图所示 2.2 专用socket地址结构 TCP/IP协议族有sockaddr_in和sockaddr_in6两个专用socket地址结构体它们分别用于IPV4和IPV6 /*sin_family:地址族 AF_INETsin_port:端口号需要用网络字节序表示sin_addr:IPV4地址结构s_addr以网络字节序表示IPV4地址
*/
struct in_addr
{u_int32_t s_addr;
};
struct sockaddr_in
{sa_family sin_family;u_int16_t sin_port;struct in_addr sin_addr;
};
struct in6_addr
{unsigned char sa_addr[16];
};
struct sockaddr_in6
{sa_family_t sin6_family;//地址族AF_INETu_inet16_t sin6_port;//端口号用网络字节序表示u_int32_t sin6_flowinfo;//流信息应设置为0struct in6_addr sin6_addr;//IPV6地址结构体u_int32_t sin6_scope_id;//scope ID,尚处于实验阶段
}; 2.3 IP地址转换函数 通常人们习惯用点分十进制字符串表示IPV4地址但编程中我们需要把它们转换成整数方能使用下面函数可用于点分十进制字符串表示的IPV4地址和网络字节序整数表示的IPV4地址之间的转换 # include arpa/inet.h
in_addr_t inet_addr(const char*cp);//字符串表示的IPV地址转化为网络字节序
char*inet_ntoa(struct in_addr in);//IPV4地址的网络字节序转化为字符串表示 3.网络编程接口 # include sys.types.h
# include sys/socket.h
int socket(int domain,int type,int protocol);
/*
socket()创建套接字成功返回套接字的文件描述符失败返回-1
第一个参数domain(地址族):AF_UNIX、AF_INET、AF_INET6
第二个参数type(服务类型)设置套接字的服务类型SOCK_STREAMTCP协议 SOCK_DGRAMUDP协议
第三个参数protocol(协议版本):一般设置为0表示使用默认协议
*/
int bind(int sockfd,const struct sockaddr*addr,socklen_t addrlen);
/*
bind():将sockfd与一个socket地址绑定成功返回0失败返回-1
sockfd是网络套接字描述符
addr是地址结构
addrlen是socket地址长度
*/
int listen(int sockfd,int backlog);
/*
listen()创建一个监听队列以存储待处理的客户连接成功返回0失败返回-1
sockfd是被监听的socket套接字
backlog表示处于完全连接状态的socket的上限
*/
int accept(int sockfd,struct sockaddr*addr,socklen_t*addrlen);
/*
accept()从listen监听队列中接收一个连接成功返回一个新的连接socket该socket唯一地标识了被接收的这个连接失败返回-1
sockfd是执行过listen系统调用的监听socket
addr参数用来获取被接受连接的远端socket地址
addrlen指定该socket地址的长度
*/
ssize_t recv(int sockfd,void*buff,size_t len,int flags);
ssize_t send(int sockfd,const void*buff,size_t len,int flags);
/*
TCP数据读写
recv()读取sockfd上的数据buff和len参数分别指定读缓冲区的位置和大小
send()往socket上写入数据buff和len参数分别指定写缓冲区的位置和数据长度
flgs参数为数据收发提供了额外的控制
*/
int connect(int sockfd,const struct sockaddr*serv_addr,socklen_t addrlen);
/*
connect()客户端需要通过此系统调用来主动与服务器建立连接成功返回0失败返回-1
第一个参数sockfd时由socket()返回的一个套接字标识符
第二个参数serv_addr是服务器监听的socket地址
第三个参数addrlen则指定这个地址的长度
*/
ssize_t recvfrom(int sockfd,void*buff,size_t len,int flags,struct sockaddr*src_addr,socklen_t*addrlen);
ssize_t sendto(int sockfd,void*buff,size_t len,int flags,struct sockaddr*dest_addr,socklen_t addrlen);
/*
UDP数据读取
recvfrom()读取sockfd上的数据buff和len参数分别指读缓冲区的位置和大小src_addr记录发生端的socket地址addrlen指定该地址的长度
sendto()往socket上写入数据buff和冷参数分别指读取缓冲区的位置和数据长度dest_addr指定接收数据端的地址addrlen指定该地址的长度
*/ 4.TCP编程流程 TCP提供的是面向连接的、可靠的、字节流服务。TCP的服务器端和客户端编程流程如下 socket()方法是用来创建一个套接字有了套接字就可以通过网络进行数据的收发。这也是为什么进行网络通信的程序首先要创建一个套接字。创建套接字时要指定使用的服务类型使用TCP协议选择流式服务SOCK_STREAM。 bind()方法是用来指定套接字使用的IP地址和端口。IP地址就是自己主机的地址如果主机没有接入网络测试程序时可以使用回环地址127.0.0.1。端口是一个16位的整形值一般0-1024为知名端口入HTTP使用的是80号端口。这类端口一般用户不能随便使用。其次1024-4096为保留端口用户一般也不用。4096以上为临时端口用户可以使用。在Linux上1024以内的端口只有root用户可以使用。 listen()方法是用来创建监听队列。监听队列有两种一个是存放未完成三次握手的连接一种是存放已完成三次握手的连接。listen()第二个参数就是指定已完成三次握手队列的长度。 accept()处理存放在listen创建的已完成三次握手的队列中的连接。每处理一个连接则accept()返回该连接对应的套接字描述符。如果该队列为空则accept()阻塞。 connect()方法一般由客户端程序执行需要指定连接的服务器端的IP地址和端口。该方法执行后会进行三次握手建立连接。 send()方法用来向TCP连接的对端发送数据。send()执行成功只能说明将数据成功写入到发送端的发送缓冲区中并不能说明数据已经发送到了对端。send()的返回值为实际写入到发送端缓冲区中的数据长度。 recv()方法用来接收TCP连接的对端发送来的数据。recv()从本端的缓冲区中读取数据如果接收缓冲区中没有数据则recv()方法会阻塞。返回值是实际读到的字节数如果recv()返回值为0说明对方已经关闭了TCP连接。 close()方法用来关闭TCP连接。此时会进行四次挥手。 TCP服务端代码ser.c如下 # include stdio.h
# include stdlib.h
# include unistd.h
# include string.h
# include assert.h
# include sys/socket.h
# include arpa/inet.h
# include netinet/in.h
int main ()
{int sockfdsocket(AF_INET,SOCK_STREAM,0);//第一步创建套接字assert(sockfd!-1);struct sockaddr_in saddr;//IPV4地址结构saddr以网络字节序列表示地址结构memset(saddr,0,sizeof (saddr));//清空saddr防止占用空间saddr.sin_familyAF_INET;saddr.sin_porthtons(6000);//htons将主机字节序转换为网络字节序saddr.sin_addr.s_addrinet_addr(127.0.0.1);//回环地址int resbind(sockfd,(struct sockaddr*)saddr,sizeof(saddr));//讲套接字标识符与套接字socket地址绑定成功返回0失败返回-1.assert(res!-1);reslisten(sockfd,5);//创建一个监听队列处理待处理的连接assert(res!-1);while(1){struct sockaddr_in caddr;//远端连接服务器的地址结构socklen_t lensizeof (caddr);int caccept(sockfd,(struct sockaddr*)caddr,len);//从listen监听队列中接收一个连接成功返回一个新的连接socket该socket唯一地标识了被接收的这个连接失败返回-1if (c-1){continue;}printf(accept c%d\n,c);char data[128]{0};int nrecv(c,data,127,0);printf (n%d,buff%s\n,n,data);send(c,ok,2,0);close(c);//关闭连接开始四次挥手
}
close(sockfd);
exit(0);
} TCP客服端代码cli.c如下 # include stdio.h
# include stdlib.h
# include unistd.h
# include string.h
# include assert.h
# include sys/socket.h
# include arpa/inet.h
# include netinet/in.h
int main ()
{int sockfdsocket(AF_INET,SOCK_STREAM,0);assert(sockfd!-1);struct sockaddr_in saddr;memset(saddr,0,sizeof(saddr));saddr.sin_familyAF_INET;saddr.sin_porthtons(6000);saddr.sin_addr.s_addrinet_addr(127.0.0.1);int resconnect(sockfd,(struct sockaddr*)saddr,sizeof (saddr));//客服端需要通过此系统调用来主动与服务器端建立连接成功返回0失败返回-1。assert(res!-1);printf (please input:);char buff[128]{0};fgets(buff,128,stdin);send(sockfd,buff,strlen(buff)-1,0);//往socked上写入数据int nrecv(sockfd,data,127,0);//读取sockfd上的数据printf (%s\n,data);close(sockfd);exit(0);
} 结果 5.多进程、多线程处理并发 如图所示当一个客户端与服务器建立连接以后·服务器端accept()返回进而准备循环接收客服端发过来数据 。如果客服端暂时没发数据服务端会在recv()阻塞。此时其他客户端向服务器发起连接后由于服务器阻塞了无法执行accept()接受连接也就是其他客户端发生的数据服务器无法读取服务器也就无法并发同时处理多个客户端。 这个问题可以通过引入多线程或多进程来解决。服务器端接受一个客服端的连接后创建一个线程或者进程然后在新创建的线程中循环处理数据。主线程(父进程)只负责监听客服端的连接并使用accept()接受连接不进行数据的处理。如下图所示: 多线程处理并发的服务器端代码ser.c如下 # include stdio.h
# include stdlib.h
# include unistd.h
# include string.h
# include assert.h
# include sys/socket.h
# include netinet/in.h
# include arpa/inet.h
# include pthread.h
void*fun(void *arg)
{int c(int)arg;while (1){char buff[128]{0};if (recv(c,buff,127,0)0){break;}printf (recv(%d)%s\n,c,buff);send(c,ok,2,0);}printf (one client over(%d)\n,c);close(c);
}
int main ()
{int sockfdsocket(AF_INET,SOCK_STREAT,0);assert(sockfd!-1);struct sockaddr_in saddr,caddr;memset(saddr,0,sizeof (saddr));saddr.sin_familyAF_INET;saddr.sin_porthtons(6000);saddr.sin_addr.s_addrinet_addr(127.0.0.1);int resbind(sockfd,(struct sockaddr*)saddr,sizeof(saddr));assert(res!-1);reslisten(sockfd,5);assert(res!-1);while (1){int lensizeof(caddr);int caccept(sockfd,(struct sockaddr*)caddr,sizeof (caddr));if (c0){continue;}printf (accept c%d\n,c);pthread_t id;pthread_create(id,NULL,fun,(void*)c);}close(sockfd);exit(0);
} 6.UDP编程流程 UDP提供的是无连接、不可靠、数据服务。编程流程如下。 socket()用来创建套接字使用udp协议时选择数据报服务SOCK_DGRAM。sendto()用来发送数据由于udp时无连接的每次发送数据都需要指定对端的地址IP和端口。recvfrom()接收数据每次都需要传给该方法一个地址结构来存放发送端的地址·。recvfrom()可以用来接收所有客服端发送给当前应用程序的地址并不是一个只能接收某一个客服端的数据。 UDP服务端编程代码示例 # include stdio.h
# include stdlib.h
# include string.h
# include unistd.h
# include assert.h
# include sys/socket.h
# include netinet/in.h
# include arpa/inet.h
int main ()
{int sockfdsocket(AF_INET,SOCK_DGRAM,0);assert(sockfd!-1);struct socksaddr_in saddr,caddr;memset(saddr,0,sizeof (saddr));saddr.sin_familyAF_INET;saddr.sin_porthtons(6000);saddr.sin_addr.s_addrinet_addr(127.0.0.1);int resbind(sockfd,(struct saddr*)saddr,sizeof(saddr));assert(res!-1);while (1){int lensizeof(caddr);char buff[128]{0};recvfrom(sockfd,buff,127,0,(struct sockaddr*)caddr,len);printf (ip:%s,port:%d,buff%s\n,inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buff);sendto(sockfd,ok,2,0,(struct sockaddr*)caddr,sizeof (caddr));}close(sockfd);
} UDP客户端代码 # include stdio.h
# include stdlib.h
# include string.h
# include unistd.h
# include sys/socket.h
# include assert.h
# include netinet/in.h
# include arpa/inet.h
int main ()
{int sockfdsocket(AF_INET,SOCK_DGRAM,0);assert(sockfd!-1);struct sockaddr_in saddr;memset(saddr,0,sizeof(saddr));saddr.sin_familyAF_INET;saddr.sin_porthtons(6000);saddr.sin_addr.s_addrinet_addr(127.0.0.1);while(1){char buff[128]{0};printf (input:\n);fgets(buff,128,stdin);if(strcnmp(buff,end,3)0){break;}sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)saddr,sizeof(saddr));memset(buff,0,128);int lensizeof(saddr);recvfrom(sockfd,buff,127,0,(struct saddr*)saddr,len);printf (buff%s\n,buff);}
close(sockfd);
}