网络基础的讲解:
https://blog.csdn.net/Z_JUAN1/article/details/81281606
从应用层到传输层有一个接口:socket API 接口
因此不管是服务器还是客户端在通信时都需要创建socket
创建socket,绑定地址端口,就可以进行数据传输
创建socket,直接进行数据传输
1.有两类socket,一类用于监听,一类用于传输
listen和accpet做得事情
先创建socket,然后绑定这个地址端口,在对这个地址进行监听,此时客户端若发送一个连接请求,服务器接收成功后从连接队列里面取出一个已经连接好的socket,返回这个socket的文件描述符,此时这个socket专门用于数据通信。
创建socket文件描述符
绑定地址端口
监听函数
接收请求
连接成功,他会从连接成功的队列中取出一个已经连接成功的socket,返回socket的文件描述符,专门用于数据通信
inet_addr();
将一个点分十进制的字符串转化为一个网络字节序
char * inet_ntoa();(全局静态变量,要慎用,内存会覆盖) (再多线程中可以使用inet_ntop)
将一个主机IP转为字符串
htonl();
是将一个32位长整数的主机字节序转为网络字节序,(包含了验证大小端)
htons();
是将一个16位短整数的主机字节序转为网络字节序,
ntohl();
将一个网络字节序转为32为长整数的主机字节序
ntohs();
将一个网络字节序转为32为长整数的主机字节序
1.服务器:
1 /*
2 * udp服务端程序:简单聊天服务器端
3 * 1.创建socket socket()
4 * 2.为socket绑定地址信息 bind()
5 * 3.接收发送数据 sendto()/recfrom()
6 * 4.关闭socket close()
7 */
9 #include
10 #include
11 #include
12 #include
13 #include
14 #include
15 #include
16 int main()
17 {
18 int sockfd=-1;
19 socklen_t len;
20 //创建sock接口 (协议组,类型,方式)
21 sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
22 if(sockfd<0){
23 perror("socket");
24 return -1;
25 }
26 //结构体 IPv4
27 struct sockaddr_in local;
28 local.sin_family=AF_INET;//哪个协议组
29 local.sin_port=htons(9000);//端口号
30 local.sin_addr.s_addr=inet_addr("192.168.239.128");//地址
31 int ret;
32 len=sizeof(struct sockaddr_in);//结构体大小
33 //绑定端口号(描述符,地址,大小)
34 ret=bind(sockfd,(struct sockaddr*)&local,len);
35 if(ret<0){
36 perror("bind");
37 return -1;
38 }
39 while(1){
40 char buff[1024]={0};
41 ssize_t s;
42 //接收数据(描述符,放到哪,内存大小,阻塞,从哪来,数据大小)
43 s= recvfrom(sockfd,buff,1023,0,(struct sockaddr*)&local,&len);
44 printf("%s\n",buff);
45
46 char tmp[1024]={0};
47 printf("you say");
48 scanf("%s",tmp);
49 //发送数据(描述符,从哪取,内存大小,阻塞,到哪去,数据大小)
50 sendto(sockfd,tmp,strlen(tmp),0,(struct sockaddr*)&local,len);
51 }
52 close(sockfd);
53 return 0;
54 }
2.客户端:
16 int main()
17 {
18 int sockfd;
19 socklen_t len;
20 //创建sock接口 (协议组,类型,方式)
21 sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
22 if(sockfd<0){
23 perror("socket");
24 return -1;
25 }
26 //结构体 IPv4
27 struct sockaddr_in server;
21 sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
22 if(sockfd<0){
23 perror("socket");
24 return -1;
25 }
26 //结构体 IPv4
27 struct sockaddr_in server;
28 server.sin_family=AF_INET;//哪个协议组
29 server.sin_port=htons(9000);//端口号
30 server.sin_addr.s_addr=inet_addr("192.168.239.128");//地址
31 int ret;
32 len=sizeof(struct sockaddr_in);//结构体大小
33 //收发数据
34 while(1){
35 char buff[1024]={0};
36 printf("you say:");
37 scanf("%s",buff);
38 len=sizeof(struct sockaddr_in);
39 //发送数据(,发的数据在哪,发到哪,)
40 sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&server,len);
41 //接收数据(描述符,放到哪,内存大小,阻塞,从哪来,数据大小)
42 char tmp[1024]={0};
43 recvfrom(sockfd,tmp,1023,0,(struct sockaddr*)&server,&len);
44 printf("%s\n",tmp);
45 }
46 close(sockfd);
47 return 0;
48 }
服务器
10 /*
11 * TCP---简单的网络聊天程序
12 * 1.创建socket
13 * 2.为socket绑定地址端口
14 * 3.开始监听socket--告诉操作系统,开始接受连接请求,并且处理
15 * 4.接收连接请求后--获取新连接的socket(sip sport dip dport proto(什么协议))
16 * 5.收发数据--recv\send
17 * 6.关闭socket
18 */
19 int main(int argc,char *argv[]){
20 if(argc!=3){
21 printf("use: ./tcp_server ip port\n");
22 return -1;
23 }
24 //1.创建socket,专门用于监听
25 int listen_fd;
26 listen_fd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
27 if(listen_fd<0){
28 perror("socket");
29 return -1;
30 }
31 //2.绑定地址端口
32 struct sockaddr_in listen_addr;
33 listen_addr.sin_family=AF_INET;
34 listen_addr.sin_port=htons(atoi(argv[2]));//(将字符串转为int整数)
35 listen_addr.sin_addr.s_addr=inet_addr(argv[1]);
36 socklen_t len;
37
38 len=sizeof(struct sockaddr_in);
39
40 int ret;
41 bind(listen_fd,(struct sockaddr_in*)&listen_addr,len);
42 if(ret<0){
43 perror("bind");
44 return -1;
45 }
46
47 //监听
48 if(listen(listen_fd,5)<0){
49 perror("listen");
50 return -1;
51 }
52
53 while(1){
54 struct sockaddr_in sock;//用于通信
55 len=sizeof(struct sockaddr_in);
56 int newfd;
57 newfd= accept(listen_fd,(struct sockaddr_in *)&sock,&len);
58 if(newfd<0){
59 perror("accpet");
60 continue;
61 }
62 while(1){
63
64 char buff[1024]={0};
65 ret=recv(newfd,buff,1023,0);
66 if(ret==0){
67 printf("peer shutdown!!!\n");
68 break;
60 continue;
61 }
50 return -1;
51 }
52
53 while(1){
54 struct sockaddr_in sock;//用于通信
55 len=sizeof(struct sockaddr_in);
56 int newfd;
57 newfd= accept(listen_fd,(struct sockaddr_in *)&sock,&len);
58 if(newfd<0){
59 perror("accpet");
60 continue;
61 }
62 while(1){
63
64 char buff[1024]={0};
65 ret=recv(newfd,buff,1023,0);
66 if(ret==0){
67 printf("peer shutdown!!!\n");
68 break;
69 }
70 else if(ret<0){
71 if(errno==EINTR || errno==EAGAIN){
72 continue;
73 }
74 break;
75 }
76
77 printf("client say:%s\n",buff);
78
79 // char tmp[1024];
80 // printf("input:");
81 // fflush(stdout);
82 // scanf("%s",tmp);
83 // send(newfd,tmp,strlen(tmp),0);
84 }
85 close(newfd);
86 }
87 close(listen_fd);
88 return 0;
89 }
11 /*
12 * TCP---简单的网络聊天程序--客户端
13 * 1.创建socket socket()
14 * 2.为socket绑定地址--客户端不推荐手动绑定
15 * 3.向服务器发起连接请求 connect()
16 * 4.收发数据--recv\send
17 * 5.关闭socket
18 */
19 void sigcb(int signo){
20 printf("recv a signal:SIGPIPE\n");
21 }
22 int main(int argc,char *argv[]){
23 if(argc!=3){
24 printf("use: ./tcp_server ip port\n");
25 return -1;
26 }
27 signal(SIGPIPE,sigcb);
28 //1.创建socket
29 int sockfd;
30 sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
31 if(sockfd<0){
32 perror("socket");
33 return -1;
34 }
35
36 struct sockaddr_in serv_addr;
37 serv_addr.sin_family=AF_INET;
38 serv_addr.sin_port=htons(atoi(argv[2]));//(将字符串转为int整数)
39 serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
40 socklen_t len;
41 len=sizeof(struct sockaddr_in);
42
43 //向服务器发起连接
44 int ret=connect(sockfd,(struct sockaddr_in*)&serv_addr,len);
45 if(ret<0){
46 perror("connect");
47 return -1;
48 }
49 while(1){
50 char buff[1024]={0};
51 //printf("input:");
52 // fflush(stdout);
53 sprintf(buff,"%s","i am client!!!");
54 // scanf("%s",buff);
55 send(sockfd,buff,strlen(buff),0);
56 sleep(1);
57 //char tmp[1024];
58 // recv(sockfd,tmp,1024,0);
59 // ret=recv(sockfd,tmp,1024,0);
60 // printf("serv say:%s\n",tmp);
61
62 }
63 close(sockfd);
64 return 0;
65 }
在这里我们看到有一个信号:SIGPIPE.
这是在发送数据的时候,系统检测到对端关闭,就会发送一个SIGPIPE信号,使进程退出。若我们不想让进程退出,那么我们可以自定义这个信号。
对于接收信号,有三种返回值:
(1)>0 :接收数据成功
(2) =0 :检测到对端关闭
(3) <0 :出错,但有两个可原谅的错误 1.EINTR:被信号打断 2.EAGAIN:还在缓冲区里面
上面的通信都一对一的,那么对于有多个客户端用该如何处理:
(1)多进程:
(2)多线程:
代码:
21 void *handler(void *arg)
22 {
23 int socked=(int)arg;
24 while(1){
25 char buff[1024]={0};
26 int ret=recv(socked,buff,1023,0);
27 printf("pthread:%lu,say:%s\n",pthread_self(),buff);
28 }
29 close(socked);
30 return NULL;
31 }
32 int create_worker(int newfd){
33 pthread_t tid;
34 pthread_create(&tid,NULL,handler,(void *)newfd);
35 pthread_detach(tid);
36 }
71 while(1){
72 struct sockaddr_in sock;//用于通信
73 len=sizeof(struct sockaddr_in);
74 int newfd;
75 newfd= accept(listen_fd,(struct sockaddr_in *)&sock,&len);
76 if(newfd<0){
77 perror("accpet");
78 continue;
79 }
80 printf("new_fd=%d\n",newfd);
81 create_worker(newfd);
82 }
83 return 0;
84 }
-- INS
在这里我们看到线程我们采用detach的方式进行等待,因为我们不关心线程的返回值,之间让其退出
对于两种处理方法也是各有利弊:
对于多进程来说:安全性比较高,传输比多线程慢
对于多线程来说:占用资源少,传输数据快,但有可能会丢失数据
对于这两者来说,若进程或者线程太多,会导致cpu调度频繁,传输数据慢