socket编程

一、sockket定义

在计算机通信领域,socket翻译为:“套接字”。他是计算机之间通讯的一种的方式。IP地址和端口能够在广袤的互联网中定位到要通信的程序,协议和数据传输方式规定了如何传输数据,有了这些,两台计算机就可以通信了。

二、实现通信的条件

两台计算机之间要通信必须知道对方的IP地址和端口号

2.1IP地址(IP Address)

  计算机分布在全世界,彼此要通信,必须知道对方的确切的位置,通常是通过确定IP地址来进行通信的,我们计算机不知道IP地址对应的地理位置,通常是将IP地址封装到发送的数据包中,交给路由器处理,路由器通过算法,找到目标计算机,并将数据包传给他,完成一次通信。

目前主要有IPV4和IPV6两种,因IPV4资源已经耗尽,现阶段在推广IPV6

2.2端口(Port)

  有了IP地址,虽然可以找到目标机,但是任然不能进行通信,因为通常一台计算机同时提供多种网路服务,通常有:FTP、SMTP、Web等服务,仅有IP地址,计算机可以接受到数据,但是不知道将数据包交给哪个网络进行处理,因此还不能正常通信。

  计算机为了区分各种网络程序,会为每一个程序分配独一无二的端口号,例如:网络服务:80端口,FTP:21   SMTP:25。端口是虚拟的,可以将端口理解为门户,数据需要通过门户流进流出,每一个门户都有一个门牌号,就是端口号。

三、通信协议及数据传输方式

  3.1 通信协议

协议(Protocol)就是网络通信的约定,通信的双方必须都遵守才能正常收发数据。协议有很多种,例如 TCP、UDP、IP 等,通信的双方必须使用同一协议才能通信。协议是一种规范,由计算机组织制定,规定了很多细节,例如,如何建立连接,如何相互识别等。

  所谓协议族(Protocol Family),就是一组协议(多个协议)的统称。最常用的是 TCP/IP 协议族,它包含了 TCP、IP、UDP、Telnet、FTP、SMTP 等上百个互为关联的协议,由于 TCP、IP 是两种常用的底层协议,所以把它们统称为 TCP/IP 协议族。

  2.2数据传输方式

  计算机之间有很多数据传输方式,各有优缺点,常用的有两种:SOCK_STREAM 和 SOCK_DGRAM。
  1) SOCK_STREAM 表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常见的 http 协议就使用 SOCK_STREAM 传输数据,因为要确保数据的正确性,否则网页不能正常解析。
  2) SOCK_DGRAM 表示无连接的数据传输方式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。因为 SOCK_DGRAM 所做的校验工作少,所以效率比 SOCK_STREAM 高。

  QQ 视频聊天和语音聊天就使用 SOCK_DGRAM 传输数据,因为首先要保证通信的效率,尽量减小延迟,而数据的正确性是次要的,即使丢失很小的一部分数据,视频和音频也可以正常解析,最多出现噪点或杂音,不会对通信质量有实质的影响。

 四、window/Linux编程实例

4.1 windows下Socket编程实例:

4.1.1 server.cpp

 1 #include<stdio.h>
 2 #include<winsock2.h>
 3 #pragma comment(lib,"ws2_32.lib")
 4 
 5 int main()
 6 {
 7     // 初始化
 8     WSAData wsaData;
 9     WSAStartup(MAKEWORD(2,2),&wsaData);
10     //创建套接字
11     SOCKET serverSock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
12     //绑定套接字
13     sockaddr_in sockAddr;
14     memset(&sockAddr, 0, sizeof(sockAddr));//每个字节都填充0
15     sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//具体的IP地址
16     sockAddr.sin_family = PF_INET;//使用IPV4
17     sockAddr.sin_port = htons(1234);
18     bind(serverSock,(SOCKADDR*)&sockAddr,sizeof(SOCKADDR));
19 
20     //监听
21     listen(serverSock,5);
22     //接受客户端请求
23     SOCKADDR clntAddr;
24     int nSize = sizeof(SOCKADDR);
25     SOCKET clntSock = accept(serverSock,(SOCKADDR*)&clntAddr,&nSize);
26 
27     //向客户端发送数据
28     char *str = "hello world!";
29     send(clntSock,str,strlen(str)+sizeof(char),NULL);
30     //关闭套接字
31     closesocket(clntSock);
32     closesocket(serverSock);
33     //终止DLL的使用
34     WSACleanup();
35     return 0;
36 
37 }
View Code

