假设A机器的应用层先向传输层传入一个“aaa”,再向传输层传入一个“bbb”,到待对端机器的传输层不会区分,是不是一次传过来的:
、
本节只介绍基于IPv4的socket网络编程,sockaddr_in中的成员struct in_addr sin_addr表示32位 的IP 地址但是我们通常用点分十进制的字符串表示IP 地址,以下函数可以在字符串表示 和in_addr表示之间转换;字符串转in_addr的函数。
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr * address,socklen_t address_len);*
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr*addr,socklen_t addrlen);
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、 IPv6,以及后面要讲的UNIX DomainSocket. 然而, 各种网络协议的地址格式并不相同。
虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型, 端口号, IP地址。
in_addr用来表示一个IPv4的IP地址. 其实就是一个32位的整数。
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
1 #include<iostream>
2 #include<sys/types.h>
3 #include<sys/socket.h>
4 #include<unistd.h>
5
6 using namespace std;
7 int main()
8 {
9 int SockFd=socket(AF_INET,SOCK_STREAM,0);
10 if(SockFd<0)
11 {
12 cout<<"套接字创建失败!"<<endl;
13 }
14 cout<<"SockFd:"<<SockFd<<endl;
15 while(1)
16 {
17 sleep(1);
18 }
19 return 0;
20 }
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr * address,socklen_t address_len);*
- sockfd:socket函数返回的套接字描述符;将创建出来的套接字和网卡,端口好进行绑定
- addr:地址信息结构
- addr的类型是struct sockaddr ,struct sockaddr 是一个通用地址信息结构,如下图所示:
假设,定义一个int fun(void * x)参数可以接收任何类型数据的函数,使用时就需要强转 char* p = “abc”; fun((void*)lp);而如上结构体的作用相当于此例中的参数,因为bind函数可能绑定 ipv4(uint32_t) / ipv6(uint128_t) / 本地域套接字 等不同类型的协议,所以绑定不同版本的IP地址需要提供不同的绑定函数,而此做法非常的麻烦,所以将协议的数据结构定义为一个通用的,要使用某一具体的协议,只需传入具体的协议对应的数据结构并强转即可。
如下图所示,我们可以在 vim /usr/include/netinet/in.h 路径下查看ipv4协议使用的结构体:- addrlen:地址信息结构的长度(告诉网络协议栈最多能解析多少个字节)
1 #include<iostream>
2 #include<sys/types.h>
3 #include<sys/socket.h>
4 #include<unistd.h>
5 #include<netinet/in.h>
6 #include<arpa/inet.h>
7 using namespace std;
8
9 int main()
10 {
11 int SockFd=socket(AF_INET,SOCK_DGRAM,0);
12 if(SockFd<0)
13 {
14 cout<<"套接字创建失败!"<<endl;
15 }
16 cout<<"SockFd:"<<SockFd<<endl;
17
18 struct sockaddr_in addr;
19
20 addr.sin_port=htons(20000);
21 addr.sin_family=AF_INET;
22 addr.sin_addr.s_addr=inet_addr("172.16.0.9");
23 int ret=bind(SockFd,(struct sockaddr*)&addr,sizeof(addr));
24 if(ret<0)
25 {
26 cout<<"绑定失败!"<<endl;
27 return 0;
28 }
29 while(1)
30 {
31 sleep(1);
32 }
33 return 0;
34 }
ssize_t sendto(int sockfd, const void * buf, size_t len, int flags,const struct sockaddr * dest_addr, socklen_t addrlen);
- sockfd:套接字描述符
- buf:要发送的数据
- len:发送数据的长度
- flags:0 阻塞发送
- dest_addr:目标主机的地址信息结构(IP,port)
- addrlen:目标主机地址信息结构的长度
- 返回值:
成功返回具体发送的字节数量,失败返回-1
ssize_t recvfrom(int sockfd, void * buf, size_t len, int flags,struct sockaddr * src_addr, socklen_t * addrlen);
- sockfd:套接字描述符
- buf:将数据接收到buf当中
- len:buf的最大接收能力
- flags:0阻塞接收
- src_addr:这个数据来源的主机的地址信息结构(IP,port)---->由recvfrom()函数填充
- addrlen:输入输出型参数
输入:在接收之前准备的对端地址信息结构的长度
输出:实际接收回来的地址信息长度
close(int sockfd);
本质上是不想让客户端程序将端口写死,即不想让客户端在启动的时候,都是绑定一个端口的(一个端口只能被一个进程所绑定)。
eg:客户端A绑定了端口,本机在启动客户端B的时候就会绑定失败
当客户端没有主动的绑定端口,UDP客户端在调用sendto的时候,会自动绑定一个空闲的端口(操作系统分配一个空闲的端口)。
客户端只需创建套接字,向服务端发送请求,接收服务端的回复即可。
1 #include<iostream>
2 #include<stdio.h>
3 #include<sys/types.h>
4 #include<sys/socket.h>
5 #include<unistd.h>
6 #include<netinet/in.h>
7 #include<arpa/inet.h>
8 #include<string.h>
9 #include<stdlib.h>
10 using namespace std;
11
12 int main()
13 {
14 int SockFd=socket(AF_INET,SOCK_DGRAM,0);
15 if(SockFd<0)
16 {
17 cout<<"套接字创建失败!"<<endl;
18 }
19 cout<<"SockFd:"<<SockFd<<endl;
20
21 /* struct sockaddr_in addr;
22
23 addr.sin_port=htons(20000);
24 addr.sin_family=AF_INET;
25 addr.sin_addr.s_addr=inet_addr("172.16.0.9");
26 int ret=bind(SockFd,(struct sockaddr*)&addr,sizeof(addr));
27 if(ret<0)
28 {
29 cout<<"绑定失败!"<
32 while(1)
33 {
34 char buf[1024]="i am client!";
35 struct sockaddr_in dest_addr;
36 dest_addr.sin_family=AF_INET;
37 dest_addr.sin_port=htons(20000);
38 dest_addr.sin_addr.s_addr=inet_addr("1.14.165.138");
39
40 sendto(SockFd,buf,strlen(buf),0,(struct sockaddr*)&dest_addr,sizeof(dest_addr));
41
42 memset(buf,'\0',sizeof(buf));
43
44 struct sockaddr_in peer_addr;
45 socklen_t len=sizeof(peer_addr);
46
47 ssize_t rece_size=recvfrom(SockFd,buf,sizeof(buf)-1,0,(struct sockaddr*)&peer_addr,&len);
48 if(rece_size<0)
49 {
50 continue;
51 }
52 cout<<"recv msg:"<<buf<<" from "<<inet_ntoa(peer_addr.sin_addr)<<" "<<ntohs(peer_addr.sin_port)<<endl;
53 sleep(1);
54 }
55 close(SockFd);
56 return 0;
57 }
服务端只需创建套接字,绑定端口,接收客户端的请求,回复客户端信息即可。
1 #include<iostream>
2 #include<stdio.h>
3 #include<sys/types.h>
4 #include<sys/socket.h>
5 #include<unistd.h>
6 #include<netinet/in.h>
7 #include<arpa/inet.h>
8 #include<string.h>
9 #include<stdlib.h>
10 using namespace std;
11
12 int main()
13 {
14 int SockFd=socket(AF_INET,SOCK_DGRAM,0);
15 if(SockFd<0)
16 {
17 cout<<"套接字创建失败!"<<endl;
18 }
19 cout<<"SockFd:"<<SockFd<<endl;
20
21 struct sockaddr_in addr;
22
23 addr.sin_port=htons(20000);
24 addr.sin_family=AF_INET;
25 addr.sin_addr.s_addr=inet_addr("172.16.0.9");
26 int ret=bind(SockFd,(struct sockaddr*)&addr,sizeof(addr));
27 if(ret<0)
28 {
29 cout<<"绑定失败!"<<endl;
30 return 0;
31 }
32 while(1)
33 {
34 char buf[1024]={0};
35 struct sockaddr_in peer_addr;
36 socklen_t len=sizeof(peer_addr);
37 ssize_t rece_size=recvfrom(SockFd,buf,sizeof(buf)-1,0,(struct sockaddr*)&peer_addr,&len);
38 if(rece_size<0)
39 {
40 continue;
41 }
42 cout<<"recv msg:"<<buf<<" from "<<inet_ntoa(peer_addr.sin_addr)<<" "<<ntohs(peer_addr.sin_port)<<endl;
43
44 memset(buf,'\0',sizeof(buf));
45 sprintf(buf,"welcome client %s:%d\n",inet_ntoa(peer_addr.sin_addr),ntohs(peer_addr.sin_port));
46 sendto(SockFd,buf,strlen(buf),0,(struct sockaddr*)&peer_addr,sizeof(peer_addr));
47 }
48 close(SockFd);
49 return 0;
50 }
创建套接字接口socket(),绑定端口bind(),关闭套接字接口close(),的使用和UDP套接字编程中的使用是一样的,下面介绍程序中用到的socket API,这些函数都在sys/socket.h中。
1 #include<unistd.h>
2 #include<iostream>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/socket.h>
6 #include<arpa/inet.h>
7 #include<error.h>
8 #include<stdio.h>
9 using namespace std;
10
11 int main()
12 {
13 int listen_sock=socket(AF_INET,SOCK_STREAM,0);
14
15 if(listen_sock<0)
16 {
17 perror("socket");
18 return 0;
19 }
20 cout<<listen_sock<<endl;
21 return 0;
22 }
1 #include<unistd.h>
2 #include<iostream>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/socket.h>
6 #include<arpa/inet.h>
7 #include<error.h>
8 #include<stdio.h>
9 using namespace std;
10
11 int main()
12 {
13 int listen_sock=socket(AF_INET,SOCK_STREAM,0);
14
15 if(listen_sock<0)
16 {
17 perror("socket");
18 return 0;
19 }
20 cout<<listen_sock<<endl;
21
22 struct sockaddr_in addr;
23
24 addr.sin_family=AF_INET;
25 addr.sin_port=htons(20000);
26 addr.sin_addr.s_addr=inet_addr("172.16.0.9");
27 // addr.sin_addr.s_addr=inet_addr("0.0.0.0");这个地址包含所有本地网卡地址
28 int ret=bind(listen_sock,(struct sockaddr*)&addr,sizeof(addr));
29
30 if(ret<0)
31 {
32 perror("bind");
33 return 0;
34 }
35
36 return 0;
37 }
int listen(int sockfd, int backlog);
当客户端和服务端进行三次握手的时候会存在两种状态:连接还未建立和连接已建立,此时操作系统内核中就会存在两个队列:未完成连接队列和已完成连接队列。
如上图若客户端只完成①或①②则此连接在未完成连接队列中,当完成三次握手后会由未完成连接队列放到已完成连接队列,而backlog就是已完成连接队列的大小,backlog影响了服务端并发接收连接的能力。
eg:假设backlog=1,服务端不accepct接收连接,此时有三个客户端都完成了三次握手,则必有一个客户端连接进入已完成连接队列中,由于已完成连接队列空间不够,所以剩余两个客户端的连接只能放入未完成连接队列中。
1 #include<unistd.h>
2 #include<iostream>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/socket.h>
6 #include<arpa/inet.h>
7 #include<error.h>
8 #include<stdio.h>
9 using namespace std;
10
11 int main()
12 {
13 int listen_sock=socket(AF_INET,SOCK_STREAM,0);
14
15 if(listen_sock<0)
16 {
17 perror("socket");
18 return 0;
19 }
20 cout<<listen_sock<<endl;
21
22 struct sockaddr_in addr;
23
24 addr.sin_family=AF_INET;
25 addr.sin_port=htons(20000);
26 addr.sin_addr.s_addr=inet_addr("172.16.0.9");
27 // addr.sin_addr.s_addr=inet_addr("0.0.0.0");这个地址包含所有本地网卡地址
28 int ret=bind(listen_sock,(struct sockaddr*)&addr,sizeof(addr));
29
30 if(ret<0)
31 {
32 perror("bind");
33 return 0;
34 }
35
36 ret=listen(listen_sock,1);
37 if(ret<0)
38 {
39 perror("listen");
40 return 0;
41 }
42
43 while(1)
44 {
45 sleep(1);
46 }
47
48 return 0;
49 }
从已经完成连接队列中获取已经完成三次握手的连接,没有连接时,调用accept会阻塞。
int accept(int sockfd, struct sockaddr * addr, socklen_t * addrlen);
- sockfd:套接字描述符(listen_sockfd)
- addr:客户端地址信息结构(客户端IP,客户端的端口)
- addrlen:客户端地址信息结构的长度
- 返回值:
成功:返回新连接的套接字描述符
失败:返回-1
三次握手的时候是对listen_sockfd进行操作,当调用accept()会在Tcp服务端内部创建一个新的套接字new_sockfd,三次握手之后的数据收发都是多new_sockfd进行操作,如下图所示:
int connect(int sockfd, const struct sockaddr * addr,socklen_t addrlen);
- sockfd:套接字描述符(listen_sockfd)
- addr:服务端地址信息结构(服务端IP,服务端的端口)
- addrlen:服务端地址信息结构的长度
- 返回值:
成功:返回0
小于0,连接失败
ssize_t send(int sockfd, const void * buf, size_t len, int flags);
- sockfd:套接字描述符(new_sockfd)
- buf:待要发送的数据
- len:发送数据的长度
- flags:
0:阻塞发送
MSG_OOB:发送带外数据
返回值:
大于0:返回发送的字节数量
-1:发送失败
带外数据:即在紧急情况下所产生的数据,会越过前面进行排队的数据优先进行发送。
ssize_t recv(int sockfd, void * buf, size_t len, int flags);
- sockfd:套接字描述符(new_sockfd)
- buf:将接收的数据放到buf
- len:buf的最大接收能力
- flags:0:阻塞发送;如果客户端没有发送数据,调用recv会阻塞
- 返回值:
大于0:正常接收了多少字节数据
等于0:对端将连接关闭了
小于0:接受失败
我们可以用cmd工具telnet模仿TCP三次握手建立连接,在cmd窗口输入 “tenlet + 公网IP + 端口号” 即可模拟测试:
按下回车键出现以下现象则连接成功:
当我们使用telnet与服务器建立三次连接(即进行三次三次握手)我们会看到,当我们查看服务器端口的使用情况是时会看到如下情况:
虽然我们在代码中将已完成连接队列的大小设为1,但上图已完成连接队列中却放了两个已完成连接,正常情况当我们就backlog设为1,已完成连接队列中只能放一个已完成连接,那么为什么会出现种情况呢?原因是操作系统内核中判断已完成队列是否已满的逻辑是如下所示:
所以我们设置的bakclog=1,向已完成连接队列中放入一个已完成连接,queue.size= 1不大于backlog=1,所以再向已完成连接队列中放入一个已完成连接,此时queue.size= 2大于backlog=1,不再放入,所以就出现如上图所示现象,虽然我们将backlog设为1,但已完成连接队列中却有两个已完成连接。
客户端流程:创建套接字,发起连接connect,发送和接收:
1 #include<unistd.h>
2 #include<string.h>
3 #include<iostream>
4 #include<stdlib.h>
5 #include<sys/types.h>
6 #include<sys/socket.h>
7 #include<arpa/inet.h>
8 #include<error.h>
9 #include<stdio.h>
10 using namespace std;
11
12 int main()
13 {
14 int sockfd=socket(AF_INET,SOCK_STREAM,0);
15
16 if(sockfd<0)
17 {
18 perror("socket");
19 return 0;
20 }
21 cout<<sockfd<<endl;
22
23 struct sockaddr_in addr;
24
25 addr.sin_family=AF_INET;
26 addr.sin_port=htons(20000);
27 addr.sin_addr.s_addr=inet_addr("1.14.165.138");
28 // addr.sin_addr.s_addr=inet_addr("0.0.0.0");这个地址包含所有本地网卡地址
29 int ret=connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));
30
31 if(ret<0)
32 {
33 perror("connect");
34 return 0;
35 }
36
37 if(ret<0)
38 {
39 perror("connect");
40 return 0;
41 }
42
43
44 while(1)
45 {
46 char buf[1024]="i am client呀呀呀!";
47
48 send(sockfd,buf,strlen(buf),0);
49 memset(buf,'\0',sizeof(buf));
50 //接收
51 ssize_t recv_size=recv(sockfd,buf,sizeof(buf)-1,0);
52 if(recv_size<0)
53 {
54 perror("recv");
55 return 0;
56 }
57 else if(recv_size==0)
58 {
59 cout<<"close peer connect"<<endl;
60 close(sockfd);
61 continue;
62 }
63
64 cout<<"buf:"<<buf<<endl;
65
66
67
68
69 sleep(1);
70 }
71
72 return 0;
73 }
服务端流程:创建侦听套接字,绑定地址信息,监听,接收新连接accept,接收,发送:
1 #include<unistd.h>
2 #include<string.h>
3 #include<iostream>
4 #include<stdlib.h>
5 #include<sys/types.h>
6 #include<sys/socket.h>
7 #include<arpa/inet.h>
8 #include<error.h>
9 #include<stdio.h>
10 using namespace std;
11
12 int main()
13 {
14 int listen_sock=socket(AF_INET,SOCK_STREAM,0);
15
16 if(listen_sock<0)
17 {
18 perror("socket");
19 return 0;
20 }
21 cout<<listen_sock<<endl;
22
23 struct sockaddr_in addr;
24
25 addr.sin_family=AF_INET;
26 addr.sin_port=htons(20000);
27 addr.sin_addr.s_addr=inet_addr("172.16.0.9");
28 // addr.sin_addr.s_addr=inet_addr("0.0.0.0");这个地址包含所有本地网卡地址
29 int ret=bind(listen_sock,(struct sockaddr*)&addr,sizeof(addr));
30
31 if(ret<0)
32 {
33 perror("bind");
34 return 0;
35 }
36
37 ret=listen(listen_sock,1);
38 if(ret<0)
39 {
40 perror("listen");
41 return 0;
42 }
43
44 struct sockaddr_in cli_addr;
45 socklen_t cli_addrlen=sizeof(cli_addr);
46 int newsockfd=accept(listen_sock,(struct sockaddr*)&cli_addr,&cli_addrlen);
47 if(newsockfd<0)
48 {
49 perror("accept");
50 return 0;
51 }
52
53 cout<<"i accept new connect form client:"<<inet_ntoa(cli_addr.sin_addr)<<" :"<<ntohs(cli_addr.sin_port)<<endl;
54 while(1)
55 {
56 char buf[1024]={0};
57
58 ssize_t recv_size=recv(newsockfd,buf,sizeof(buf)-1,0);
59 if(recv_size<0)
60 {
61 perror("recv");
62 return 0;
63 }
64 else if(recv_size==0)
65 {
66 cout<<"close peer connect"<<endl;
67 close(newsockfd);
68 continue;
69 }
70
71 cout<<"buf:"<<buf<<endl;
72
73 memset(buf,'\0',sizeof(buf));
74 strcpy(buf,"i am serve!!!");
75 send(newsockfd,buf,strlen(buf),0);
76
77
78
79 sleep(1);
80 }
81
82 return 0;
83 }
再启动一个客户端, 尝试连接服务器, 发现第二个客户端, 不能正确的和服务器进行通信。
分析原因, 是因为我们accecpt了一个请求之后, 就在一直while循环尝试read, 没有继续调用到accecpt, 导致不能接受新的请求
通过pstack查看服务端阻塞在recv处,因为服务端accept接收新连接在while循环外面,所以服务端在进行一次连接之后会进入while循环内部,不能再接收新连接(虽然客户端2和服务端完成了三次握手建立了新连接,但服务端无法接收连接,此时客户端则无法收到服务端的数据)
TCP单进程存在的问题:当存在多个客户端与服务器进行通信时,可能会出现recv阻塞或accept阻塞。
若将accept放入while循环里呢?
将accpect放入while循环中,则每个客户端只能收到一条,当客户端与服务端建立连接,向服务端发送数据服务端,服务端接收数据并回复客户端,此时服务端将回到while循环的开始阻塞在accept处(因为之前已经接收客户端发起的连接,当第二次accept时,已完成连接队列中就是空队列)。
多进程的客户端代码和单进程是一样的,单进程服务端父进程负责accept,子进程负责数据的接收和发送,需要注意的是,父进程一定要进程等待,防止子进程先于父进程退出使子进程变为僵尸进程,而父进程不能直接父进程的逻辑处使用wait或waitpid进行等待,因为阻塞等待,若子进程一直不退出,则父进程一直在等待,永远无法接收新连接,我们 需要使用需要使用自定义信号处理方式将SIGCHLD信号重新定义,当子进程退出发出SIGCHLD信号时,父进程则对子进程的资源进行回收。
创建套接字,发起连接,发送和接收
1 #include<unistd.h>
2 #include<string.h>
3 #include<iostream>
4 #include<stdlib.h>
5 #include<sys/types.h>
6 #include<sys/socket.h>
7 #include<arpa/inet.h>
8 #include<error.h>
9 #include<stdio.h>
10 using namespace std;
11
12 int main()
13 {
14 int sockfd=socket(AF_INET,SOCK_STREAM,0);
15
16 if(sockfd<0)
17 {
18 perror("socket");
19 return 0;
20 }
21 cout<<sockfd<<endl;
22
23 struct sockaddr_in addr;
24
25 addr.sin_family=AF_INET;
26 addr.sin_port=htons(20000);
27 addr.sin_addr.s_addr=inet_addr("1.14.165.138");
28 // addr.sin_addr.s_addr=inet_addr("0.0.0.0");这个地址包含所有本地网卡地址
29 int ret=connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));
30
31 if(ret<0)
32 {
33 perror("connect");
34 return 0;
35 }
36
37 if(ret<0)
38 {
39 perror("connect");
40 return 0;
41 }
42
43
44 while(1)
45 {
46 char buf[1024]="i am client!";
47
48 send(sockfd,buf,strlen(buf),0);
49 memset(buf,'\0',sizeof(buf));
50 //接收
51 ssize_t recv_size=recv(sockfd,buf,sizeof(buf)-1,0);
52 if(recv_size<0)
53 {
54 perror("recv");
55 return 0;
56 }
57 else if(recv_size==0)
58 {
59 cout<<"close peer connect"<<endl;
60 close(sockfd);
61 continue;
62 }
63
64 cout<<"buf:"<<buf<<endl;
65
66
67
68
69 sleep(1);
70 }
71
72 return 0;
73 }
服务端主要流程:创建侦听套接字,绑定地址信息,监听,接收新连接,创建子进程,接收,发送
1 #include<unistd.h>
2 #include<string.h>
3 #include<iostream>
4 #include<stdlib.h>
5 #include<sys/types.h>
6 #include<sys/socket.h>
7 #include<arpa/inet.h>
8 #include<error.h>
9 #include<stdio.h>
10 #include<pthread.h>
11 using namespace std;
12
13 struct ThreadInfo
14 {
15 int _newSockFd;
16 };
17
18 void* TcpThreadStart(void* arg)
19 {
20 pthread_detach(pthread_self());
21 struct ThreadInfo* ti=(struct ThreadInfo*)arg;
22 int newsockfd=ti->_newSockFd;
23
24 while(1)
25 {
26 char buf[1024]={0};
27
28 ssize_t recv_size=recv(newsockfd,buf,sizeof(buf)-1,0);
29 if(recv_size<0)
30 {
31 perror("recv");
32 return 0;
33 }
34 else if(recv_size==0)
35 {
36 cout<<"close peer connect"<<endl;
37 close(newsockfd);
38 continue;
39 }
40
41 cout<<"buf:"<<buf<<endl;
42
43 memset(buf,'\0',sizeof(buf));
44 strcpy(buf,"i am serve!!!");
45 send(newsockfd,buf,strlen(buf),0);
46
47 }
48 delete ti;
49 return NULL;
50 }
51 int main()
52 {
53 int listen_sock=socket(AF_INET,SOCK_STREAM,0);
54
55 if(listen_sock<0)
56 {
57 perror("socket");
58 return 0;
59 }
60 cout<<listen_sock<<endl;
61
62 struct sockaddr_in addr;
63
64 addr.sin_family=AF_INET;
65 addr.sin_port=htons(20000);
66 addr.sin_addr.s_addr=inet_addr("172.16.0.9");
67 // addr.sin_addr.s_addr=inet_addr("0.0.0.0");这个地址包含所有本地网卡地址
68 int ret=bind(listen_sock,(struct sockaddr*)&addr,sizeof(addr));
69
70 if(ret<0)
71 {
72 perror("bind");
73 return 0;
74 }
75
76 ret=listen(listen_sock,1);
77 if(ret<0)
78 {
79 perror("listen");
80 return 0;
81 }
82
83 while(1)
84 {
85 struct sockaddr_in cli_addr;
86 socklen_t cli_addrlen=sizeof(cli_addr);
87 int newsockfd=accept(listen_sock,(struct sockaddr*)&cli_addr,&cli_addrlen);
88 if(newsockfd<0)
89 {
90 perror("accept");
91 return 0;
92 }
93
94 cout<<"i accept new connect form client:"<<inet_ntoa(cli_addr.sin_addr)<<" :"<<ntohs(cli_addr.sin_port)<<endl;
95
96 struct ThreadInfo* ti =new ThreadInfo;
97 ti->_newSockFd=newsockfd;
98
99
100 pthread_t tid;
101 ret=pthread_create(&tid,NULL,TcpThreadStart,(void*)ti);
102 if(ret<0)
103 {
104 perror("pthread_create");
105 close(newsockfd);
106 delete ti;
107 continue;
108 }
109
110
111
112
113 sleep(1);
114 }
115
116 return 0;
117 }
1 #include<unistd.h>
2 #include<string.h>
3 #include<iostream>
4 #include<stdlib.h>
5 #include<sys/types.h>
6 #include<sys/socket.h>
7 #include<arpa/inet.h>
8 #include<error.h>
9 #include<stdio.h>
10 using namespace std;
11
12 int main()
13 {
14 int sockfd=socket(AF_INET,SOCK_STREAM,0);
15
16 if(sockfd<0)
17 {
18 perror("socket");
19 return 0;
20 }
21 cout<<sockfd<<endl;
22
23 struct sockaddr_in addr;
24
25 addr.sin_family=AF_INET;
26 addr.sin_port=htons(20000);
27 addr.sin_addr.s_addr=inet_addr("1.14.165.138");
28 // addr.sin_addr.s_addr=inet_addr("0.0.0.0");这个地址包含所有本地网卡地址
29 int ret=connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));
30
31 if(ret<0)
32 {
33 perror("connect");
34 return 0;
35 }
36
37 if(ret<0)
38 {
39 perror("connect");
40 return 0;
41 }
42
43
44 while(1)
45 {
46 char buf[1024]="i am client!";
47
48 send(sockfd,buf,strlen(buf),0);
49 memset(buf,'\0',sizeof(buf));
50 //接收
51 ssize_t recv_size=recv(sockfd,buf,sizeof(buf)-1,0);
52 if(recv_size<0)
53 {
54 perror("recv");
55 return 0;
56 }
57 else if(recv_size==0)
58 {
59 cout<<"close peer connect"<<endl;
60 close(sockfd);
61 continue;
62 }
63
64 cout<<"buf:"<<buf<<endl;
65
66
67
68
69 sleep(1);
70 }
71
72 return 0;
73 }
1 #include<iostream>
2 #include<errno.h>
3 #include<stdlib.h>
4 #include<sys/socket.h>
5 #include<sys/types.h>
6 #include<unistd.h>
7 #include<string.h>
8 #include<arpa/inet.h>
9 #include<netinet/in.h>
10 #include<sys/wait.h>
11 #include<stdio.h>
12 using namespace std;
13
14 void signalcallback(int signo)
15 {
16 //当前的wait是进程等待的阻塞接口, 但是应用的场景一定是子进程退出之后,
17 //父进程收到了SIGCHLD信号之后, 才会回调sigcallback函数, 才会调用wait
18 cout<<"recv signo:"<<signo<<endl;
19 wait(NULL);
20 }
21 int main()
22 {
23 signal(SIGCHLD,signalcallback);
24 int listen_sock=socket(AF_INET,SOCK_STREAM,0);
25 if(listen_sock<0)
26 {
27 perror("socket");
28 return 0;
29 }
30
31 struct sockaddr_in addr;
32
33 addr.sin_family=AF_INET;
34 addr.sin_port=htons(20000);
35 addr.sin_addr.s_addr=inet_addr("172.16.0.9");
36
37 int ret=bind(listen_sock,(struct sockaddr*)&addr,sizeof(addr));
38 if(ret<0)
39 {
40 perror("bind");
41 return 0;
42 }
43
44
45 ret=listen(listen_sock,5);
46 if(ret<0)
47 {
48 perror("listen");
49 return 0;
50 }
51
52 while(1)
53 {
54 int new_sock=accept(listen_sock,NULL,NULL); //客服端的地址和端口号,这么传NULL,不关心客服端的端口号
55 if(new_sock<0)
56 {
57 continue;
58 }
59
60 //创建子进程
61 int pid=fork();
62 if(pid<0)
63 {
64 //创建子进程失败,但是接收新链接成功
65 close(new_sock);
66 continue;
67 }
68 else if(pid==0)
69 {
70 //child
71 close(listen_sock);
72
73 while(1)
74 {
75 //recv and send
76
77 char buf[1024]={0};
78
79 ssize_t recv_size=recv(new_sock,buf,sizeof(buf)-1,0);
80 if(recv_size<0)
81 {
82 perror("recv");
83 continue;
84 }
85 else if(recv_size==0)
86 {
87 cout<<"peer shutdown!"<<endl;
88 close(new_sock);
89
90 //子进程故障
91 //exit(1);
92
93 }
94
95 cout<<"client say:"<<buf<<endl;
96
97 memset(buf,'\0',sizeof(buf));
98 strcpy(buf,"i am serve");
99 send(new_sock,buf,strlen(buf),0);
100 }
101 }
102 else
103 {
104 close(new_sock);
105 }
106 }
107 return 0;
108 }
这个建立连接的过程, 通常称为 三次握手。
这这个断开连接的过程, 通常称为 四次挥手。
以上就是今天要讲的内容,本文详细介绍了网络编程中UDP、和Tcp的编程等知识的使用,网络提供了大量的方法供我们使用,非常的便捷,我们务必掌握。希望大家多多支持!另外如果上述有任何问题,请懂哥指教,不过没关系,主要是自己能坚持,更希望有一起学习的同学可以帮我指正,但是如果可以请温柔一点跟我讲,爱与和平是永远的主题,爱各位了。加油啊!