Linux平台 TCP Socket通信实例,发现用代码注释记笔记也不错
TCP server 阻塞
1 // 两个进程通过socket进行通信,client需要知道server的,server却不需要实现知道client的。 2 // socket需要知道type和address domain; 3 // address domain:unix domain用于同主机进程间,通过文件系统实现;Internet domain用于两台机器间,需要IP和Port信息。一般2000以下的port保留。 4 // socket type:stream(意味着持续读取,TCP是一种协议)、datagram(一次需要读完整个信息,UDP是一种协议)。 5 6 // port复用机制的理解 7 8 /** 9 stream server 10 1.socket 11 2.bind 12 3.listen 13 4.accept 14 */ 15 #include <cstdlib> /* exit() */ 16 #include <cstdio> /* perror(): 打印信息+发生错误的原因,可用于定位。 */ 17 #include <cstring> /* memset memcpy*/ 18 #include <iostream> /* cin cout */ 19 #include <sys/types.h> /* 为了满足一些 BSD系统添加头文件*/ 20 #include <sys/socket.h> /* socket(); listen(); baccept(); socklen_t */ 21 #include <netinet/in.h> /* struct sockaddr_in: 保存socket信息; ntohl(), ntohs(), htonl() and htons()*/ 22 #include <unistd.h> /* read() write(), 不是C语言范畴,所以没有cxxxx的实现 */ 23 // http://man7.org/linux/man-pages/man2/read.2.html 24 // http://man7.org/linux/man-pages/man2/write.2.html 25 26 /** 27 \brief 错误处理函数 28 */ 29 void tzError(const char *msg) 30 { 31 perror(msg); 32 exit(1); // 一般不同原因不同的exit code更为规范 33 } 34 35 /** 36 \brief 主函数 37 */ 38 int main(int argc, char *argv[]){ 39 // 参数check 40 if (argc < 2) { 41 fprintf(stderr,"ERROR, no [port] arg provided\n"); 42 exit(1); 43 } 44 // 生成socke + check 45 int sockfd; 46 sockfd = socket(AF_INET, SOCK_STREAM, 0); 47 // int socket(int domain, int type, int protocol); 48 // http://man7.org/linux/man-pages/man2/socket.2.html 49 // domain: 50 // AF_UNIX 用于本地通信 51 // AF_INET IPv4 52 // AF_INET6 IPv6 53 // type: 54 // SOCK_STREAM 面向连接通信 55 // SOCK_DGRAM 面向无连接通信 56 // protocol: 57 // SOCK_NONBLOCK 非阻塞 58 // SOCK_CLOEXEC 子类不继承父类的socket fd 59 // Return: 60 // 建立的socket:对于server用于监听,对于client用于向指定位置发送 61 if (sockfd < 0){ 62 tzError("socket()"); 63 } 64 // bind socket + check 65 int port_no = atoi(argv[1]); 66 struct sockaddr_in serv_addr; 67 serv_addr.sin_family = AF_INET; // IPv4 68 serv_addr.sin_addr.s_addr = INADDR_ANY; // 相当于inet_addr("0.0.0.0"),所有网卡。 69 serv_addr.sin_port = htons(port_no); // 需要网络序转换 70 int bind_ret = bind(sockfd, 71 (struct sockaddr*)&serv_addr, 72 sizeof(serv_addr)); 73 // int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 74 // http://man7.org/linux/man-pages/man2/bind.2.html 75 // socket()产生sockdf,有了name space(address family)但是没有具体地址,bind完成此任务 76 if( bind_ret<0 ){ 77 tzError("bind()"); 78 } 79 // listen 80 listen(sockfd,5); 81 // int listen(int sockfd, int backlog); 82 // http://man7.org/linux/man-pages/man2/listen.2.html 83 // listen让sockfd对应的socket成为被动socket,用于accetp 84 // backlog: 最大可以入队等待处理的连接数量。如果大于128(somaxconn)可能被内核截断,cat /proc/sys/net/core/somaxconn 85 // 如果尝试connect的链接超过backlog,client收到ECONNREFUSED错误,如果有重传机制可能忽略此错误。 86 // accept + check 87 struct sockaddr_in client_addr; 88 socklen_t clilen_len = sizeof(client_addr); 89 int new_sockfd = accept(sockfd, 90 (struct sockaddr *) &client_addr, 91 &clilen_len); 92 // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) 93 // http://man7.org/linux/man-pages/man2/accept.2.html 94 // 用于面向连接socket(SOCK_STREAM, SOCK_SEQPACKET),提取队列中第一个请求,创建新的connected socket 95 // 返回新创建的socket的fd,原始的被动sockfd不受影响。 96 // addr: socketaddr指针,记录peer的信息,信息格式取决于socket的定义。 97 // 如果等待队列为空,且socket没有设置nonblocking-->阻塞等待; 98 // 如果等待队列为空,且socket设置nonblocking,返回错误:EAGAIN or EWOULDBLOCK 99 100 // 为了处理对socket的新的connect,可使用:select(2), poll(2), or epoll(7)-->有尝试连接的请求会出触发"readable event" 101 // 也可以通过socket()设置发送SIGIO中断。 102 // addrlen:传入传输参数,实际的大小会重新保存到addrlen中 103 if (new_sockfd < 0) 104 tzError("accept()"); 105 // 接收-发送信息 106 #define RECV_BUF_SIZE 256 107 char buffer[RECV_BUF_SIZE]; 108 std::memset(buffer, 0, RECV_BUF_SIZE); // 用0进行set 109 int valread = read( new_sockfd , buffer, RECV_BUF_SIZE); 110 std::cout << "read:" << valread << "char, content: " << buffer << std::endl; 111 write(new_sockfd , "get msg", 7); 112 // close sockets 113 close(new_sockfd); 114 close(sockfd); 115 116 return 0; 117 } 118 119 // ref: 120 // https://www.cnblogs.com/fuchongjundream/p/3914696.html 121 // http://www.linuxhowtos.org/C_C++/socket.htm 122 //
TCP Client - 单次连接发送接收测试
1 // 两个进程通过socket进行通信,client需要知道server的,server却不需要实现知道client的。 2 // socket需要知道type和address domain; 3 // address domain:unix domain用于同主机进程间,通过文件系统实现;Internet domain用于两台机器间,需要IP和Port信息。一般2000以下的port保留。 4 // socket type:stream(意味着持续读取,TCP是一种协议)、datagram(一次需要读完整个信息,UDP是一种协议)。 5 6 // port复用机制的理解 7 8 /** 9 stream client 10 1.socket 11 2.connect 12 */ 13 #include <cstdlib> /* exit() */ 14 #include <cstdio> /* perror(): 打印信息+发生错误的原因,可用于定位。 */ 15 #include <cstring> /* memset memcpy strcpy string.c_str()*/ 16 #include <iostream> /* cin cout */ 17 #include <sys/types.h> /* 为了满足一些 BSD系统添加头文件*/ 18 #include <sys/socket.h> /* socket(); listen(); baccept(); socklen_t */ 19 // #include <netinet/in.h> /* struct sockaddr_in: 保存socket信息; ntohl(), ntohs(), htonl() and htons()*/ 20 #include <netdb.h> /* hostent,IP地址 */ 21 22 #include <unistd.h> /* read() write(), 不是C语言范畴,所以没有cxxxx的实现 */ 23 // http://man7.org/linux/man-pages/man2/read.2.html 24 // http://man7.org/linux/man-pages/man2/write.2.html 25 26 /** 27 \brief 错误处理函数 28 */ 29 void tzError(const char *msg) 30 { 31 perror(msg); 32 exit(1); // 一般不同原因不同的exit code更为规范 33 } 34 35 /** 36 \brief 主函数 37 */ 38 int main(int argc, char *argv[]){ 39 // 参数check 40 if (argc < 3) { 41 tzError("ERROR, no [IP] [port] arg provided"); 42 } 43 // 生成socke + check 44 int sockfd; 45 sockfd = socket(AF_INET, SOCK_STREAM, 0); 46 // int socket(int domain, int type, int protocol); 47 // http://man7.org/linux/man-pages/man2/socket.2.html 48 // domain: 49 // AF_UNIX 用于本地通信 50 // AF_INET IPv4 51 // AF_INET6 IPv6 52 // type: 53 // SOCK_STREAM 面向连接通信 54 // SOCK_DGRAM 面向无连接通信 55 // protocol: 56 // SOCK_NONBLOCK 非阻塞 57 // SOCK_CLOEXEC 子类不继承父类的socket fd 58 // Return: 59 // 建立的socket:对于server用于监听,对于client用于向指定位置发送 60 if (sockfd < 0){ 61 tzError("socket()"); 62 } 63 // connect + check 64 struct hostent *server; 65 // struct hostent { 66 // char *h_name; /* official name of host */ 67 // char **h_aliases; /* alias list */ 68 // int h_addrtype; /* host address type */ 69 // int h_length; /* length of address */ 70 // char **h_addr_list; /* list of addresses */ 71 // } 72 // #define h_addr h_addr_list[0] /* for backward compatibility,第一个地址 */ 73 server = gethostbyname(argv[1]); 74 if (server == NULL) { 75 tzError("[IP] error"); 76 } 77 int port_no = atoi(argv[2]); 78 struct sockaddr_in serv_addr; 79 memset(&serv_addr, 0, sizeof(serv_addr)); 80 serv_addr.sin_family = AF_INET; // set type 81 memcpy((char *)server->h_addr, // set addr 82 (char *)&serv_addr.sin_addr.s_addr, 83 server->h_length); 84 serv_addr.sin_port = htons(port_no); // set port 85 int conntect_ret = connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)); 86 if(conntect_ret<0){ 87 tzError("connect()"); 88 } 89 90 // write and read 91 std::cout << "msg>"; 92 std::string msg; 93 std::cin >> msg; 94 #define BUF_SIEE 128 95 char buf[BUF_SIEE]; 96 memset(buf, 0, BUF_SIEE); 97 strcpy(buf, msg.c_str()); 98 int write_n = write(sockfd, buf, msg.size()); 99 std::cout << write_n << " chars send." << std::endl; 100 memset(buf, 0, BUF_SIEE); // 用于输出,依赖\0 101 int read_n = read(sockfd, buf, BUF_SIEE); 102 std::cout << buf << std::endl; 103 std::cout << read_n << " chars read." << std::endl; 104 105 // close sockets 106 close(sockfd); 107 108 return 0; 109 } 110 111 // ref: 112 // http://www.linuxhowtos.org/C_C++/socket.htm 113