前2篇文章,我写了一些网络编程的基础以及一些网络编程需要掌握的基础。
socket编程
TCP协议的流程图
收到确认消息后才会继续发送消息,否则继续等待。这样的好处是传输的数据是可靠的,此外它是有连接的传输,大多数网络传输都是用的TCP。
TCP协议
(1)面向连接的可靠的传输控制协议,连接的建立需要三次握手,连接的 释放需要进行四次握手才能保证连接的建立,数据的同步传输。
(2)面向字节流,会把从上层传输下来的数据当作是无结构的字节流。
(3)一对一的通信。
(4)TCP在IP协议的基础之上添加了序号机制,确认机制,超时重传机制,数据校验,从而保证传输的可靠性,同时保证不出现丢失或者是乱序。
TCP通信的基本步骤如下:
服务端:socket---bind---listen---while(1){---accept---recv---send---close---}---close
客户端:socket---------connect---send---recv-----------------close
服务器端:
1. 创建socket
1sock_fd = socket(AF_INET, SOCK_STREAM,0);//AF_INET:IPV4;SOCK_STREAM:TCP
2if(-1== sock_fd)
3{
4fprintf(stderr,"socket error:%s\n\a", strerror(errno));
5exit(1);
6}
所需要头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数格式:
int socket(int domain, int type, int protocol);
函数功能:
创建一个套接字;
domain:协议域(族),决定了套接字的地址类型,例如AF_INET决定了要用IPv4地址(32位)与端口号(16位)的组合。常见的协议族有:AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX)、AF_ROUTE等;
type:指定套接字类型,SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)、SOCK_RAW
protocol:指定socket所使用的传输协议编号,通常为0
返回值:
若成功,返回一个套接字描述符,否则返回-1;
Socket就是一种文件描述符,和普通的打开文件一样,需要检测其返回结果。
2. 设置socket
1memset(&server_addr,0,sizeof(struct sockaddr_in));//clear
2server_addr.sin_family = AF_INET;
3server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY:This machine all IP
4server_addr.sin_port = htons(PORT_NUMBER);
设置何种协议族,设置本机IP和端口,也就有了唯一性。
3. 绑定socket
1ret = bind(sock_fd, (struct sockaddr *)(&server_addr),sizeof(struct sockaddr));
2if(-1== ret)
3{
4fprintf(stderr,"bind error:%s\n\a", strerror(errno));
5close(sock_fd);
6exit(1);
7}
所需要头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数格式:
int bind(int sockfd, struct sockaddr *addr, int addrlen);
函数功能:
把套接字绑定到本地计算机的某一个端口上;
sockfd:待绑定的套接字描述符
addr:一个struct sockaddr *指针,指定要绑定给sockfd的协议地址。内容结构由前面的协议族决定。
addrlen:地址的长度
返回值:
若成功,返回0,否则返回-1,错误信息存在errno中;
4. 开始监听
1ret =listen(sock_fd, BACKLOG);
2if(-1== ret)
3{
4fprintf(stderr,"listen error:%s\n\a", strerror(errno));
5close(sock_fd);
6exit(1);
7}
所需要头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数格式:
int listen(int sockfd, int backlog);
函数功能:
使服务器的这个端口和IP处于监听状态,等待网络中某一客户机的连接请求,最大连接数量为backlog≤128;
sockfd:待监听的套接字描述符
backlog:最大可监听和连接的客户端数量
返回值:
若成功,返回0,否则返回-1;
5. 阻塞,等待连接
1addr_len =sizeof(struct sockaddr);
2new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &addr_len);
3if(-1== new_fd)
4{
5fprintf(stderr,"accept error:%s\n\a", strerror(errno));
6close(sock_fd);
7exit(1);
8}
1
所需要头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数格式:
int accept(int sockfd, struct sockaddr *addr, int *addrlen);
函数功能:
接受连接请求,建立起与客户机之间的通信连接。服务器处于监听状态时,如果某时刻获得客户机的连接请求,此时并不是立即处理这个请求,而是将这个请求放在等待队列中,当系统空闲时再处理客户机的连接请求;
当accept函数接受一个连接时,会返回一个新的socket标识符,以后的数据传输和读取就要通过这个新的socket编号来处理,原来参数中的socket也可以继续使用,继续监听其它客户机的连接请求;
accept连接成功时,参数addr所指的结构体会填入所连接机器的地址数据;
sockfd:待监听的套接字描述符
addr:指向struct sockaddr的指针,用于返回客户端的协议地址
addrlen:协议地址的长度
返回值:
若成功,返回一个由内核自动生成的一个全新描述字,代表与返回客户的TCP连接,否则返回-1,错误信息存在errno中;
6. 接收数据
1recv_len =recv(new_fd, recv_buf,999,0);
2if(recv_len <=0)
3{
4fprintf(stderr,"recv error:%s\n\a", strerror(errno));
5close(new_fd);
6exit(1);
7}
8else
9{
10recv_buf[recv_len] ='\0';
11printf("Get msg from client%d: %s\n", client_num, recv_buf);
12}
所需要头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数格式:
int recv(int sockfd, void *buf, size_t len, int flags);
函数功能:
用新的套接字来接收远端主机传来的数据,并把数据存到由参数buf指向的内存空间;
sockfd:sockfd为前面accept的返回值,即new_fd,也就是新的套接字
buf:指明一个缓冲区
len:指明缓冲区的长度
flags:通常为0
返回值:
若成功,返回接收到的字节数,另一端已关闭则返回0,否则返回-1,错误信息存在errno中;
7. 关闭socket
1close(sock_fd);
2exit(0);
为了应对多个连接,并保证它们之间相互独立,实际编程中往往还要加入多进程fork()。
让子进程接收数据,父进程继续监听新的连接。
客户机端:
1. 创建socket
1sock_fd = socket(AF_INET, SOCK_STREAM,0);//AF_INET:IPV4;SOCK_STREAM:TCP
2if(-1== sock_fd)
3{
4fprintf(stderr,"socket error:%s\n\a", strerror(errno));
5exit(1);
6}
2. 设置socket
1memset(&server_addr,0,sizeof(struct sockaddr_in));//clear
2server_addr.sin_family = AF_INET;
3server_addr.sin_port = htons(PORT_NUMBER);
其中注意的是,这里设置的socket内容是指 希望连接的服务器IP和端口号信息,IP地址来自用户的输入,并转换格式得到。因此,这里的设置和服务器的设置,要保持内容上的一致。
1ret = inet_aton(argv[1], &server_addr.sin_addr);
2if(0== ret)
3{
4fprintf(stderr,"server_ip error.\n");
5close(sock_fd);
6exit(1);
7}
3. 连接
1ret = connect(sock_fd, (conststruct sockaddr *)&server_addr,sizeof(struct sockaddr));
2if(-1== ret)
3{
4fprintf(stderr,"connect error:%s\n\a", strerror(errno));
5close(sock_fd);
6exit(1);
7}
所需要头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数格式:
int connect (int sockfd, struct sockaddr *serv_addr, int addrlen);
函数功能:
用来请求连接远程服务器,将参数sockfd的socket连至参数serv_addr所指定的服务器IP和端口号上去;
sockfd:客户端的socket套接字
serv_addr:一个struct sockaddr类型的结构体指针变量,存储着远程服务器的IP与端口号信息
addrlen:结构体变量的长度
返回值:
若成功,返回0,否则返回-1,错误信息存在errno中;
4. 发送
1send_buf = send(sock_fd, send_buf,strlen(send_buf),0);
2if(send_buf <=0)
3{
4fprintf(stderr,"send error:%s\n\a", strerror(errno));
5close(sock_fd);
6exit(1);
7}
所需要头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数格式:
int send(int sockfd, const void *buf, int len, int flags);
函数功能:
用来发送数据给指定的远端主机;
sockfd:客户端的socket套接字
buf:指明一个缓冲区
len:指明缓冲区的长度
flags:通常为0
返回值:
若成功,返回发送的字节数,否则返回-1,错误信息存在errno中
5. 关闭socket
1close(sock_fd);
2exit(0);
TCP完整例子:
service:
1#include<stdio.h>
2#include<unistd.h>
3#include<sys/types.h>
4#include<sys/socket.h>
5#include<strings.h>
6#include<string.h>
7#include<ctype.h>
8#include<arpa/inet.h>
9
10#defineSERV_PORT 9527
11
12intmain(void)
13
{
14intsfd, cfd;
15intlen, i;
16charbuf[BUFSIZ], clie_IP[BUFSIZ];
17
18structsockaddr_inserv_addr,clie_addr;
19socklen_tclie_addr_len;
20
21/*创建一个socket 指定IPv4协议族 TCP协议*/
22sfd = socket(AF_INET, SOCK_STREAM,0);
23
24/*初始化一个地址结构 man 7 ip 查看对应信息*/
25bzero(&serv_addr,sizeof(serv_addr));//将整个结构体清零
26serv_addr.sin_family = AF_INET;//选择协议族为IPv4
27serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本地所有IP地址
28serv_addr.sin_port = htons(SERV_PORT);//绑定端口号
29
30/*绑定服务器地址结构*/
31bind(sfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr));
32
33/*设定链接上限,注意此处不阻塞*/
34listen(sfd,64);//同一时刻允许向服务器发起链接请求的数量
35
36printf("wait for client connect ...\n");
37
38/*获取客户端地址结构大小*/
39clie_addr_len =sizeof(clie_addr_len);
40/*参数1是sfd; 参2传出参数, 参3传入传入参数, 全部是client端的参数*/
41cfd = accept(sfd, (struct sockaddr *)&clie_addr, &clie_addr_len);/*监听客户端链接, 会阻塞*/
42
43printf("client IP:%s\tport:%d\n",
44inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP,sizeof(clie_IP)),
45ntohs(clie_addr.sin_port));
46
47while(1) {
48/*读取客户端发送数据*/
49len = read(cfd, buf,sizeof(buf));
50write(STDOUT_FILENO, buf, len);
51
52/*处理客户端数据*/
53for(i =0; i < len; i++)
54buf[i] =toupper(buf[i]);
55
56/*处理完数据回写给客户端*/
57write(cfd, buf, len);
58}
59
60/*关闭链接*/
61close(sfd);
62close(cfd);
63
64return0;
65}
client:
1#include<stdio.h>
2#include<unistd.h>
3#include<string.h>
4#include<sys/socket.h>
5#include<arpa/inet.h>
6
7#defineSERV_IP"127.0.0.1"
8#defineSERV_PORT 9527
9
10intmain(void)
11
{
12intsfd, len;
13structsockaddr_inserv_addr;
14charbuf[BUFSIZ];
15
16/*创建一个socket 指定IPv4 TCP*/
17sfd = socket(AF_INET, SOCK_STREAM,0);
18
19/*初始化一个地址结构:*/
20bzero(&serv_addr,sizeof(serv_addr));//清零
21serv_addr.sin_family = AF_INET;//IPv4协议族
22inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr);//指定IP 字符串类型转换为网络字节序 参3:传出参数
23serv_addr.sin_port = htons(SERV_PORT);//指定端口 本地转网络字节序
24
25/*根据地址结构链接指定服务器进程*/
26connect(sfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr));
27
28while(1) {
29/*从标准输入获取数据*/
30fgets(buf,sizeof(buf),stdin);
31/*将数据写给服务器*/
32write(sfd, buf,strlen(buf));//写个服务器
33/*从服务器读回转换后数据*/
34len = read(sfd, buf,sizeof(buf));
35/*写至标准输出*/
36write(STDOUT_FILENO, buf, len);
37}
38
39/*关闭链接*/
40close(sfd);
41
42return0;
43}
推荐阅读:
觉得有用,点赞,分享就是对小编最大的支持
▼长按2秒识别二维码关注公众号
欢迎把我推荐给你的朋哟
每天进步一点点,如果有用给点个赞