服务器端创建的套接字又称为服务器端套接字或监听套接字。
-调用socket函数(安装电话机)时进行的对话
-调用bind函数(分配电话号码)时进行的对话
-调用listen函数(连接电话线)时进行的对话
-调用accept(拿起话筒)时进行的对话
参数说明:
sockfd是由socket函数返回的套接字描述符,参数addr和addrlen用来返回已连接的对端进程(客户端)的协议地址。如果我们对客户端的协议地址不感兴趣,可以把addr和addrlen均置为空指针
接受连接请求的套接字创建过程可整理如下:
-调用socket函数创建套接字
-调用bind函数分配IP地址和端口号
-调用listen函数转为可接受请求状态
-调用accept函数受理连接请求
编写"Hello,world!"服务器端
#include
#include
#include
#include
#include
#include
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
int main(int argc,char *argv[])
{
int serv_sock;
int clnt_sock;
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
char message[] = "Hello,world!";
if(argc != 2)
{
printf("Usage : %s \n",argv[0]);
exit(1);
}
serv_sock = socket(PF_INET,SOCK_STREAM,0); //调用socket函数创建套接字,serv_sock套接字文件描述符
if (serv_sock == -1)
error_handling("socket() error ");
memset(&serv_addr,0,sizeof(serv_addr)); //初始化,使所有字节为0
serv_addr.sin_family = AF_INET; //IPv4网络协议使用的地址族是AF_INET
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //自动获取服务器端IP地址
serv_addr.sin_port = htons(atoi(argv[1])); //根据命令行参数初始化端口号
//分配地址信息给套接字
if (bind(serv_sock,(struct sockaddr*) &serv_addr,sizeof(serv_addr)) == -1)
error_handling("bind() error");
if(listen(serv_sock,5) == -1) //调用listen函数将套接字转为可接受连接状态
error_handling("listen() error");
clnt_addr_size = sizeof(clnt_addr);
clnt_sock = accept(serv_sock,(struct sockaddr*) &clnt_addr,&clnt_addr_size); //调用accept函数受理连接请求
/* 如果accept成功,返回值是由内核生成的一个全新描述符,代表与客户端的TCP连接 */
if (clnt_sock == -1)
error_handling("accept() error");
write(clnt_sock,message,sizeof(message)); //使用write函数传输数据
close(clnt_sock);
close(serv_sock);
return 0;
}
例子:构建打电话套接字:
接下来介绍的套接字是用于请求连接的客户端套接字。
客户端程序只有“调用socket函数创建套接字” 和 “调用connect函数向服务器端发送连接请求”两个步骤,因此比服务器端简单。
打电话(请求连接)的函数,因为其调用的是客户端套接字。
客户端程序只有“调用socket函数创建套接字” 和 “调用connect函数向服务器端发送连接请求” 两个步骤。
下面给出客户端,查看以下两项内容:
第一:调用socket函数和connect函数
第二:与服务器端共同运行以收发字符串数据
#include
#include
#include
#include
#include
#include
void error_handling(char *message);
int main(int argc,char* argv[])
{
int sock;
struct sockaddr_in serv_addr;
char message[30];
if (argc != 3)
{
printf("Usage : %s \n",argv[0]);
exit(1);
}
sock = socket(PF_INET,SOCK_STREAM,0);
if (sock == -1)
error_handling("socket() error");
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET; //IPv4使用的地址族是AF_INET
serv_addr.sin_addr.s_addr = inet_addr(argv[1]); //32位IP地址信息
serv_addr.sin_port = htons(atoi(argv[2])); //端口号
//向服务器端发送连接请求
if (connect(sock,(struct sockaddr*) &serv_addr,sizeof(serv_addr)) == -1)
error_handling("connect() error");
int str_len = read(sock,message,sizeof(message)-1); //从服务端读取数据
if (str_len == -1)
error_handling("read() error!");
printf("Message from server : %s \n",message);
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
服务器端:
9190是端口号。
客户端:
127.0.0.1是服务器端所在计算机的IP地址。9190是端口号。
打开文件-open函数:
关闭文件-close函数:
将数据写入文件-write函数:
//补充:此函数定义中,size_t是通过typedef声明的unsigned int类型。对ssize_t相对与size_t前面多了个s代表signed,即ssize_t是通过typedef声明的signed int类型。
将数据读到缓存-read函数:
特征:
收发数据的套接字内部有缓冲,字节数组。通过套接字传输的数据将保存到该数组。收到数据并不意味着马上调用read函数。只要不超过数组容量,则有可能在数据填充满缓冲后通过1次read函数调用读取全部,也有可能分成多次read调用读取。
面向连接的套接字不存在数据边界。
面向连接的套接字连接必须一一对应。
面向消息的套接字不存在连接的概念。
前两个参数决定数据的传输方式。但协议可能不同。第三个参数具体制定协议信息。
例如:
int tcp_socket = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
PF_INET是指Ipv4协议。SOCK_STREAM面向连接。满足这两个条件的协议只有IPPROTO_TCP,这种套接字成为TCP套接字。
int udp_socket = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
SOCK_DGRAM是指面向消息的数据传输方式。此套接字称为UDP套接字。