Sockets 如何将select()函数用于TCP和;UDP连接?
我有一个功能服务器代码,只有TCP连接。现在我希望服务器从UDP连接接收数据。我将端口2000用于TCP,端口2001用于UDP。下面是我的代码片段Sockets 如何将select()函数用于TCP和;UDP连接?,sockets,tcp,udp,Sockets,Tcp,Udp,我有一个功能服务器代码,只有TCP连接。现在我希望服务器从UDP连接接收数据。我将端口2000用于TCP,端口2001用于UDP。下面是我的代码片段 struct timeval timeout; // timeout for select(), 1ms timeout.tv_sec = 0; timeout.tv_usec = 1000; fd_set master; // master file descriptor list fd_set read
struct timeval timeout; // timeout for select(), 1ms
timeout.tv_sec = 0;
timeout.tv_usec = 1000;
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int fdmax; // maximum file descriptor number
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
// TCP port setup
int sockfd; // listening socket descriptor
int newsockfd; // newly accept()ed socket descriptor
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf_tcp[256]; // buffer for client data
char buf_copy_tcp[256];
int recv_bytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1; // for setsockopt() SO_REUSEADDR
int i, k, rv_getaddrinfo, rv_setsockopt, rv_bind, rv_listen, rv_select;
struct addrinfo hints, *servinfo, *ptr;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
rv_getaddrinfo = getaddrinfo(NULL, "2000", &hints, &servinfo);
for(ptr=servinfo; ptr!=NULL; ptr=ptr->ai_next)
{
sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
rv_setsockopt = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
rv_bind = bind(sockfd, ptr->ai_addr, ptr->ai_addrlen);
break;
}
if (ptr == NULL)
{
fprintf(stderr, "CLI Server error: failed to bind\n\r");
exit(2);
}
freeaddrinfo(servinfo); // all done with this
rv_listen = listen(sockfd, 10);
////////////////////////////////////////////////////////////////////////////////////////////////
// UDP port setup
int sockfd_udp; // listening socket descriptor
struct sockaddr_storage remoteaddr_udp; // client address
socklen_t addrlen_udp;
char buf_udp[256]; // buffer for client data
char buf_copy_udp[256];
int recv_bytes_udp;
char remoteIP_udp[INET6_ADDRSTRLEN];
int yes_udp=1; // for setsockopt() SO_REUSEADDR
int j, rv_getaddrinfo_udp, rv_setsockopt_udp, rv_bind_udp;
struct addrinfo hints_udp, *servinfo_udp, *ptr_udp;
memset(&hints_udp, 0, sizeof(hints_udp));
hints_udp.ai_family = AF_UNSPEC;
hints_udp.ai_socktype = SOCK_DGRAM;
hints_udp.ai_flags = AI_PASSIVE;
rv_getaddrinfo_udp = getaddrinfo(NULL, "2001", &hints_udp, &servinfo_udp);
for(ptr_udp=servinfo_udp; ptr_udp!=NULL; ptr_udp=ptr_udp->ai_next)
{
sockfd_udp = socket(ptr_udp->ai_family, ptr_udp->ai_socktype, ptr_udp->ai_protocol);
rv_setsockopt_udp = setsockopt(sockfd_udp, SOL_SOCKET, SO_REUSEADDR, &yes_udp, sizeof(int));
rv_bind_udp = bind(sockfd_udp, ptr_udp->ai_addr, ptr_udp->ai_addrlen);
break;
}
if (ptr_udp == NULL)
{
fprintf(stderr, "CLI UDP Server error: failed to bind\n\r");
exit(2);
}
freeaddrinfo(servinfo_udp); // all done with this
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// add the listener to the master set
FD_SET(sockfd, &master);
FD_SET(sockfd_udp, &master);
// keep track of the biggest file descriptor
if(sockfd > sockfd_udp)
fdmax = sockfd; // so far, it's this one
else
fdmax = sockfd_udp; // so far, it's this one
do
{
read_fds = master; // copy it
rv_select = select(fdmax+1, &read_fds, NULL, NULL, &timeout);
// run through the existing connections looking for data to read
for(i=0; i<=fdmax; i++)
{
if (FD_ISSET(i, &read_fds))
{ // we got one!!
if (i == sockfd)
{
// handle new connections
addrlen = sizeof(remoteaddr);
newsockfd = accept(sockfd, (struct sockaddr *)&remoteaddr, &addrlen);
FD_SET(newsockfd, &master); // add to master set
if (newsockfd > fdmax) // keep track of the max
fdmax = newsockfd;
inet_ntop(remoteaddr.ss_family, get_in_addr((struct sockaddr*)&remoteaddr), remoteIP, INET6_ADDRSTRLEN);
fprintf(stdout, "CLI Server: new connection from %s on socket %d\n\r", remoteIP, newsockfd);
}
else if (i == sockfd_udp)
{
// handle new udp connections
addrlen_udp = sizeof(remoteaddr_udp);
recv_bytes_udp = recvfrom(i, buf_udp, sizeof(buf_udp), 0, (struct sockaddr *)&remoteaddr_udp, &addrlen_udp);
inet_ntop(remoteaddr_udp.ss_family, get_in_addr((struct sockaddr*)&remoteaddr_udp), remoteIP_udp, INET6_ADDRSTRLEN);
for(j=0; j<=recv_bytes_udp; j++)
{
if( (buf_udp[k] == '\r') | (buf_udp[k] == '\n') )
buf_udp[k] = '\0';
}
fprintf(stdout, "CLI UDP Server: received %s from connection %s\n\r", buf_udp, remoteIP_udp);
}
else
{ // handle data from a client
if ((recv_bytes = recv(i, buf_tcp, sizeof(buf_tcp), 0)) <= 0)
{ // got error or connection closed by client
if (recv_bytes == 0) // connection closed
{
fprintf(stdout, "CLI Server: socket %d hung up\n\r", i);
}
else
{
perror("CLI Server error: recv");
exit(6);
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
}
else
{
for(k=0; k<=recv_bytes; k++)
{
if( (buf_tcp[k] == '\r') | (buf_tcp[k] == '\n') )
buf_tcp[k] = '\0';
}
fprintf(stdout, "CLI Server: received %s from socket %d\n\r", buf_tcp, i);
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} while(QUIT);
struct timeval timeout;//select()的超时,1ms
timeout.tv_sec=0;
timeout.tv_usec=1000;
fd_集主控;//主文件描述符列表
fd_set read_fds;//select()的临时文件描述符列表
int fdmax;//最大文件描述符数
FD_ZERO(&master);//清除主设置和临时设置
FD_零(&read_fds);
//TCP端口设置
int sockfd;//侦听套接字描述符
int newsockfd;//新添加的套接字描述符
struct sockaddru storage remoteaddr;//客户地址
索克伦·阿德伦;
char buf_tcp[256];//客户端数据缓冲区
char buf_copy_tcp[256];
int recv_字节;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1;//对于setsockopt(),请重新使用readdr
int i,k,rv_getaddrinfo,rv_setsockopt,rv_bind,rv_listen,rv_select;
结构addrinfo提示,*servinfo,*ptr;
memset(&hints,0,sizeof(hints));
hits.ai_family=AF_unsec;
hits.ai_socktype=SOCK_流;
hits.ai_flags=ai_被动;
rv_getaddrinfo=getaddrinfo(NULL,“2000”&提示和服务信息);
for(ptr=servinfo;ptr!=NULL;ptr=ptr->ai_next)
{
sockfd=socket(ptr->ai_系列,ptr->ai_socktype,ptr->ai_协议);
rv_setsockopt=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int));
rv_bind=bind(sockfd,ptr->ai_addr,ptr->ai_addrlen);
打破
}
如果(ptr==NULL)
{
fprintf(stderr,“CLI服务器错误:绑定失败\n\r”);
出口(2);
}
freeaddrinfo(servinfo);//一切都结束了
rv_listen=listen(sockfd,10);
////////////////////////////////////////////////////////////////////////////////////////////////
//UDP端口设置
int sockfd_udp;//侦听套接字描述符
结构sockaddr\u存储远程地址udp;//客户地址
socklen_t addrlen_udp;
字符buf_udp[256];//客户端数据缓冲区
char buf_copy_udp[256];
int recv_bytes_udp;
char remoteIP_udp[INET6_ADDRSTRLEN];
int yes_udp=1;//对于setsockopt(),请重新使用readdr
int j,rv_getaddrinfo_udp,rv_setsockopt_udp,rv_bind_udp;
struct addrinfo hints_udp、*servinfo_udp、*ptr_udp;
memset(&hints_udp,0,sizeof(hints_udp));
提示_udp.ai_family=AF_unsec;
提示_udp.ai_socktype=SOCK_DGRAM;
hints_udp.ai_flags=ai_PASSIVE;
rv_getaddrinfo_udp=getaddrinfo(NULL,“2001”&提示_udp&服务信息_udp);
对于(ptr_udp=servinfo_udp;ptr_udp!=NULL;ptr_udp=ptr_udp->ai_next)
{
sockfd_udp=socket(ptr_udp->ai_族,ptr_udp->ai_socktype,ptr_udp->ai_协议);
rv_setsockopt_udp=setsockopt(sockfd_udp,SOL_SOCKET,SO_REUSEADDR,&yes_udp,sizeof(int));
rv_bind_udp=bind(sockfd_udp,ptr_udp->ai_addr,ptr_udp->ai_addrlen);
打破
}
如果(ptr_udp==NULL)
{
fprintf(stderr,“CLI UDP服务器错误:绑定失败\n\r”);
出口(2);
}
freeaddrinfo(servinfo_udp);//一切都结束了
//////////////////////////////////////////////////////////////////////////////////////////////////////////
//将侦听器添加到主集
FD_集(sockfd和master);
FD_集(sockfd_udp和master);
//跟踪最大的文件描述符
如果(sockfd>sockfd_udp)
fdmax=sockfd;//到目前为止,就是这个
其他的
fdmax=sockfd_udp;//到目前为止,就是这个
做
{
read_fds=master;//复制它
rv_select=select(fdmax+1和read_fds、NULL、NULL和超时);
//运行现有连接,查找要读取的数据
对于(i=0;i fdmax)//跟踪max
fdmax=newsockfd;
inet_ntop(remoteaddr.ss_系列,get_in_addr((struct sockaddr*)和remoteaddr),remoteIP,INET6_ADDRSTRLEN);
fprintf(标准输出,“CLI服务器:来自套接字%d上%s的新连接\n\r”,remoteIP,newsockfd);
}
else if(i==sockfd_udp)
{
//处理新的udp连接
addrlen_udp=sizeof(remoteaddr_udp);
recv_bytes_udp=recvfrom(i,buf_udp,sizeof(buf_udp),0,(struct sockaddr*)和remoteaddr_udp,&addrlen_udp);
inet_ntop(remoteaddr_udp.ss_系列、get_in_addr((struct sockaddr*)和remoteaddr_udp)、remoteIP_udp、INET6_ADDRSTRLEN);
对于(j=0;j,我上面写的程序是正确的,但我对套接字编程的理解是错误的。我侥幸写下了正确的代码,但感谢@EJP在评论中进行了扩展讨论,澄清了我的疑问
我的错误是使用Teraterm的TCP客户端连接到UDP服务器。这两种通信是互斥的,因此无法相互通信。因此我必须使用UDP客户端。Netcat使用Netcat-u
提供了UDP客户端选项。然后,我的UDP服务器能够从UDP客户端接收消息
另一个错误是在数据报套接字中将bind()与connect()混淆。连接的DGRAM是指在服务器和客户端上使用connect时
我以为问题出在select()上,因为我错误地认为UDP和TCP套接字不能在select()中同时使用。但上面的代码是如何为多个客户端编写UDP/TCP服务器的
再次感谢Beej和EJP