目录
1、网络编程和套接字概要
网络编程就是编写程序使两台联网的计算机相互交换数据。这就是网络编程,是不是非常简单
如今大部分计算机都已经连接到互联网上了,我们只需要考虑如何编写数据传输软件,但这其实也不难,因为操作系统会提供“套接字”(socket)的软件。为了与远程计算机进行数据传输,我们需要连接到互联网上,而编程中的“套接字”就是用来连接该网络的工具。
2、构建电话套接字
套接字大致分为两种,其中,先要讨论的TCP套接字可以比喻为电话机,那么我们用一个电话机来模拟TCP通信
2.1:socket函数(安装电话机)
“接电话需要准备什么?”
“当然是电话机了”
socket函数的功能就好比安装电话机,有了电话机才能通信,我们在Linux系统中调出socket函数的解释文件:
/*需要包含的头文件*/
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
/*
*函数名称:socket
*函数功能:创建一个通信端口
*函数参数:
* int domain:协议族
* int type:socket的类型
* int protocol:协议,一般给0
*函数返回值:int:成功返回socket的文件描述符,失败返回-1
*/
int socket(int domain, int type, int protocol);
domain:
Name Purpose Man page
AF_UNIX Local communication unix(7)
AF_LOCAL Synonym for AF_UNIX
AF_INET IPv4 Internet protocols ip(7)
AF_AX25 Amateur radio AX.25 protocol ax25(4)
AF_IPX IPX - Novell protocols
AF_APPLETALK AppleTalk ddp(7)
AF_X25 ITU-T X.25 / ISO-8208 protocol x25(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
AF_DECnet DECet protocol sockets
AF_KEY Key management protocol, originally de‐
veloped for usage with IPsec
AF_NETLINK Kernel user interface device netlink(7)
AF_PACKET Low-level packet interface packet(7)
AF_RDS Reliable Datagram Sockets (RDS) protocol rds(7)
rds-rdma(7)
AF_PPPOX Generic PPP transport layer, for setting
up L2 tunnels (L2TP and PPPoE)
AF_LLC Logical link control (IEEE 802.2 LLC)
protocol
AF_IB InfiniBand native addressing
AF_MPLS Multiprotocol Label Switching
AF_CAN Controller Area Network automotive bus
protocol
AF_TIPC TIPC, "cluster domain sockets" protocol
AF_BLUETOOTH Bluetooth low-level socket protocol
AF_ALG Interface to kernel crypto API
AF_VSOCK VSOCK (originally "VMWare VSockets") vsock(7)
protocol for hypervisor-guest communica‐
tion
AF_KCM KCM (kernel connection multiplexor) in‐
terface
AF_XDP XDP (express data path) interface
type:
SOCK_STREAM Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported.
SOCK_DGRAM Supports datagrams (connectionless, unreliable messages of a fixed maximum length).
SOCK_SEQPACKET Provides a sequenced, reliable, two-way connection-based data transmission path for datagrams of fixed maximum length; a consumer is required to read an
entire packet with each input system call.
SOCK_RAW Provides raw network protocol access.
SOCK_RDM Provides a reliable datagram layer that does not guarantee ordering.
SOCK_PACKET Obsolete and should not be used in new programs; see packet(7)
2.2:bind函数(分配电话号码)
问 ”请问您的电话号码是多少?”
答 “我的电话号码是123-21334”
所以bind函数就像给电话机分配电话号一样,给socket函数绑定IP。我们同样从Linux系统中调出bind函数的解释文件,我在其中加了注释,大家可以看一下
/*需要包含的头文件*/
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
/*
*函数名称:bind
*函数功能:给socket绑定一个IP地址和端口号,让对端能够找到
*函数参数:
* int sockfd:socket的文件描述符
* const struct sockaddr *addr:存储地址信息的结构首地址
* socklen_t addrlen:地址结构所占的字节数
*函数返回值:int:成功返回0,失败返回-1
*/
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
2.3:listen函数(连接电话线)
那么你要给对方打电话,那肯定需要一根电话线你们才能互相交流把,所以宾果很对,listen函数就是我们的电话线。
/*需要包含的头文件*/
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
/*
*函数名称:listen
*函数功能:初始化监听队列
*函数参数:
* int socket:监听套接字的文件描述符
* int backlog:监听队列的容量,一般设置为5
*函数返回值:int,成功返回0,失败返回-1
*/
int listen(int sockfd, int backlog);
2.4、accept函数(拿起话筒)
当你家的电话响了,你的第一个动作是不是去拿起话筒,而在你拿起话筒的时候,你也就跟对方建立齐了连接,那么accept函数也是如此,我们把accept函数的解释性文件拿过来
/*需要包含的头文件*/
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
/*
*函数名:accept
*函数功能:接收客户端的连接请求
*函数参数:
* int socket:监听套接字的文件描述符
* struct sockaddr *addr:用来存储对端地址信息的结构的首地址
* socklen_t *addrlen:存储地址信息结构长度的变量的地址
*函数返回值:int:成功返回通信套接字的文件描述符,失败返回-1
*/
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
3、编写服务器和客户端
那么我们来实战一下,我们来熟悉一下服务器端和客户端的编写流程,那我先把流程给大家展示一下,然后我们就可以照着流程来写程序了
服务器:
socket
bind
listen
accept
send/recv
close
客户端:
socket
connect
send/recv
close
是不是非常简单?那我们来编写一下吧,当然了哦,我们的程序是在Linux下跑的啊,
//服务器端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message);
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 <port>\n",argv[0]);
exit(1);
}
serv_sock=socket(PF_INET,SOCK_STREAM,0);
if(serv_sock==-1)
{
error_handling("socket() error");
}
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(atoi(argv[1]));
int ret=-1;
ret=bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
if(ret==-1)
{
error_handling("bind() error");
}
ret=listen(serv_sock,5);
if(ret==-1)
{
error_handling("listen() error");
}
clnt_addr_size=sizeof(clnt_addr);
clnt_sock=accept(serv_sock,(struct sockaddr *)&clnt_addr,&clnt_addr_size);
if(clnt_sock <0)
{
error_handling("accept error");
}
write(clnt_sock,message,sizeof(message));
close(clnt_sock);
close(serv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
//客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message);
int main(int argc,char* argv[])
{
int sock;
struct sockaddr_in serv_addr;
char message[30];
int str_len;
if(argc!=3)
{
printf("Usage:%s <IP><port>\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;
serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
serv_addr.sin_port=htons(atoi(argv[2]));
int ret=-1;
ret=connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
if(ret<0)
{
error_handling("connect() error");
}
str_len=read(sock,message,sizeof(message)-1);
if(str_len==-1)
{
error_handling("read() error");
}
puts(message);
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(-1);
}