<-----------------------------------如果有什么不对的地方还请大家指出来----------------------------------->
socket类型:
1. 流式套接字:提供了一种面向连接,可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置了流量控制,避免数据流淹没的接收方。数据被看作字节留,无长度限制。
2. 数据包套接字:数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。
3. 原始套接字:可以对较低层协议如IP、ICMP直接访问。
socket的位置:
流式套接字位于应用层跟TCP层之间,数据包套接字位于应用层跟UDP层之间,原始套接字位于应用层跟IP层之间。
ip地址:
internet中主机要与别的机器通信必须要有一个ip地址。
ip地址分为32位,128位。
每个数据包必须携带目标IP地址和源IP地址,路由器依靠此信息为数据包选择路由。
表示形式:点分形式如(192.168.0.223),最后都会转换为一个32位的无符号整数。
端口号:
为了区分一台主机接收到的数据包应该转交给那个进程来处理,使用端口号来区别。
TCP端口号跟UDP端口号独立
端口号一般由IANA(internet assigned numbers authority)
众所周知的端口号:1~1023(1~255之间为众所周知的端口号,256~1023端口通常由unix系统占用)
已登记的端口号: 1024~49151
动态或私有端口: 49152~65535
字节序:
不同类型CPU的主机中,内存存储多字节整数的序列有两种方式。及大端、小端。
1. 大端:高字节存储在低地址
2. 小端:低字节存储在低地址
》网络中传输的数据必须按网络字节序,及大端模式。所以当应用进程将整数送入socket之前需要转化成网络字节序;当应用进程从socket取出整数后,要转化成相应的字节序。(因为网络字节序是大端,但是终端的字节序不同,所以要将网络字节序转化为我们自己的字节序)。
IP地址与网络字节序的转换:
1. inet_aton()
int inet_aton(const char *strptr, struct in_addr *addrptr); 将strptr所指的字符串装换成32位的网络字节序。
2. inet_addr()
in_addr_t inet_addr(const char *strptr); 功能同上
3. inet_ntoa()
char *inet_ntoa(struct in_addr inaddr); 将32位的网络字节序转化成点分十进制的形式
端口号与网络字节序的转换:
1. 主机字节序到网络字节序: u_long htonl (u_long hostlong)
u_short htons (u_short short)
2. 网络字节序到主机字节序: u_long ntohl (u_long hostlong)
u_short ntohs (u_shrt short)
tcp服务器端
1.创建套节字
int socket(int domain, int type, int protocol);
成功返回其描述符,失败返回-1
第一个参数:使用的协议族 IPv4 ,通常写AF_INET
第二个参数:套节字类型
第三个参数:使用的协议,如果是流式套节子或者是数据报套节字,通常写为0,让协议栈自动识别
如果是原始套节字,必须指定使用的协议编号
eg:
int listen_fd;
if((listen_fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
....
}
2.绑定服务器端地址
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
通用的网络地址结构类型:struct sockaddr
我们填充的地址结构类型:struct sockaddr_in
struct sockaddr_in
{
协议族
sin_family;
端口
sin_port;
IP地址
struct in_addr sin_addr;
填充
char sin_zero[8];
};
eg:./server 192.168.0.10 8887
填充地址ip,端口号
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
if(bind(listen_fd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr)) < 0)
{
....
}
3.设置套节子为监听模式
它是为了接收客户端的请求,然后将请求放到请求队列中,供accept去取走请求。
int listen(int sockfd, int backlog);
sockfd:套接字的文件描述符
backlog:监听的一个最大值
eg:listen(listen_fd,5);
4.提取请求
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
第一个参数:监听套节字的描述符
第二个参数:存放发起请求的客户端地址
第三个参数:地址大小,它是一个值结果参数(使用前必须进行初始化,调用结束记录对方实际地址大小)
注意:如果请求队列为空,则阻塞
如果请求队列不为空,提取请求,并且创建一个已连接的套接字,返回其描述符,此描述符,可以进行读写
5.connect(); 这个用于向listen发出请求
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen)
sockfd: socket返回的文件描述符
serv_addr: 服务器端的地址信息
addrlen: serv_addr的长度
这个函数是客户端使用的系统调用
6.建立好连接以后就是相互之间发送信息。例如:
6.1:ssize_t send (int socket, const void *buf, size_t length, int flags); 向socket套接字中发送信息,相当于write
返回值:成功返回实际发送的字节数,失败返回-1
buf:发送缓冲区首地址
length: 发送的字节数
flags: 发送的方式,通常为0。在这种情况下跟write一样。
6.2 ssize_t recv (int socket, const void *buf, size_t length, int flags); 从socket套接字中提取信息,相当于read
返回值:成功返回接收的字节数,失败返回-1
buf: 接收缓冲区的首地址
length: 字节数
flags: 接收方式,通常为0
其中还有两组函数可以实现上面的功能:sendto recvfrom 这一组一般用在UDP协议中
recvmsg sendmsg 这一组可以实现前面所有的功能,但用起来比较复杂,平时很少使用。
7. 关闭套接字:
int close( int socket ); 关闭双向通信
int shutdown(int sockfd, int howto); TCP连接是双向的,当我们用close关闭时是将读写端都关闭,如果想关闭一端用这个就行。
howto:为0时:关闭读端,但还可以往里面写数据
为1时: 关闭写端,只能从里面读数据
为2时: 关闭读写端。同close一样
下面是源码:
服务器端:
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
#include <errno.h>
-
#include <sys/socket.h>
-
#include <netinet/in.h>
-
#include <arpa/inet.h>
-
-
#define hander_err(meg) \
-
do {perror(meg); exit(EXIT_FAILURE);} while(0)
-
//./server ip port
-
int main(int argc,char *argv[])
-
{
-
int n;
-
char buf[1024];
-
int listen_fd,connfd;
-
int addr_len = sizeof(struct sockaddr);
-
struct sockaddr_in server_addr,peer_addr;
-
-
if(argc < 3)
-
{
-
fprintf(stderr,"Usage : %s ip port.\n",argv[0]);
-
exit(EXIT_FAILURE);
-
}
-
-
if((listen_fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
-
hander_err("socket");
-
-
bzero(&server_addr,sizeof(server_addr));
-
server_addr.sin_family = AF_INET;
-
server_addr.sin_port = htons(atoi(argv[2])); //这里用htons是因为sin_port的类型
-
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
-
-
if(bind(listen_fd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
-
hander_err("listen");
-
-
listen(listen_fd,5);
-
-
if((connfd = accept(listen_fd,(struct sockaddr *)&peer_addr,&addr_len)) < 0) //其实我们通信是通过connfd这个套接字通信的?
-
hander_err("accept");
-
-
printf("connfd = %d.\n",connfd);
-
-
printf("**********************************\n");
-
printf("Ip : %s.\n",inet_ntoa(peer_addr.sin_addr));
-
printf("Port : %d.\n",ntohs(peer_addr.sin_port));
-
printf("**********************************\n");
-
-
while(1)
-
{
-
n = read(connfd,buf,sizeof(buf));
-
buf[n] = '\0';
-
printf("Read %d bytes : %s.\n",n,buf);
-
}
-
-
exit(EXIT_SUCCESS);
-
}
客户端:
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
#include <errno.h>
-
#include <sys/socket.h>
-
#include <netinet/in.h>
-
#include <arpa/inet.h>
-
-
#define hander_err(meg) \
-
do {perror(meg); exit(EXIT_FAILURE);} while(0)
-
-
//./server ip port
-
int main(int argc,char *argv[])
-
{
-
char buf[1024];
-
int sockfd;
-
int addr_len = sizeof(struct sockaddr);
-
struct sockaddr_in server_addr;
-
-
if(argc < 3)
-
{
-
fprintf(stderr,"Usage : %s ip port.\n",argv[0]);
-
exit(EXIT_FAILURE);
-
}
-
-
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
-
hander_err("socket");
-
-
bzero(&server_addr,sizeof(server_addr));
-
server_addr.sin_family = AF_INET;
-
server_addr.sin_port = htons(atoi(argv[2]));
-
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
-
-
//发出请求
-
if(connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
-
hander_err("connect");
-
-
while(1)
-
{
-
printf(">");
-
fgets(buf,sizeof(buf),stdin);
-
buf[strlen(buf)-1] = '\0';
-
-
write(sockfd,buf,strlen(buf));
-
}
-
-
exit(EXIT_SUCCESS);
-
}
UDP:
函数的介绍:
1. ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
socket: 套接字文件描述符
buf: 接收数据的首地址
flags: 通常为0
len: 接收数据的长度
src_addr: 为NULL时,后面的addrlen就不用了,直接写NULL就行
当不为NULL时,存的是数据发送端的一些ip、port信息,此时adrlen为sockaddr的大小。
返回值: 成功返回接收到的字节数,失败返回-1,如果信息发送端不存在将返回0。
2. ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
socket: 套接字文件描述符
buf: 要发送的信息的首地址
len: 发送信息的大小
flags: 通常为0
dest_addr: 如果想将自己的ip、port发出去的话这里要写上。反之为NULL。此时addrlen为0
返回值: 成功返回发送的字节数,失败返回-1,并且设置错误码
下面是源代码:
服务器端:
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <sys/types.h>
-
#include <sys/socket.h>
-
#include <string.h>
-
#include <arpa/inet.h>
-
#include <netinet/in.h>
-
-
#define handler_err(msg) \
-
do {perror(msg); exit(EXIT_FAILURE);} while(0)
-
-
// .service ip port
-
int main(int argc, char *argv[])
-
{
-
int sockfd;
-
struct sockaddr_in service_addr,peer_addr;
-
int ret;
-
char buf[1024];
-
socklen_t addrlen = sizeof(peer_addr);
-
-
if(argc != 3)
-
{
-
fprintf(stderr,"Usage : %s ip port\n",argv[0]);
-
exit(EXIT_FAILURE);
-
}
-
-
sockfd = socket(AF_INET,SOCK_DGRAM,0);
-
if(sockfd < 0)
-
handler_err("Fail to socket");
-
-
bzero(&service_addr, sizeof(service_addr));
-
service_addr.sin_family = AF_INET;
-
service_addr.sin_port = htons(atoi(argv[2]));
-
service_addr.sin_addr.s_addr = inet_addr(argv[1]);
-
-
ret = bind(sockfd,(struct sockaddr*)&service_addr,sizeof(service_addr));
-
if(ret < 0)
-
handler_err("Fail to bind");
-
-
while(1)
-
{
-
ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&peer_addr, &addrlen);
-
buf[ret] = '\0';
-
if(ret <= 0)
-
handler_err("Fail to recvfrom");
-
-
printf("buf : %s\n",buf);
-
}
-
exit(EXIT_SUCCESS);
-
}
客户端程序:
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <sys/types.h>
-
#include <sys/socket.h>
-
#include <string.h>
-
#include <arpa/inet.h>
-
#include <netinet/in.h>
-
-
#define handler_err(msg) \
-
do {perror(msg); exit(EXIT_FAILURE);} while(0)
-
-
// .service ip port
-
int main(int argc, char *argv[])
-
{
-
int sockfd;
-
struct sockaddr_in service_addr;
-
int ret;
-
char buf[1024];
-
-
if(argc != 3)
-
{
-
fprintf(stderr,"Usage : %s ip port\n",argv[0]);
-
exit(EXIT_FAILURE);
-
}
-
sockfd = socket(AF_INET,SOCK_DGRAM,0);
-
if(sockfd < 0)
-
handler_err("Fail to socket");
-
bzero(&service_addr, sizeof(service_addr));
-
service_addr.sin_family = AF_INET;
-
service_addr.sin_port = htons(atoi(argv[2]));
-
service_addr.sin_addr.s_addr = inet_addr(argv[1]);
-
/*
-
ret = bind(sockfd,(struct sockaddr*)&service_addr,sizeof(service_addr));
-
if(ret < 0)
-
handler_err("Fail to bind");
-
*/
-
while(1)
-
{
-
printf(">");
-
fgets(buf,sizeof(buf),stdin);
-
buf[strlen(buf) - 1] = '\0';
-
-
ret = sendto(sockfd, &buf, strlen(buf), 0, (struct sockaddr*)&service_addr, sizeof(struct sockaddr_in));
-
if(ret <= 0)
-
handler_err("Fail to sendto");
-
}
-
exit(EXIT_SUCCESS);
-
}