socket套接字的使用
2015-08-27 19:26
316 查看
int socket(int family,int type,int protocol);
功能:创建一个通过tcp/ip协议发送和接收网络数据的Socket
返回值:成功返回Socket描述符,失败<0
参数:family:指明协议族(AF_INET,AF_INET6AF_LOCAL,AF_ROUTE,AF_KEY)
type:指明套接字类型:(SOCKET_STREAM,SOCK_DGREAM,SOCK_SEQPACKET,SOCK_RAW)
protocol: 指明协议类型,0时由family和type一起决定
Linux 内核将Socket描述符和文件进行统一管理,所以可以将Socket看做特殊文件
int close(int sockfd)
功能:关闭套接字
返回值:成功返回0,失败<0
参数:已经发开的套接字描述符
family决定地址类型:
当为AF_LOCAL(相当于AF_UNIX)
struct sockaddr_un
{
sa_family_t sun_family; /* AF_UNIX */
char sun_path(); /* pathname */
}
当为AF_INET时:
struct sockaddr_in
{
short int sin_family ; /* AF_INET */
unsignde short int sin_port ; /* port number */
struct in_addr sin_addr ; /* Internet address */网络字节序
}
struct in_addr
{
unsigned long int s_addr ; //IP地址为 32 位 IPV 4 地址 网络字节序
}
后续Socket为了兼容更多类型,采用一下结构:
struct socket
{
short int sin_family;//协议族
char sa_data[14];//协议制定地址
}
在使用这些函数时候我们可以强制类型转换
主机字节序:某个给定的主机系统所用的字节序,它由cpu决定
网络字节序:网络协议制定的字节序,网络协议使用大端字节序来传送多字节整数
unsigned long int htonl(unsigned long int hostlong)
unsigned short int htons(unsigned short int hostshort)
unsigned long int ntohl(unsigned long int netlong)
unsigned short int ntohs(unsigned short int netshort)
备注:h代表host n代表network s代表short l代表long
常用字节操作:
void *memset(void *dest,int ch,size_t len)
void *memcpy(void *dest,const void *src,size_t nbytes)
int memcmp(const void *ptr1,const void *ptr2,size_t nbytes)
void bzero(void *dest,size_t nbytes)等价于memset(dest,0,nbytes);
十进制与32位地址转换:
int inet_aton(const char *strptr,struct in_addr *addrptr)//字符串有效返回1,否则为0
char *inet_ntoa(struct in_addr inaddr)
注: struct in_addr inaddr 均指网络字节序32位ip地址
inet_ntoa 函数的结果位于静态数据区
域名与地址转换:
struct hostent *gethostbyname(const char *hostname);
struct hostent *gethostbyaddr(const char *addr,size_t len,int family)//addr是struct in_addr
struct hostent 结构:
程序如下:
Socket 读写函数:
ssize_t read(int fd,void *buf,size_t count)//把fd内容读入buf
ssize_t write(int fd,const void *buf,size_t count)//把buf内容写入fd
ssize_t recv(int sockfd,void *buf,size_t nbytes,int flags)
ssize_t send(int sockfd,const void *buff,size_t nbytes,int flags)//send.recv为Socket专用
对于tcp而言,read,recv返回0表示远程socket已关闭
flags一般取0,也可以取以下内容:
tcp连接建立:
三次握手与四次挥手:
tcp连接范式:
客户端范式:
服务器范式:
关键函数:
int connect (int sockfd,const struct sockadr *servaddr,socklen_t addrlen) /* 客户端连接服务器函数 */
功能:通过三路握手建立tcp服务器的连接
返回值:成功为0,失败<0
参数:sockfd: sockfd函数返回的socket描述符
servaddr: 指向与协议族相符的地址结构指针 /* 对于AF_INT协议族,其地址组成为IP地址+端口号,机构为:struct sockaddr_in */
addrlen: 实际地址结构大小
int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen) /* 一般用于服务器自身socket与本地地址绑定 */
功能:将本地协议地址赋予指定的socket描述符
返回值: 成功为0,失败<0
参数: sockfd: sockfd函数返回的sockfd描述符
myaddr: 指向与协议族相同的地址结构指针
addrlen: 实际地址大小
int listen(int sockfd,int backlog) /* 服务器 */
功能:将主动套接字改为被动套接字(用来接受向该套接字的连接请求)并设定套接字排队最大连接个数.
返回值:成功返回0,失败<0
参数: sockfd: 处于未连接的套接字描述符
backlog: 制定管理连接请求的队列长度,一般为5-20,大型服务器则为系统能支持的最大数
内核针对每个被动套接字采用两个队列管理连接请求:
未完成连接队列 and 已完成连接队列
!! 该函数不会阻塞
int accept(int sockfd,struct sockaddr *cliaddr, socklen_t *addrlen) /*服务器 */
功能:从已完成连接队列的队头返回一个已完成连接,空队列则阻塞等待已完成连接
返回值: 成功返回已完成连接对应的socket描述符,服务器通过这个描述符与请求连接的客户端进行数据传输,失败<0,即已连接套接字
参数:
sockfd;监听套接字(即服务器用来处理连接请求的被动套接字)
cliaddr:返回已连接的对端进程(客户端)的协议地址
addrlen:调用前指定cliaddr的地址结构长度,返回后保存对应地址结构的确切字节数
!! 注意区分监听套接字与已连接套接字
不关系客户端地址cliaddr和addrlen 可以设为NULL
本函数会阻塞
未连接UDP 范式:
已连接UDP范式:
ssize_t sendto (int sockfd,const void *buff,size_t nbytes,int flags,const struct sockaddr *to,socklen_t addrlen)
功能:向指定地址发送期望字节数量的数据
返回值:成功返回实际发送的字节数,失败<0
参数:sockfd.buff,nbytes,flags:与send函数一样
to: 指向接收者的协议地址结构指针 (类似于connect对应参数的含义)
addrlen: to 所用的地址结构的字节长度 (类似于connect对应参数含义)
ssize_t recvfrom (int sockfd,void *buff,size_t bytes,int flags,struct sockaddr *from, socklen_t *addrlen)
功能:从指定的udp socket里面接收数据,同时填充发送发的协议地址结构
返回值:成功返回接收到的字节数,失败<0
参数:sockfd,buff,nbytes,flags 含义同recv函数
from: 指向发送发的协议地址结构指针,类似于accept对应的参数含义,用于保存发送发地址,便于通过sendto回应对方相应数据.
addrlen :from所用的地址结构的字节长度,类似于accept对应参数的含义,指出实际使用的地址结构长度。
返回0是可接受的正确的结果,并非表示socket关闭,意味着可以发送空的UDP数据报(只有UDP首部而没有数据部分)给远端进程。
from和addrlen可以同时为NULL,表示不关心发送方地址信息。
未连接UDP socket不足之处
知道客户端临时端口和地址的任何进程都可以向其发送UDP包,客户端需要区分哪些包是自己期望的包(比如可以通过recvfrom的最后两个参数与期望服务器的协议地址比较来区分)
如果服务器进程未启动,客户端将永久阻塞在recvfrom,无法获取相关错误信息。内核在发送UDP数据前有一个使用ARP协议验证服务器地址的过程,这个过程如果发现错误,内核却无法将此错误返回给sendto,除非这个UDP是一个已连接的UDP
非阻塞式I/O 实现函数:
fcntl(int fd,int cmd,long arg)
int flag;
flag=fcntl(sockfd,F_GETFL,0);
flag |= O_NONBLOCK;
fcntl(sockfd,F_SETFL,flag);
多路复用:
int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);
select()用来等待文件描述词状态的改变。参数n代表最大的文件描述词加1,参数readfds、writefds 和exceptfds 称为描述词组,是用来回传该描述词的读,写或例外的状况。底下的宏提供了处理这三种描述词组的方式:
FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位
FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真
FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位
FD_ZERO(fd_set *set);用来清除描述词组set的全部位
getsockname/getpeername
getsockope /setsockopt
代码分析:
多进程进行通信:
客户端:
服务器:
使用select,数组存储套接字描述符,然后循环执行:
客户端:
服务器:
多线程执行:
客户端:
服务器:
功能:创建一个通过tcp/ip协议发送和接收网络数据的Socket
返回值:成功返回Socket描述符,失败<0
参数:family:指明协议族(AF_INET,AF_INET6AF_LOCAL,AF_ROUTE,AF_KEY)
type:指明套接字类型:(SOCKET_STREAM,SOCK_DGREAM,SOCK_SEQPACKET,SOCK_RAW)
protocol: 指明协议类型,0时由family和type一起决定
Linux 内核将Socket描述符和文件进行统一管理,所以可以将Socket看做特殊文件
int close(int sockfd)
功能:关闭套接字
返回值:成功返回0,失败<0
参数:已经发开的套接字描述符
family决定地址类型:
当为AF_LOCAL(相当于AF_UNIX)
struct sockaddr_un
{
sa_family_t sun_family; /* AF_UNIX */
char sun_path(); /* pathname */
}
当为AF_INET时:
struct sockaddr_in
{
short int sin_family ; /* AF_INET */
unsignde short int sin_port ; /* port number */
struct in_addr sin_addr ; /* Internet address */网络字节序
}
struct in_addr
{
unsigned long int s_addr ; //IP地址为 32 位 IPV 4 地址 网络字节序
}
后续Socket为了兼容更多类型,采用一下结构:
struct socket
{
short int sin_family;//协议族
char sa_data[14];//协议制定地址
}
在使用这些函数时候我们可以强制类型转换
主机字节序:某个给定的主机系统所用的字节序,它由cpu决定
网络字节序:网络协议制定的字节序,网络协议使用大端字节序来传送多字节整数
unsigned long int htonl(unsigned long int hostlong)
unsigned short int htons(unsigned short int hostshort)
unsigned long int ntohl(unsigned long int netlong)
unsigned short int ntohs(unsigned short int netshort)
备注:h代表host n代表network s代表short l代表long
常用字节操作:
void *memset(void *dest,int ch,size_t len)
void *memcpy(void *dest,const void *src,size_t nbytes)
int memcmp(const void *ptr1,const void *ptr2,size_t nbytes)
void bzero(void *dest,size_t nbytes)等价于memset(dest,0,nbytes);
十进制与32位地址转换:
int inet_aton(const char *strptr,struct in_addr *addrptr)//字符串有效返回1,否则为0
char *inet_ntoa(struct in_addr inaddr)
注: struct in_addr inaddr 均指网络字节序32位ip地址
inet_ntoa 函数的结果位于静态数据区
域名与地址转换:
struct hostent *gethostbyname(const char *hostname);
struct hostent *gethostbyaddr(const char *addr,size_t len,int family)//addr是struct in_addr
struct hostent 结构:
程序如下:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { struct hostent * phost = NULL; struct in_addr *pinaddr = NULL; struct in_addr **ppinaddr = NULL; char *ip = NULL; char **ppaliases = NULL; phost = gethostbyname("www.sina.com.cn"); printf("phost=%p\n",phost); ppinaddr = (struct in_addr **)phost->h_addr_list; while(*ppinaddr != NULL) { pinaddr = *ppinaddr; ip = inet_ntoa(*pinaddr); printf("ip=%s\n",ip); ppinaddr++; } printf("h_name=%s\n",phost->h_name); ppaliases = phost->h_aliases; while(*ppaliases != NULL) { printf("aliases = %s\n",*ppaliases); ppaliases++; } phost = gethostbyname("www.sohu.com"); printf("phost=%p\n",phost); ppinaddr = (struct in_addr **)phost->h_addr_list; while(*ppinaddr != NULL) { pinaddr = *ppinaddr; ip = inet_ntoa(*pinaddr); printf("ip=%s\n",ip); ppinaddr++; } printf("h_name=%s\n",phost->h_name); ppaliases = phost->h_aliases; while(*ppaliases != NULL) { printf("aliases = %s\n",*ppaliases); ppaliases++; } return 0; }
Socket 读写函数:
ssize_t read(int fd,void *buf,size_t count)//把fd内容读入buf
ssize_t write(int fd,const void *buf,size_t count)//把buf内容写入fd
ssize_t recv(int sockfd,void *buf,size_t nbytes,int flags)
ssize_t send(int sockfd,const void *buff,size_t nbytes,int flags)//send.recv为Socket专用
对于tcp而言,read,recv返回0表示远程socket已关闭
flags一般取0,也可以取以下内容:
tcp连接建立:
三次握手与四次挥手:
tcp连接范式:
客户端范式:
服务器范式:
关键函数:
int connect (int sockfd,const struct sockadr *servaddr,socklen_t addrlen) /* 客户端连接服务器函数 */
功能:通过三路握手建立tcp服务器的连接
返回值:成功为0,失败<0
参数:sockfd: sockfd函数返回的socket描述符
servaddr: 指向与协议族相符的地址结构指针 /* 对于AF_INT协议族,其地址组成为IP地址+端口号,机构为:struct sockaddr_in */
addrlen: 实际地址结构大小
int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen) /* 一般用于服务器自身socket与本地地址绑定 */
功能:将本地协议地址赋予指定的socket描述符
返回值: 成功为0,失败<0
参数: sockfd: sockfd函数返回的sockfd描述符
myaddr: 指向与协议族相同的地址结构指针
addrlen: 实际地址大小
int listen(int sockfd,int backlog) /* 服务器 */
功能:将主动套接字改为被动套接字(用来接受向该套接字的连接请求)并设定套接字排队最大连接个数.
返回值:成功返回0,失败<0
参数: sockfd: 处于未连接的套接字描述符
backlog: 制定管理连接请求的队列长度,一般为5-20,大型服务器则为系统能支持的最大数
内核针对每个被动套接字采用两个队列管理连接请求:
未完成连接队列 and 已完成连接队列
!! 该函数不会阻塞
int accept(int sockfd,struct sockaddr *cliaddr, socklen_t *addrlen) /*服务器 */
功能:从已完成连接队列的队头返回一个已完成连接,空队列则阻塞等待已完成连接
返回值: 成功返回已完成连接对应的socket描述符,服务器通过这个描述符与请求连接的客户端进行数据传输,失败<0,即已连接套接字
参数:
sockfd;监听套接字(即服务器用来处理连接请求的被动套接字)
cliaddr:返回已连接的对端进程(客户端)的协议地址
addrlen:调用前指定cliaddr的地址结构长度,返回后保存对应地址结构的确切字节数
!! 注意区分监听套接字与已连接套接字
不关系客户端地址cliaddr和addrlen 可以设为NULL
本函数会阻塞
未连接UDP 范式:
已连接UDP范式:
ssize_t sendto (int sockfd,const void *buff,size_t nbytes,int flags,const struct sockaddr *to,socklen_t addrlen)
功能:向指定地址发送期望字节数量的数据
返回值:成功返回实际发送的字节数,失败<0
参数:sockfd.buff,nbytes,flags:与send函数一样
to: 指向接收者的协议地址结构指针 (类似于connect对应参数的含义)
addrlen: to 所用的地址结构的字节长度 (类似于connect对应参数含义)
ssize_t recvfrom (int sockfd,void *buff,size_t bytes,int flags,struct sockaddr *from, socklen_t *addrlen)
功能:从指定的udp socket里面接收数据,同时填充发送发的协议地址结构
返回值:成功返回接收到的字节数,失败<0
参数:sockfd,buff,nbytes,flags 含义同recv函数
from: 指向发送发的协议地址结构指针,类似于accept对应的参数含义,用于保存发送发地址,便于通过sendto回应对方相应数据.
addrlen :from所用的地址结构的字节长度,类似于accept对应参数的含义,指出实际使用的地址结构长度。
返回0是可接受的正确的结果,并非表示socket关闭,意味着可以发送空的UDP数据报(只有UDP首部而没有数据部分)给远端进程。
from和addrlen可以同时为NULL,表示不关心发送方地址信息。
未连接UDP socket不足之处
知道客户端临时端口和地址的任何进程都可以向其发送UDP包,客户端需要区分哪些包是自己期望的包(比如可以通过recvfrom的最后两个参数与期望服务器的协议地址比较来区分)
如果服务器进程未启动,客户端将永久阻塞在recvfrom,无法获取相关错误信息。内核在发送UDP数据前有一个使用ARP协议验证服务器地址的过程,这个过程如果发现错误,内核却无法将此错误返回给sendto,除非这个UDP是一个已连接的UDP
非阻塞式I/O 实现函数:
fcntl(int fd,int cmd,long arg)
int flag;
flag=fcntl(sockfd,F_GETFL,0);
flag |= O_NONBLOCK;
fcntl(sockfd,F_SETFL,flag);
多路复用:
int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);
select()用来等待文件描述词状态的改变。参数n代表最大的文件描述词加1,参数readfds、writefds 和exceptfds 称为描述词组,是用来回传该描述词的读,写或例外的状况。底下的宏提供了处理这三种描述词组的方式:
FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位
FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真
FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位
FD_ZERO(fd_set *set);用来清除描述词组set的全部位
getsockname/getpeername
getsockope /setsockopt
代码分析:
多进程进行通信:
客户端:
skywalker@skywalker:~/work/network/multiprocess$ cat client.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <strings.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/select.h> #include <sys/time.h> #include <stdlib.h> int main(int argc, char *argv[]) { int sockfd; int stdinfd = 0; fd_set rset; struct sockaddr_in servaddr; int ret; sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立套接字 bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5678); inet_aton("127.0.0.1", &servaddr.sin_addr); connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));//connect服务器,sockaddr_in 强制转换为aockaddr while(1) { FD_ZERO(&rset); FD_SET(stdinfd,&rset); FD_SET(sockfd,&rset); ret = select(sockfd + 1,&rset,NULL,NULL,NULL);//多路复用 if(ret <= 0) { if(errno == EINTR) { continue; } else { break; } } if(FD_ISSET(stdinfd,&rset))//终端输入 { char buf[20] = {'\0'}; int len = 0; int isexit = 0; do { bzero(buf,20); fgets(buf,20,stdin);//fgets函数读不玩的内容下次循环在缓冲器继续读取,fgts默认尾部加'\0' if(strcmp(buf,"quit\n") == 0) { isexit = 1; break; } len = strlen(buf); if(buf[len - 1] == '\n') { buf[len - 1] = '\0'; write(sockfd,buf,len); break; } write(sockfd,buf,len); }while(buf[18] != '\0'); if(isexit) { break; } } if(FD_ISSET(sockfd,&rset))//接收数据 { int len; char buf[20] = {'\0'}; write(1,"Client Say:",strlen("Client Say:")); len = read(sockfd,buf,20); if(len == 0) { break; } write(1,buf,len); while(len == 20 && buf[19] != '\0') { len = read(sockfd,buf,20); write(1,buf,len); } write(1,"\n",1); } } close(sockfd); exit(0); }
服务器:
skywalker@skywalker:~/work/network/multiprocess$ cat server.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <strings.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/select.h> #include <sys/time.h> #include <sys/types.h> #include <sys/wait.h> void HandleClient(int fd); int main(int argc, char **argv) { int listenfd, connfd; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; int pid = -1; listenfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; inet_aton("127.0.0.1", &(servaddr.sin_addr)); servaddr.sin_port = htons(5678); bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));//套接字与主机绑定 listen(listenfd, 5); while(1)//每来一个客户端创建其子进程的子进程 { connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen);//等待接收客户端 if(connfd > 0) { pid = fork(); if(pid < 0) { printf("fork failed!!!\n"); close(connfd); } else if(pid == 0) { pid = fork(); if(pid < 0) { printf("fork failed!!!2\n"); close(connfd); } else if(pid == 0) { close(listenfd); HandleClient(connfd); exit(0); } else { close(connfd); close(listenfd); exit(0); } } else { close(connfd); waitpid(pid,NULL,0); } } } close(listenfd); return 0; } void HandleClient(int fd) { int len; char buf[20]; while(1) { len = read(fd,buf,20); if(len == 0) { close(fd); break; } write(1,"Server Say:",strlen("Server Say:")); write(1,buf,len); write(fd,buf,len); while(len == 20 && buf[19] != '\0') { len = read(fd,buf,20); write(1,buf,len); write(fd,buf,len); } write(1,"\n",1); } }
使用select,数组存储套接字描述符,然后循环执行:
客户端:
skywalker@skywalker:~/work/network/selectecho$ cat client.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <strings.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/select.h> #include <sys/time.h> #include <stdlib.h> int main(int argc, char *argv[]) { int sockfd; int stdinfd = 0; fd_set rset; struct sockaddr_in servaddr; int ret; sockfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5678); inet_aton("127.0.0.1", &servaddr.sin_addr); connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); while(1) { FD_ZERO(&rset); FD_SET(stdinfd,&rset); FD_SET(sockfd,&rset); ret = select(sockfd + 1,&rset,NULL,NULL,NULL); if(ret <= 0) { if(errno == EINTR) { continue; } else { break; } } if(FD_ISSET(stdinfd,&rset)) { char buf[20] = {'\0'}; int len = 0; int isexit = 0; do { bzero(buf,20); fgets(buf,20,stdin); if(strcmp(buf,"quit\n") == 0) { isexit = 1; break; } len = strlen(buf); if(buf[len - 1] == '\n') { buf[len - 1] = '\0'; write(sockfd,buf,len); break; } write(sockfd,buf,len); }while(buf[18] != '\0'); if(isexit) { break; } } if(FD_ISSET(sockfd,&rset)) { int len; char buf[20] = {'\0'}; write(1,"Client Say:",strlen("Client Say:")); len = read(sockfd,buf,20); if(len == 0) { break; } write(1,buf,len); while(len == 20 && buf[19] != '\0') { len = read(sockfd,buf,20); write(1,buf,len); } write(1,"\n",1); } } close(sockfd); exit(0); }
服务器:
skywalker@skywalker:~/work/network/selectecho$ cat server.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <strings.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/select.h> #include <sys/time.h> int getMaxFd(int * pint,int size); void addFd(int * pint,int size,int fd); void setAllFd(int * pint,int size,fd_set * fdset); int main(int argc, char **argv) { int listenfd, connfd; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; char buf[20] = {'\0'}; fd_set rset; int maxfd; int ret; int clientfd[64] = {0};//用来存连接服务器的套接字 int i = 0; listenfd = socket(AF_INET, SOCK_STREAM, 0); memset(clientfd,0xff,sizeof(clientfd)); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; inet_aton("127.0.0.1", &(servaddr.sin_addr)); servaddr.sin_port = htons(5678); bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); listen(listenfd, 5); maxfd = listenfd; for ( ; ; ) { FD_ZERO(&rset); FD_SET(listenfd,&rset); setAllFd(clientfd,64,&rset); maxfd = getMaxFd(clientfd,64); if(maxfd < listenfd) { maxfd = listenfd; } ret = select(maxfd+1,&rset,NULL,NULL,NULL); if(ret <= 0) { if(errno == EINTR) { continue; } else { break; } } if(FD_ISSET(listenfd,&rset)) { clilen = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen);//进行连接 addFd(clientfd,64,connfd);//将此套接字加入到执行队列 } for(i = 0; i < 64;i++)//对连接函数进行依次执行 { if(clientfd[i] > 0) { if(FD_ISSET(clientfd[i],&rset)) { int len; write(1,"Server Say:",strlen("Server Say:")); len = read(clientfd[i],buf,20); if(len == 0) { close(clientfd[i]); clientfd[i] = -1; continue; } write(1,buf,len); write(clientfd[i],buf,len); while(len == 20 && buf[19] != '\0') { len = read(clientfd[i],buf,20); write(1,buf,len); write(clientfd[i],buf,len); } write(1,"\n",1); } } } } close(listenfd); return 0; } int getMaxFd(int * pint,int size) { int max = pint[0]; int i = 0; for(i=1;i < size;i++) { if(max < pint[i]) { max = pint[i]; } } return max; } void addFd(int * pint,int size,int fd) { int i = 0; for(i = 0; i < size;i++) { if(-1 == pint[i]) { pint[i] = fd; break; } } } void setAllFd(int * pint,int size,fd_set * fdset) { int i = 0; for(i = 0; i < size;i++) { if(-1 != pint[i]) { FD_SET(pint[i],fdset); } } }
多线程执行:
客户端:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <strings.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/select.h> #include <sys/time.h> #include <stdlib.h> int main(int argc, char *argv[]) { int sockfd; int stdinfd = 0; fd_set rset; struct sockaddr_in servaddr; int ret; sockfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5678); inet_aton("127.0.0.1", &servaddr.sin_addr); connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); while(1) { FD_ZERO(&rset); FD_SET(stdinfd,&rset); FD_SET(sockfd,&rset); ret = select(sockfd + 1,&rset,NULL,NULL,NULL); if(ret <= 0) { if(errno == EINTR) { continue; } else { break; } } if(FD_ISSET(stdinfd,&rset)) { char buf[20] = {'\0'}; int len = 0; int isexit = 0; do { bzero(buf,20); fgets(buf,20,stdin); if(strcmp(buf,"quit\n") == 0) { isexit = 1; break; } len = strlen(buf); if(buf[len - 1] == '\n') { buf[len - 1] = '\0'; write(sockfd,buf,len); break; } write(sockfd,buf,len); }while(buf[18] != '\0'); if(isexit) { break; } } if(FD_ISSET(sockfd,&rset)) { int len; char buf[20] = {'\0'}; write(1,"Client Say:",strlen("Client Say:")); len = read(sockfd,buf,20); if(len == 0) { break; } write(1,buf,len); while(len == 20 && buf[19] != '\0') { len = read(sockfd,buf,20); write(1,buf,len); } write(1,"\n",1); } } close(sockfd); exit(0); }
服务器:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <strings.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/select.h> #include <sys/time.h> #include <sys/types.h> #include <sys/wait.h> #include <pthread.h> void *HandleClient(void *arg); int main(int argc, char **argv) { int listenfd, connfd; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; //int pid = -1; listenfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; inet_aton("127.0.0.1", &(servaddr.sin_addr)); servaddr.sin_port = htons(5678); bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); listen(listenfd, 5); while(1) { connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen); if(connfd > 0) { int err = 0; pthread_t tid; pthread_attr_t attr; err = pthread_attr_init(&attr); err += pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); err += pthread_create(&tid, &attr,HandleClient, (void *)connfd); pthread_attr_destroy(&attr); } } close(listenfd); return 0; } void *HandleClient(void *arg) { int len; int fd = (int)arg; char buf[20]; while(1) { len = read(fd,buf,20); if(len == 0) { close(fd); break; } write(1,"Server Say:",strlen("Server Say:")); write(1,buf,len); write(fd,buf,len); while(len == 20 && buf[19] != '\0') { len = read(fd,buf,20); write(1,buf,len); write(fd,buf,len); } write(1,"\n",1); } pthread_exit(NULL); }
相关文章推荐
- 关于ExpandableListView.setOnChildClickListener获取不到点击事件
- struct的使用
- 【转】在CentOS上安装tomcat
- Cocos2dx学习笔记7:精灵(Sprite)
- React学习笔记(4)---react属性与状态
- 播放视频
- Java LinqCollection 仿Linq的list常用函数
- 各种页面高度
- linux中root密码忘了怎么办?
- NOIP 2014 day1第二题 联合权值
- Metasploit(三)--Msfcli命令
- PIC16F883单片机EUSART串口通信
- hdu 5311 Hidden String (dfs)
- Cheering up the Cows,2008nov,usaco
- javascript float转int
- Vector Quantization(学习Free Mind知识整理)
- Struts2笔记——ONGL表达式语言
- Struts2笔记——ONGL表达式语言
- GCD和NSOperation多线程技术
- 【转】CentOS上安装 jdk:rpm安装和源码安装