4.2.2 client.cpp

 1 #include<stdio.h>
 2 #include<winsock2.h>
 3 #pragma comment(lib,"ws2_32.lib")
 4 
 5 int main()
 6 {
 7     //初始化
 8     WSADATA wsaData;
 9     WSAStartup(MAKEWORD(2,2),&wsaData);
10     //创建套接字
11     SOCKET sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
12     //向服务器发送请求
13     sockaddr_in sockAddr;
14     sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
15     sockAddr.sin_family = PF_INET;
16     sockAddr.sin_port = htons(1234);
17     connect(sock,(SOCKADDR*)&sockAddr,sizeof(SOCKADDR));
18     //接受服务器传输的数据
19     char szBuff[MAXBYTE];
20     recv(sock,szBuff,MAXBYTE,NULL);
21     printf("Message from server:%s\n",szBuff);
22     //关闭套接字
23     closesocket(sock);
24     WSACleanup();
25     system("pause");
26     return 0;
27 }
View Code

分别编译server.cpp和client.cpp,选运行server.exe,在运行client.exe,其结果为:Message form server: hello world!

 4.2 Linux下socket编程

  4.2.1 server.cpp

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <arpa/inet.h>
 6 #include <sys/socket.h>
 7 #include <netinet/in.h>
 8 
 9 int main(){
10     //创建套接字
11     int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
12 
13     //将套接字和IP、端口绑定
14     struct sockaddr_in serv_addr;
15     memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
16     serv_addr.sin_family = AF_INET;  //使用IPv4地址
17     serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
18     serv_addr.sin_port = htons(1234);  //端口
19     bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
20 
21     //进入监听状态,等待用户发起请求
22     listen(serv_sock, 20);
23 
24     //接收客户端请求
25     struct sockaddr_in clnt_addr;
26     socklen_t clnt_addr_size = sizeof(clnt_addr);
27     int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
28 
29     //向客户端发送数据
30     char str[] = "Hello World!";
31     write(clnt_sock, str, sizeof(str));
32    
33     //关闭套接字
34     close(clnt_sock);
35     close(serv_sock);
36 
37     return 0;
38 }
View Code

  4.2.2  client.cpp

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <arpa/inet.h>
 6 #include <sys/socket.h>
 7 
 8 int main() {
 9     //创建套接字
10     int sock = socket(AF_INET, SOCK_STREAM, 0);
11 
12     //向服务器(特定的IP和端口)发起请求
13     struct sockaddr_in serv_addr;
14     memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
15     serv_addr.sin_family = AF_INET;  //使用IPv4地址
16     serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
17     serv_addr.sin_port = htons(1234);  //端口
18     connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
19 
20     //读取服务器传回的数据
21     char buffer[40];
22     read(sock, buffer, sizeof(buffer) - 1);
23 
24     printf("Message form server: %s\n", buffer);
25 
26     //关闭套接字
27     close(sock);
28 
29     return 0;
30 }
View Code

  先编译运行server.cpp

[admin@lcj ~]$ g++ server.cpp - o server
[admin@lcj ~]$ . / server
View Code

  在编译client.cpp

[admin@lcj ~]$ g++ client.cpp -o client
[admin@lcj ~]$ ./client
Message form server: Hello World!
View Code

  4.3 windows/linux中socket编程差异

  1) Windows 下的 socket 程序依赖 Winsock.dll 或 ws2_32.dll,必须提前加载。DLL 有两种加载方式,请查看:动态链接库DLL的加载

  2) Linux 使用“文件描述符”的概念,而 Windows 使用“文件句柄”的概念;Linux 不区分 socket 文件和普通文件,而 Windows 区分;Linux 下 socket() 函数的返回值为 int 类型,而 Windows 下为 SOCKET 类型,也就是句柄。

  3) Linux 下使用 read() / write() 函数读写,而 Windows 下使用 recv() / send() 函数发送和接收。

  4) 关闭 socket 时,Linux 使用 close() 函数,而 Windows 使用 closesocket() 函数。

  

posted on 2019-08-12 21:24  lfylcj  阅读(226)  评论(0编辑  收藏  举报

导航