本文主要是在Linux
环境下编写socket
程序,本次实现简单TCPserver
和client
以及echo server
和echo client
什么是Socket Socket中文译名套接字, 是一个当时没有在计网中讲到的知识, 但确实是极其重要的一个知识点, 他是基于TCP/IP协议的封装抽象层, 它对于TCP协议进行了封装, 使得其现在已经成为了最为通用的网络通信接口/应用程序接口. 关于TCP协议可以看下面这篇文章
https://tools.ietf.org/html/rfc793
为什么会有Socket 我们知道, 服务/服务器其实是有很大可能不存在于一个计算机上的, 这就有了客户端与服务端, 在服务器之间, 也是需要存在连接通信的, 所以需要有一个用于进行通信的通用 可编程 接口, 这就是socket, 他对于 server
和 client
之间架起了一道桥, 让 server
与 port
绑定, 让 client
与 ip:port
连接, 这样就相当于 client
进程与 server
进程之间有了像是管道一样的通信, 不过这个通信是全双工的.
我们回顾TCP状态转换图
在这里能够找到许多socket封装的影子, 从客户端来看
则更加明显
学习Linux 下的网络编程(socket编程) 创建套接字 int socket(int domain, int type, int protocol) return : 文件描述符 / or not -1 domain: 套接字所使用的协议簇 type: 套接字数据传输类型 protocol: 套接字所使用的协议
文件描述符(windows下称之为句柄): 系统分配给文件或套接字的一个整数:
img
https://segmentfault.com/a/1190000009724931
img
协议簇: img
套接字类型: SOCK_STREAM(流式)(面向连接)image.png
SOCK_DGRAM(包式)(面向消息)img
协议 img
sockaddr_in img
img
网络字节序 img
IntelCPU默认都是小端序, 而网络连接统一都是大端序
img
img
TCPserver/clien img
img
所以又回到了最最上面的第一个图:
img
accept() img
sock: 利用socket()函数建立的sock addr: 地址变量 addlen: 结构体长度
connect() img
实现简单的server/client Server 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 #include <arpa/inet.h> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <sys/socket.h> #include <unistd.h> using std ::string ;using std ::to_string;void error_handler (char *message) ;int main (int argc, char *argv[]) { int serv_sock, clnt_sock; struct sockaddr_in serv_addr { }; struct sockaddr_in clnt_addr { }; socklen_t clnt_addr_size; string message = "Hello World!" ; if (argc != 2 ) { printf ("Usage : %s <port>\n" , argv[0 ]); exit (1 ); } serv_sock = socket(PF_INET, SOCK_STREAM, 0 ); if (serv_sock == -1 ) { error_handler("socket() error" ); } memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(atoi(argv[1 ])); if (bind(serv_sock, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) == -1 ) { error_handler("bind() error" ); } if (listen(serv_sock, 5 ) == -1 ) { error_handler("listen() error" ); } clnt_addr_size = sizeof (clnt_addr); clnt_sock = accept(serv_sock, (struct sockaddr *) &clnt_addr, &clnt_addr_size); if (clnt_sock == -1 ) { error_handler("accept() error" ); } for (int i = 1 ; i < 4 ; ++i) { sleep(rand() % 2 ); string tmp = message + to_string(i); write(clnt_sock, tmp.c_str(), tmp.size()); } close(clnt_sock); close(serv_sock); return 0 ; } void error_handler (char *message) { fputs (message, stderr ); fputc('\n' , stderr ); exit (0 ); }
client 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 #include <arpa/inet.h> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <sys/socket.h> #include <unistd.h> using std ::string ;void error_handler (char *messgae) ;int main (int argc, char *argv[]) { int sock; sockaddr_in serv_addr{}; int str_len = 0 ; int idx = 0 , read_len = 0 ; string message; message.resize(100 , '\0' ); if (argc != 3 ) { printf ("Usage : %s <IP> <port>\n" , argv[0 ]); exit (1 ); } sock = socket(PF_INET, SOCK_STREAM, 0 ); if (sock == -1 ) { error_handler("socket() error" ); } memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr(argv[1 ]); serv_addr.sin_port = htons(atoi(argv[2 ])); if (connect(sock, (sockaddr *) &serv_addr, sizeof (serv_addr)) == -1 ) { error_handler("connect() error!" ); } bool check = false ; while (read_len = read(sock, &message[idx++], 1 )) { if (str_len == -1 ) { if (check) error_handler("read() error!" ); else sleep(3 ); } str_len += read_len; } printf ("Message from server is : %s \n" , message.c_str()); printf ("Function read call count : %d \n" , str_len); close(sock); return 0 ; } void error_handler (char *message) { fputs (message, stderr ); fputc('\n' , stderr ); exit (0 ); }
结果 可以看到 client
成功接受到了流字符:
img
同时 server
也发送成功结束了程序:
img
实现echo服务器 Server 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 #include <arpa/inet.h> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <sys/socket.h> #include <unistd.h> using std ::stoi;using std ::string ;using std ::to_string;constexpr int BUF_SIZE = 1024 ;void error_handler (char *message) ;int main (int argc, char *argv[]) { int serv_sock, clnt_sock; char message[BUF_SIZE]; int size_len = 4 ; char size_buf[size_len]; sockaddr_in serv_addr{}, clnt_addr{}; socklen_t clnt_addr_size; int str_len = 0 ; if (argc != 2 ) { printf ("Usage : %s <port>\n" , argv[0 ]); exit (1 ); } serv_sock = socket(PF_INET, SOCK_STREAM, 0 ); if (serv_sock == -1 ) { error_handler("socket() error" ); } memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(atoi(argv[1 ])); if (bind(serv_sock, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) == -1 ) { error_handler("bind() error" ); } if (listen(serv_sock, 5 ) == -1 ) { error_handler("listen() error" ); } clnt_addr_size = sizeof (clnt_addr); for (int i = 0 ; i < 5 ; ++i) { clnt_sock = accept(serv_sock, (sockaddr *) &clnt_addr, &clnt_addr_size); if (clnt_sock == 1 ) { error_handler("accept() error!" ); } else { printf ("Connected client %d \n" , i + 1 ); } while (true ) { if ((str_len = read(clnt_sock, size_buf, size_len)) != 0 ) { if ((str_len = read(clnt_sock, message, stoi(size_buf))) != 0 ) { write(clnt_sock, size_buf, size_len); write(clnt_sock, message, str_len); } else { break ; } } else { break ; } } close(clnt_sock); } close(serv_sock); return 0 ; } void error_handler (char *message) { fputs (message, stderr ); fputc('\n' , stderr ); exit (0 ); }
Client 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 #include <arpa/inet.h> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <sys/socket.h> #include <unistd.h> using std ::stoi;using std ::string ;using std ::to_string;constexpr int BUF_SIZE = 1024 ;void error_handler (char *messgae) ;int main (int argc, char *argv[]) { int sock; sockaddr_in serv_addr{}; int str_len = 0 ; char message[BUF_SIZE]; if (argc != 3 ) { printf ("Usage : %s <IP> <port>\n" , argv[0 ]); exit (1 ); } sock = socket(PF_INET, SOCK_STREAM, 0 ); if (sock == -1 ) { error_handler("socket() error" ); } memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr(argv[1 ]); serv_addr.sin_port = htons(atoi(argv[2 ])); if (connect(sock, (sockaddr *) &serv_addr, sizeof (serv_addr)) == -1 ) { error_handler("connect() error!" ); } else { puts ("Connected......" ); } while (true ) { fputs ("Input message(Q/q to quit): " , stdout ); std ::fgets(message, BUF_SIZE, stdin ); if (!strcmp (message, "q\n" ) || !strcmp (message, "Q\n" )) { break ; } int l = strlen (message); printf ("len = %d\n" , l); string s = to_string(l); s.resize(4 ); write(sock, s.c_str(), 4 ); write(sock, message, strlen (message)); char buf[4 ]; str_len = read(sock, buf, 4 ); str_len = read(sock, message, stoi(buf)); message[str_len] = 0 ; printf ("Message from server: %s\n" , message); } close(sock); return 0 ; } void error_handler (char *message) { fputs (message, stderr ); fputc('\n' , stderr ); exit (0 ); }
结果 client:
img
server:
img