一、Socket API函数
Linux 下使用
int socket(int af, int type, int protocol);
其中:af 为地址族(Address Family),即:IP 地址类型,常用的有 AF_INET和PF_INET。
type为数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向链接套接字)和SOCK_DGRAM(数据报套接字/无连接的套接字)。
protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。
bind() 函数的原型为:
int bind(int sock, struct sockaddr *addr, socklen_t addrlen);
其中:sock 为 socket 文件描述符,addr 为 sockaddr 结构体变量的指针,addrlen 为 addr 变量的大小,可由 sizeof() 计算得出。
connect() 函数用来建立连接,它的原型为:
int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);
其参数和 bind() 相同。
对于服务器端程序,使用 bind() 绑定套接字后,还需要使用 listen() 函数让套接字进入被动监听状态,再调用 accept() 函数,就可以随时响应客户端的请求了。
通过 listen() 函数可以让套接字进入被动监听状态,它的原型为:
int listen(int sock, int backlog);
其中:sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度。
所谓被动监听,是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。
当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求。它的原型为:
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
其中:它的参数与 listen() 和 connect() 是相同的:sock 为服务器端套接字,addr 为 sockaddr_in 结构体变量,addrlen 为参数 addr 的长度,可由 sizeof() 求得。
Linux 不区分套接字文件和普通文件,使用 write() 可以向套接字中写入数据,使用 read() 可以从套接字中读取数据。
write() 的原型为:
ssize_t write(int fd, const void *buf, size_t nbytes);
其中:fd 为要写入的文件的描述符,buf 为要写入的数据的缓冲区地址,nbytes 为要写入的数据的字节数。
read() 的原型为:
ssize_t read(int fd, void *buf, size_t nbytes);
其中:fd 为要读取的文件的描述符,buf 为要接收数据的缓冲区地址,nbytes 为要读取的数据的字节数。
二、简单的网络聊天程序
下面是简单的网络聊天程序的代码。
server.c 是服务器端代码,
#include/* perror */ #include /* exit */ #include /* WNOHANG */ #include /* waitpid */ #include /* memset */ #include "socketwrapper.h" /* socket layer wrapper */ #define true 1 #define false 0 #define MYPORT 12334 /* 监听的端口 */ #define BACKLOG 10 /* listen的请求接收队列长度 */ #define MAXSIZE 100 int main() { int numbytes=0; int sockfd, new_fd; /* 监听端口,数据端口 */ struct sockaddr_in sa; /* 自身的地址信息 */ struct sockaddr_in client_addr; /* 连接对方的地址信息 */ int sin_size; char buf[MAXSIZE]; if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } sa.sin_family = AF_INET; sa.sin_port = Htons(MYPORT); /* 网络字节顺序 */ sa.sin_addr.s_addr = INADDR_ANY; /* 自动填本机IP */ memset(&(sa.sin_zero),0, 8); /* 其余部分置0 */ if ( Bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { perror("bind"); exit(1); } if (Listen(sockfd, BACKLOG) == -1) { perror("listen"); exit(1); } sin_size = sizeof(struct sockaddr_in); new_fd = Accept(sockfd, (struct sockaddr *)&client_addr, &sin_size); if (new_fd == -1) { perror("accept"); exit(1); } printf("Got connection from %s\n", Inet_ntoa(client_addr.sin_addr)); /* 主循环 */ while(1) { if ((numbytes=recv(new_fd,buf,MAXSIZE,0))>0) { buf[numbytes]=0; printf("received:%s & length=%d\n",buf,numbytes); if(send(new_fd,buf,strlen(buf), 0) == -1) perror("send"); } } return true; }
client.c 是客户端代码,
#include/* perror */ #include /* exit */ #include /* WNOHANG */ #include /* waitpid */ #include /* memset */ #include "socketwrapper.h" /* socket layer wrapper */ #define true 1 #define false 0 #define PORT 12334 /* Server的端口 */ #define MAXDATASIZE 100 /*一次可以读的最大字节数 */ int main(int argc, char *argv[]) { int sockfd, numbytes; char buf[MAXDATASIZE]; struct hostent *he; /* 主机信息 */ struct sockaddr_in server_addr; /* 对方地址信息 */ if (argc != 2) { fprintf(stderr,"usage: client hostname\n"); exit(1); } /* get the host info */ if ((he=Gethostbyname(argv[1])) == NULL) { /* 注意:获取DNS信息时,显示出错需要用herror而不是perror */ /* herror 在新的版本中会出现警告,已经建议不要使用了 */ perror("gethostbyname"); exit(1); } if ((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1) { perror("socket"); exit(1); } server_addr.sin_family = AF_INET; server_addr.sin_port = Htons(PORT); /* short, NBO */ server_addr.sin_addr = *((struct in_addr *)he->h_addr_list[0]); memset(&(server_addr.sin_zero),0, 8); /* 其余部分设成0 */ if (Connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { perror("connect"); exit(1); } while(1) { printf("Enter Something:"); scanf("%s",buf); numbytes=send(sockfd,buf,strlen(buf),0); numbytes=recv(sockfd,buf,MAXDATASIZE,0); buf[numbytes]='\0'; printf("received:%s\n",buf); } Close(sockfd); return true; }
运行结果:
通信过程:
解析: