本文部分内容摘自《计算机网络》第六版,作者:谢希仁
>* 熟悉TCP/UDP套接字
当应用程序(客户或者服务器)需要使用网络进行通信的时候,必须首先发出socket系统调用,请求操作系统为其创建一个套接字。
socket系统调用的实际作用是请求操作系统把网络通信所需要的一些系统的资源(储存器空间,CPU时间,网络带宽)分配给应用进程。
操作系统为这些资源的总和提供了一个叫做套接字描述符的号码(小的整数 )来表示,然后把套接字描述符返回给应用进程。
此后,应用进程所进行的网络操作,(建立连接,首发数据,调整网络通信参数),都会使用这个套接字描述符。
当通信完毕后,还必须进行close系统调用,来让进程回收与该套接字描述符有关的资源。
由此可见,套接字是应用进程为了获得网络通信服务而与操作系统进行交互的时使用的一种机制。
一个机器上会有很多个套接字,所以操作系统中有一个用来存放套接字描述符的套接字描述符表,这个和文件描述符表的结构是一样的,底层是一个数组,套接字描述符就是数组的下标,每个数组元素的内容是都是一个指向套接字的指针,下面用图来表示套接字描述符表和套接字结构
套接字描述符和文件描述符有什么不同?
套接字描述符是一个抽象出来的概念,本质上也是一个文件描述符。
套接字的使用将在下面的代码中详细讲解
>* 编写udp套接字代码
首先是服务端的代码
1 #include<stdlib.h>
2 #include<sys/socket.h>
3 #include<arpa/inet.h>
4 #include<stdio.h>
5 #include<netinet/in.h>
6 #include<string.h>
7 #include<unistd.h>
8 int main (int arg,char*argv[])
9 {
10 int fd=socket(AF_INET,SOCK_DGRAM,0);//创建套接字,将套接字描述符fd返回给进程
11 if(fd<0)
12 {
13 perror("socket");
14 return 1;
15 }
16 struct sockaddr_in server;//创建一个结构体,用来保存服务器进程的信息,包括IP和端口号
17 server.sin_family=AF_INET;//协议类型
18 server.sin_port=htons(atoi(argv[2]));//赋入端口号值
19 server.sin_addr.s_addr=inet_addr(argv[1]);//赋入IP地址值
20 if(bind(fd,(struct sockaddr*)&server,sizeof(server))<0)//绑定,就是将服务器的IP和端口号,分别写入fd这个文件描述符对应指针指向的套接字结构体的源IP地址和源端口号两个位置
21 {
22 perror("bind");
23 return 1;
24 }
25 char buf[1024];
26 struct sockaddr_in client;//创建一个结构体,用来保存客户端的IP和端口号
27 while(1)//重复循环
28 {
29 socklen_t len =sizeof(client);
30 ssize_t s =recvfrom(fd,buf,sizeof(buf)-1,0,\
31 (struct sockaddr*)&client,&len);//客户端用服务器的IP地址和端口号,找到fd对应的系统内存空间,并写入信息
32 if(s>0) //再转写到缓冲区buf中
33 {
34 ssize_t ret;
35 buf[s]=0;
36 printf("[%s:%d]: %s\n",inet_ntoa(client.sin_addr),\
37 ntohs(client.sin_port),buf);
38 char buf1[1024];
39 printf("Please Enter:");
40 fflush(stdout);
41 if( (ret=read(0,buf1,sizeof(buf1)-1))<0)
42 {
43
44 perror("read");
45 return 1;
46 }
47 printf("%s\n",buf1);
48 printf("ret=%d",ret);
49 fflush(stdout);
50 buf1[ret-1]=0;
51 sendto(fd,buf1,strlen(buf1),0,\ //将要回复内容从缓冲区写到套接字内存中,按照给定的目的地址进行发送,每次都要指定地址
52 (struct sockaddr*)&client,sizeof(client)); //这就是udp无连接的理由
53
54
55
56 }
57 }
58 return 0;
59 }
然后是客户端的代码
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #include<unistd.h>
5 #include<sys/socket.h>
6 #include<arpa/inet.h>
7
8 int main (int arg,char *argv[])
9 {
10 if(arg!=3)
11 {
12 printf("Usage ./client [ip] [port]\n");
13 return 1;
14 }
15 int fd=socket(AF_INET,SOCK_DGRAM,0);
16 if(fd<0)
17 {
18 perror("socket");
19 return 1;
20 }
21 struct sockaddr_in server_addr;//创建一个结构体
22 server_addr.sin_family=AF_INET;//添加协议类型
23 server_addr.sin_addr.s_addr=inet_addr(argv[1]);//添加IP地址
24 server_addr.sin_port=htons(atoi(argv[2]));//添加端口号
25 //客户端不用绑定端口号,系统会自动生成随机端口号
26 //系统把IP地址和端口号一起绑定到sock描述符上(隐式绑定)
27 //服务器发送数据时就可以通过客户端的IP地址和端口号找到相应的代表对应文件资源的
28 //sock文件描述符
29 while(1)
30 {
31 char buf[1024]={0};
32 ssize_t read_size=read(0,buf,sizeof(buf)-1);//从表中输入中读取要发送的数据
33 if(read_size<0)
34 {
35 perror("read");
36 return 1;
37 }
38 if(read_size==0)//服务器关闭,读到EOF文件结束标志
39 {
40 printf("read done!");
41 return 0;
42 }
43 buf[read_size]='\0';
44
45 sendto(fd,buf,strlen(buf),0,(struct sockaddr*)&server_addr,sizeof(server_addr));
46 //将数据从buf缓冲区中写入fd对应的内核内存中,发送到服务器中,需要指定目的地址,属于无连接通信
47 char buf_output[1024]={0};
48 read_size=recvfrom(fd,buf_output,sizeof(buf_output)-1,0,NULL,NULL);//将服务器回复的数据从内核内存中写入buf
49 if(read_size<0)
50 {
51 perror("recvfrom");
52 return 1;
53 }
54 buf_output[read_size]='\n';
55 printf("server say %s\n",buf_output);
56 }
57 close(fd);
58
59 return 0;
60 }
下面是运行的结果