您的位置:首页 > 其它

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 结构:





程序如下:

#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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: