一、Socket API函数
Linux 下使用 <sys/socket.h> 头文件中 socket() 函数来创建套接字,原型为:
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 <stdio.h> /* perror */
#include <stdlib.h> /* exit */
#include <sys/types.h> /* WNOHANG */
#include <sys/wait.h> /* waitpid */
#include <string.h> /* 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 <stdio.h> /* perror */
#include <stdlib.h> /* exit */
#include <sys/types.h> /* WNOHANG */
#include <sys/wait.h> /* waitpid */
#include <string.h> /* 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;
}
运行结果:


客户端和服务器的通信过程如下图所示:
