udp通信(收发短信)
?? ??? ?创建socket ?? ??? ?绑定地址 ?? ??? ?通信 ?? ??? ?关闭socket ?? ?ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, ? ? ? ? ? ? ? ? ? ?const struct sockaddr *dest_addr, socklen_t addrlen);?? ? ?? ?ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, ? ? ? ? ? ? ? ? ? ?struct sockaddr *src_addr, socklen_t *addrlen); ?? ? ?
//Udp通信 被动接收 服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h> /*memset*/
#include <unistd.h> /*close*/
#define IP "127.0.0.1" //本机测试地址
#define CPORT 10010 //客户端端口
#define SPORT 10086 //服务器端口
#define SIZE 100
int main()
{
//1.设置变量
int sockfd = 0;
struct sockaddr_in addr;
int addrlen = 0;
int newID = 0;
char buf[SIZE] = {'\0'};
int ret = 0;
//2.创建socket套接字
//参数1:地址族--->使用网络协议族
//参数2:套接字类型---->使用数据报套接字
//参数3:通常设置为0
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
if(0 > sockfd)
{
perror("socket error");
return -1;
}
printf("socket ok\n");
//3.绑定地址(自己)ip+port端口
addrlen = sizeof(addr);
memset(&addr,0,addrlen);
addr.sin_family = PF_INET; //地址族--->使用网络协议族
addr.sin_port = htons(SPORT); //htons(主机字节序转换网络字节序函数),short短整型---->设置端口
addr.sin_addr.s_addr = inet_addr(IP); //inet_addr(IP地址的转换),转换成32位的网络字节序二进制值
if(0 > bind(sockfd,(struct sockaddr *)&addr,addrlen))
{
perror("bind error");
close(sockfd); //关闭套接字
return -1;
}
printf("bind ok\n");
//4.1通信----->接收消息
//参数3:通常设置为0
memset(&addr,0,addrlen);
memset(buf,0,SIZE);
ret = recvfrom(sockfd,buf,SIZE-1,0,(struct sockaddr *)&addr,&addrlen);
if(ret > 0)
{
//inet_ntoa---->将32位的网络字节序二进制地址转换成点分十进制的字符串
printf("ip=%s,port=%d\n",(char *)inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
printf("said:%s\n",buf);
}
else
{
printf("recvfrom return %d\n",ret);
}
//4.2通信----->发送消息
memset(buf,0,SIZE);
strncpy(buf,"真好!!!",SIZE-1);
ret = sendto(sockfd,buf, strlen(buf),0,(struct sockaddr *)&addr,addrlen);
if(0 < ret)
{
printf("sendto ok\n");
}
//5.关闭socket
close(sockfd);
return 0;
}
//Udp通信 主动发送 客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h> /*memset*/
#include <unistd.h> /*close*/
#define IP "127.0.0.1" //本机测试地址
#define CPORT 10010 //客户端端口
#define SPORT 10086 //服务器端口
#define SIZE 100
int main()
{
//1.设置变量
int sockfd = 0;
struct sockaddr_in addr;
int addrlen = 0;
int newID = 0;
char buf[SIZE] = {'\0'};
int ret = 0;
//2.创建socket套接字
//参数1:地址族--->使用网络协议族
//参数2:套接字类型---->使用数据报套接字
//参数3:通常设置为0
sockfd = socket(PF_INET, SOCK_DGRAM,0);
if(0 > sockfd)
{
perror("socket error");
return -1;
}
printf("socket ok\n");
//3.绑定地址(自己)ip+port端口(可以忽略,默认系统设置)
addrlen = sizeof(addr);
memset(&addr,0,addrlen);
addr.sin_family = PF_INET; //地址族--->使用网络协议族
addr.sin_port = htons(CPORT); //htons(主机字节序转换网络字节序函数),short短整型---->设置端口
addr.sin_addr.s_addr = inet_addr(IP); //inet_addr(IP地址的转换),转换成32位的网络字节序二进制值
if(0 > bind(sockfd,(struct sockaddr *)&addr,addrlen))
{
perror("bind error");
close(sockfd); //关闭套接字
return -1;
}
printf("bind ok\n");
//4.1通信----->发送消息
//设置服务器的地址
memset(&addr,0,addrlen);
addr.sin_family = PF_INET; //地址族--->使用网络协议族
addr.sin_port = htons(SPORT); //htons(主机字节序转换网络字节序函数),short短整型---->设置端口
addr.sin_addr.s_addr = inet_addr(IP); //inet_addr(IP地址的转换),转换成32位的网络字节序二
//输入准备发送的消息
strncpy(buf,"下课了!!!",SIZE-1);
ret = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&addr,addrlen);
if(ret > 0)
{
printf("sendto ok\n");
}
//4.2通信----->接收消息
//参数3:通常设置为0
memset(&addr,0,addrlen);
memset(buf,0,SIZE);
ret = recvfrom(sockfd,buf,SIZE-1,0,(struct sockaddr *)&addr,&addrlen);
if(0 < ret)
{
//inet_ntoa---->将32位的网络字节序二进制地址转换成点分十进制的字符串
printf("ip=%s,port=%d\n",(char *)inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
printf("said:%s\n",buf);
}
else
{
printf("recvfrom return:%d\n",ret);
}
//5.关闭socket
close(sockfd);
return 0;
}
?IO模型 多路复用
?? ??? ?阻塞模型 ?? ??? ??? ?没有数据,读阻塞-->当前进程会被移到阻塞队列 --> 不占用cpu ?? ??? ?非阻塞模型 ?? ??? ??? ?没有数据,立即返回-->当前进程需要循环读数据 --> 占用cpu ?? ??? ??? ?int fcntl( int 文件描述符, int 命令, int agr ); ?? ??? ??? ?命令:F_GETFL,F_SETFL
?? ??? ?信号驱动 ?? ??? ??? ?文件状态改变,内核发送信号给应用程序,应用程序收到信号后,处理信号。异步的方式。
?? ??? ?多路复用
?? ??? ??? ?一个进程中有多个阻塞IO的情况。 ?? ??? ??? ?在tcp循环服务器中,每接受一个客户端的连接,就会产生一个已连接的套接字。 ?? ??? ??? ??? ?这个已连接的套接字就是一个阻塞描述符。 ?? ??? ??? ?select原理: ?? ??? ??? ??? ?1. 创建一张存放描述符的表 ?? ??? ??? ??? ?2. 将所有准备监听的阻塞描述符放在表中 ?? ??? ??? ??? ?3. 调用select ?? ??? ??? ??? ?4. //将第一步的表复制一份给内核,内核轮询表中的描述符,看哪些可以进行IO操作。 ?? ??? ??? ??? ?5. //如果所有描述符都不能进行IO操作,内核会阻塞select. ?? ??? ??? ??? ?6. //如果有描述符能进行IO操作,内核会修改表,保留可以进行IO操作描述符,然后通过select将表返回 ?? ??? ??? ??? ?7. 应用程序轮询新表,查看具体哪个描述符可以进行IO操作,然后再去操作。 ?? ??? ??? ??? ?8. 重复2~7直到服务器关闭为止。
//tcp通信的多路复用服务器
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#define IP "127.0.0.1" //本机测试地址
#define SPORT 10086
#define CPORT 10010
int main()
{
int sockfd = 0;
int newID = 0;
int addrLength = 0;
char buf[100] = {0};
struct sockaddr_in addr;
int maxFd = 0;
fd_set readFds;
fd_set tmpFds;
//创建socket
sockfd = socket( PF_INET, SOCK_STREAM, 0 );
if ( sockfd < 0 )
{
perror("socket error");
return -1;
}
printf("socket ok \r\n");
//绑定自己的地址
addrLength = sizeof(addr);
memset(&addr, 0, addrLength );
addr.sin_family = PF_INET;
addr.sin_port = htons( SPORT );
addr.sin_addr.s_addr = inet_addr( IP );
if (0 > bind( sockfd, (struct sockaddr *)&addr, addrLength) )
{
perror("bind error");
close(sockfd);
return -1;
}
printf("bind ok \r\n");
//监听 socket变成被动,只能用于三次握手
if (0 > listen( sockfd, 5 ))
{
perror("listen error");
close(sockfd);
return -1;
}
printf("listen ok \r\n");
//清空readFds
FD_ZERO(&readFds);
//将等连接的套接字放入readFds
FD_SET( sockfd, &readFds );
tmpFds = readFds;
//maxFd是select所有监听描述符中的最大值
maxFd = sockfd;
while(1)
{
//tmpFds保存最完整的被监听的描述符
readFds = tmpFds;
//调用select函数,它是一个阻塞函数
select( maxFd + 1, &readFds, NULL, NULL, NULL );
int i = 0;
//select返回后,应用程序需要轮询内核返回的readFds
for ( i = 0; i < maxFd + 1; i++ )
{
if ( FD_ISSET( i, &readFds ) )
{
if ( i == sockfd ) //等连接socket
{
memset(&addr, 0, addrLength);
//接受连接 -- 成功时,将返回已经连接的套接字
newID = accept( sockfd, (struct sockaddr *)&addr, &addrLength );
if ( newID < 0 )
{
perror("accept error");
close(sockfd);
return -1;
}
printf("accept ok %d\r\n", newID);
printf("client ip=%s, port=%d\r\n", (char *)inet_ntoa(addr.sin_addr), ntohs(addr.sin_port) );
//把newID放到tmpFds里
FD_SET( newID, &tmpFds );
if ( maxFd < newID )
{
maxFd = newID;
}
}
else
{
memset(buf, 0, sizeof(buf));
//通信 接收 发送 使用已经连接的套接字
if ( 0 < recv( i, buf, sizeof(buf) - 1, 0 ))
{
printf("服务器收到:%s\r\n", buf);
}
else
{
printf("nothing,客户端已经关闭\r\n");
close(i);//关闭已经连接的套接字
FD_CLR(i, &tmpFds);
}
if ( 0 < send( i, buf, strlen(buf), 0 ) )
{
printf("回答完毕!\r\n");
}
}
}
}
}
//关闭socket
close( sockfd );
return 0;
}
?? ?服务器
?? ??? ?循环服务器
?? ???
tcp循环服务器
需要将"accept+通信"放在while(1)里 优点:可以接收多个客户端的连接,并且处理通信 缺点:前一个客户端在连接成功后的通信会影响下一个客户端的连接
//TCP通信 服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h> /*memset*/
#include <unistd.h> /*close*/
#define IP "127.0.0.1" //本机测试地址
#define SPORT 10086
#define CPORT 10010
#define SIZE 100
int main()
{
//1.设置变量
int sockfd = 0;
struct sockaddr_in addr;
int addrlen = 0;
int newID = 0;
char buf[SIZE] = {'\0'};
//2.创建socket套接字
//参数1:地址族--->使用网络协议族
//参数2:套接字类型---->使用流式套接字
//参数3:通常设置为0
sockfd = socket(PF_INET, SOCK_STREAM, 0);
if(0 > sockfd)
{
perror("socket error");
return -1;
}
printf("socket ok\n");
//3.绑定地址(自己)ip+port端口
addrlen = sizeof(addr);
memset(&addr,0,addrlen);
addr.sin_family = PF_INET; //地址族--->使用网络协议族
addr.sin_port = htons(SPORT); //htons(主机字节序转换网络字节序函数),short短整型---->设置端口
addr.sin_addr.s_addr = inet_addr(IP); //inet_addr(IP地址的转换),转换成32位的网络字节序二进制值
if(0 > bind(sockfd,(struct sockaddr *)&addr,addrlen))
{
perror("bind error");
close(sockfd); //关闭套接字
return -1;
}
printf("bind ok\n");
//4.建立监听----->socket变为被动,只能用于三次握手
//参数2:监听队列存放三次握手还没有结束的客户端
if(0 > listen(sockfd,5))
{
perror("listen error");
close(sockfd); //关闭套接字
return -1;
}
printf("listen ok\n");
while(1)
{
memset(&addr,0,addrlen);
//5.接受连接------>若成功,返回已经连接的套接字
newID=accept(sockfd, (struct sockaddr *)&addr,&addrlen);
if(0 > newID)
{
perror("accept error");
close(sockfd); //关闭套接字
return -1;
}
printf("accept ok\n");
while(1)
{
//6.通信----->接收 发送 使用已经连接的套接字
//参数3:通常设置为0
//参数2:为什么用不了strlen测buf有效长度----->因为strlen测有效字符,buf前面已经置空为0
memset(buf,0,SIZE);
if(0 < recv(newID, buf, sizeof(buf)-1,0))
{
printf("服务器已收到:%s\n",buf);
if(0 == strncmp(buf,"quit",4))
{
break;
}
}
else
{
printf("nothing\n");
break;
}
if ( 0 < send(newID, buf, strlen(buf),0))
{
printf("回答完毕\n");
}
}
//7.关闭已经连接的套接字
close(newID);
}
//8.关闭socket
close(sockfd);
return 0;
}
??//TCP通信 客户端
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#define IP "127.0.0.1" //本机测试地址
#define SPORT 10086
#define CPORT 10010
#define SIZE 100
int main()
{
//定义变量
int sockfd = 0;
struct sockaddr_in addr;
int addrLength = 0;
char buf[SIZE] = {"终于写完了!"};
//创建socket
sockfd = socket( AF_INET, SOCK_STREAM, 0);
if ( sockfd < 0 )
{
perror("socket error");
return -1;
}
printf("socket ok \r\n");
//绑定自己的地址
addrLength = sizeof(addr);
/*
memset(&addr, 0, addrLength );
addr.sin_family = AF_INET;
addr.sin_port = htons(CPORT);
addr.sin_addr.s_addr = INADDR_ANY;
if (0 > bind( sockfd, (struct sockaddr *)&addr, addrLength) )
{
perror("bind error");
close(sockfd);
return -1;
}
printf("bind ok \r\n");
*/
printf("操作系统默认bind ok \r\n");
//设置服务器的地址,并且发送连接请求
memset(&addr, 0, addrLength );
addr.sin_family = AF_INET;
addr.sin_port = htons(SPORT);
addr.sin_addr.s_addr = inet_addr( IP );
if (0 > connect( sockfd,( struct sockaddr *)&addr, addrLength ))
{
perror("connect error");
close(sockfd);
return -1;
}
printf("connect ok \r\n");
while(1)
{
//通信
fgets( buf, SIZE - 1, stdin );
send( sockfd, buf, strlen(buf),0);
if ( 0 == strncmp( buf, "quit", 4 ) )
{
break;
}
memset(buf, 0, SIZE);
recv( sockfd, buf, SIZE - 1, 0 );
printf("客户端收到:%s\r\n",buf);
}
//关闭套接字
close( sockfd );
return 0;
}
?udp循环服务器 ?需要将"通信"放在while(1)里 ?优点:可以处理多个客户端的通信 ? 缺点:前一个客户端接收消息后,处理消息和回复消息,会影响下一个客户端消息的接收。 --------------------------------------------------------------------------------------------------------------------------------- int open( char * fileName, int flags ); ?? ?默认都是阻塞方式打开? ?? ?open( "1.txt", O_RDONLY | O_NDELAY); ?//非阻塞打开 ?? ?或者open( "1.txt", O_RDONLY | O_NONBLOCK); //非阻塞打开
|