免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1116 | 回复: 0
打印 上一主题 下一主题

Beej的socket教程学习笔记 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-08-23 14:45 |只看该作者 |倒序浏览

                首先熟悉数据结构
struct sockaddr {    unsigned short    sa_family;    // address family, AF_xxx    char              sa_data[14];  // 14 bytes of protocol address};
为了方便使用,建立了同上基本类同大小相同的结构,sa_family=sin_family, sin_zero[8]是为了补齐,就是为了转换方便:
struct sockaddr_in {    short int          sin_family;  // Address family    unsigned short int sin_port;    // Port number    struct in_addr     sin_addr;    // Internet address    unsigned char      sin_zero[8]; // Same size as struct sockaddr}; struct in_addr {    unsigned long s_addr; // that's a 32-bit long, or 4 bytes};
inet_addr()返回网络字节序,错误时返回-1。所以对于广播地址,255.255.255.255返回也是-1。要注意检查。
int inet_aton(const char *cp, struct in_addr *inp)通过参数返回网络字节序,错误时返回0。但是并不是所有的socket都有实现。
inet_ntoa()返回一个指向静态字符串的指针,所以每一次调用它都会覆盖上一次调用的结果,如果要保存调用strcpy()。它的参数是struct in_addr而不是long。
常用函数:
int socket(int domain, int type, int protocol);创建一个socket并返回(出错返回-1),参数1为PF_INET,参数2为类型SOCK_STREAM, SOCK_DGRAM,参数3通常为0,根据type自动选择。
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);把socket和struct sockaddr结构绑定,也就是和ip和端口绑定。addrlen是sizeof(struct sockaddr)。出错返回-1。
my_addr.sin_addr.s_addr = INADDR_ANY定义为自动获取进程运行所在机器的IP。
对于重启server时出现address already in use的错误,因为先前的socket还处于连接状态,占用原有端口。等待或者主动清除这种情况:通过setsockopt函数
int yes=1;
//char yes='1'; // Solaris people use this
if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
    perror("setsockopt");
    exit(1);
}
其实很多时候不需要调用bind。当你连接远程机器时,调用connect(),会察看socket是否绑定,如果需要会同一个未使用的端口绑定。
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);参数2包含远程机器的端口和IP。出错返回-1。
int listen(int sockfd, int backlog);第二个参数表示incoming队列中允许的连接数。incoming队列中的元素是什么?incoming队列存放在哪里?出错返回-1。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);第二个参数为远端用户的socket信息。
int send(int sockfd, const void *msg, int len, int flags); 以stream形式发送数据。返回实际发送的数据长度,如果小于第三个参数,是否重发取决于程序员。如果包很小,小于1k,很可能一次发送。
int recv(int sockfd, void *buf, int len, unsigned int flags);为什么第四个参数是无符号?返回实际接收到的数据长度,错误返回-1。如果返回0,表示远端关闭了连接,这就是返回0的作用。
int sendto(int sockfd, const void *msg, int len, unsigned int flags,
           const struct sockaddr *to, socklen_t tolen);
以datagram的形式发送数据。返回实际发送的数据长度。
int recvfrom(int sockfd, void *buf, int len, unsigned int flags,
             struct sockaddr *from, int *fromlen);
以datagram的形式接收数据。返回实际接收的数据长度。
连接和接收必须是相同类型的socket。对于datagram的socket,也可以使用send()和recv()来进行传输。
close(sockfd); 关闭读写。
int shutdown(int sockfd, int how);根据第二个参数,0表示关闭接收,1表示关闭发送,2表示都关闭同close()。成功时返回0。如果使用在一个没有连接的datagram的socket上调用shutdown(),则不能使用send()和recv()(还可以使用sendto和recvfrom?)
#include  int getpeername(int sockfd, struct sockaddr *addr, int *addrlen); 参数2是远端socket结构。针对已连接的stream socket。返回什么?同accept有何区别,用于client,而accept是在server?成功返回0。
#include  int gethostname(char *hostname, size_t size);获得自己的主机名,其实和socket无关。成功返回0。
#include  struct hostent *gethostbyname(const char *name);根据域名获得主机信息,通过查询DNS服务器。
demo程序中,h_addr为网络地址数组的首地址。struct hostent中的h_addr_list是网络地址数组,其中也就是网络字节序的,和字符串数组没有关系。所以*((struct in_addr *)h->h_addr)这个转换没有问题。因为本来指针本质都是一样的,只不过转换就是换一种解释方法。
为什么在demo中,server处接收到的client IP地址,同client设置的server地址有关。client设置127.0.0.1,server接收到127.0.0.1,client设置192.168.1.10,server也收到192.168.1.10?
socket在创建时,默认为阻塞。可以使用fcntl()取消socket的阻塞。fcntl(sockfd, F_SETFL, O_NONBLOCK);
select()和accept()、recvfrom()的阻塞有何不同?select可以同时接收多个,而accept一次只能接收一个。select只是检查有没有,accept则是真正接收。
int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
最后一个参数是表示等待超时时间,如果为0表示立即查看,为NULL则无限等待。
如果一个socket在read set中关闭连接,select()返回,那个socket描述符设为"ready to read",当调用recv()时,返回0。你就知道对方关闭了连接。如果一个socket处于listening状态,将其置入readfds set,可以查看是否有新的连接。
listener就是server建立的socket,select在等待read_fds中出现可读。如果出现,则遍历read_fds中的文件描述符。如果是listener,则调用accept,可是未必是有新的连接,而accept会导致阻塞。如果不是listener,则调用recv从中读取,如果没有读到则关闭,但是如果不是i发送,这里就会把i关闭。
我的思路是:要检查的是一个文件描述符是否可读,如果可读,再做上述操作。FD_ISSET(int fd, fd_set *fdset)是检查fdset中的文件描述符fd是否可读,>0表示可读。这样看来程序应该没有问题,但是为什么server会在select处停住?sx的错误。
对于send可能返回实际发送数据量小于指定数据量的情况,对send进行封装。循环发送,直到将所有数据发送出去,或出现错误。
为什么recv返回0就表示对方关闭?
select是轮询吗?不是,通过将timeout设为NULL人工实现polling。
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/1421/showart_159349.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP