本文中的代碼是我在學習linux套接字編程時寫的,運行環境爲RedHat,編譯環境爲G++linux
測試環境有兩臺linux虛擬機,地址分別設爲192.168.8.123和192.168.8.99編程
其中,192.168.8.123上運行了客戶端,其源碼(client.cpp)爲服務器
#include<sys/types.h> //data types #include<sys/socket.h> //main sockets header #include<stdio.h> //standard buffered input/output #include<netinet/in.h> //Internet address family #include<arpa/inet.h> //definitions for internet operations #include<unistd.h> //standard symbolic constants and types #include<stdlib.h> //standard library definitions #include<string.h> //string operions int main() { struct sockaddr_in address; //#include<netinet/in.h> //struct sockaddr_in includes at least the following member: // sa_family_t sin_family AF_INET // in_port_t sin_port Port number // struct in_addr sin_addr IP address //The sockaddr_in structure is used to store addresses //for the Internet address family address.sin_family = AF_INET; address.sin_addr.s_addr = inet_addr("192.168.8.99"); address.sin_port = htons(9734); int len = sizeof(address); int sockfd = socket(AF_INET, SOCK_STREAM, 0); //#include<sys/types.h> //#include<sys/socket.h> //int socket(int domain, int type, int protocal); //0.socket() creates an endpoint for communication and returns a descriptor //1.[domain] argument specifies a communication domain // Name Purpose // AF_UNIX Local communication // AF_INET IPv4 Internet protocols //2.socket has the indicated [type] which specifies the communication semantics // SOCK_STREAM: Provides sequenced, reliable, two-way, // connection-based byte streams //3.The [protocol] specifies a particular protocol to be used with the socket. // Normally only a single protocol exists to support a particular socket // type within a given protocol family, in which case protocol can be // specified as 0 int result = connect(sockfd, (struct sockaddr *)&address, len); //#include<sys/types.h> //#include<sys/socket.h> //int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); //0.The connect() system call connects the socket referred to by the file // descriptor [sockfd] to the address specified by [addr]. The [addrlen] // argument specifies the size of [addr] //1.If the connection or binding succeeds, zero is returned. // On error, -1 is returned, and [errno] is set appropriately. if(result == -1) { perror("oops: client"); //#include<stdio.h> //void perror(const char *s); //0.The routine perror() produces a message on the standard // error output, describing the last error encountered // during a call to a system or library function. exit(1); //#include<stdlib.h> //void exit(int status); //0.The exit() function causes normal process termination // and the value of [status & 0377] is returned to the parent } char input[100]; printf("Please Enter Your Input: "); fgets(input, 100, stdin); for(int counter = 0; counter < strlen(input); counter++) { if(input[counter] == '\n') { input[counter] = '\0'; } } write(sockfd, input, 100); //#include<unistd.h> //ssize_t write(int fd, const void *buf, size_t count); //0.write() writes up to [count] bytes from the buffer pointed to the // file referred by the file descriptor [fd] //1.On success, the number of bytes written is returned. // On error, -1 is returned, and [errno] is set appropriately. printf("Send Input: %s\n", input); char output[100]; read(sockfd, output, 100); //#include<unistd.h> //ssize_t read(int fd, void *buf, size_t count); //0.read() attempts to read up to [count] bytes from file descriptor // [fd] into the buffer starting at [buf] //1.On success, the number of bytes read is returned, and the file position // is advanced by this number. // On error, -1 is returned, and [errno] is set appropriately. printf("Receive Output: %s\n", output); close(sockfd); //#include<unistd.h> //int close(int fd); //0.close() closes a file descriptor, so that it no longer refers to any // file and may be reused. Any record locks held on the file it was // associated with, and owned by the process, are removed. //1.close() returns zero on success. // On error, -1 is returned, and [errno] is set appropriately. exit(0); }
192.168.8.99上運行了服務器,其源碼(server.cpp)爲app
#include<sys/types.h> #include<sys/socket.h> #include<stdio.h> #include<netinet/in.h> #include<arpa/inet.h> #include<unistd.h> #include<stdlib.h> #include<string.h> int main() { int server_sockfd, client_sockfd; socklen_t server_len, client_len; struct sockaddr_in server_address; struct sockaddr_in client_address; server_sockfd = socket(AF_INET, SOCK_STREAM, 0); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = inet_addr("192.168.8.99"); server_address.sin_port = htons(9734); server_len = sizeof(server_address); bind(server_sockfd, (struct sockaddr *)&server_address, server_len); //#include<sys/types.h> //#include<sys/socket.h> //int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); //0.When a socket is create with socket(), it exists in a name space // (address family) but has no address assigned to it. bind() assigns // the address specified to by [addr] to the socket referred to by the // file descriptior [sockfd]. [addrlen] specifies the size, in bytes, // of the address structure pointed to by [addr]. // Traditionally, this operation is called "assigning a name to a socket" //1.On success, zero is returned. // On error, -1 is returned, and [errno] is set appropriately. listen(server_sockfd, 5); //#include<sys/types.h> //#include<sys/socket.h> //int listen(int sockfd, int backlog) //0.listen() marks the socket referred to by [sockfd] as a passive socket, // that is, as a socket that will be used to accept incoming connection // requests using accept() //1.The [sockfd] argument is a file descriptor that refers to a socket of // type SOCK_STREAM or SOCK_SEQPACKET //2.The [backlog] argument defines the maximum length to which queue of // pending connections for [sockfd] may grow //3.On success, zero is returned. // On error, -1 is returned, and [errno] is set appropriately. while(1) { printf("server waiting\n"); client_len = sizeof(client_address); client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len); //#include<sys/types.h> //#include<sys/socket.h> //int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); //0.The accept() system call is used with connection-based socket types // (SOCK_STREAM, SOCK_SEQPACKET). It extracts the first connection // request on the queue of pending connections for the listening socket, // [sockfd], creates a new connected socket, and returns a new file // descriptor referring to that socket. The newly created socket is // not in the listening state. The original socket [sockfd] is // unaffected by this call. char input[100]; read(client_sockfd, input, 100); printf("Receive Input: %s\n", input); char output[100] = "[RETURN] "; strcat(output, input); write(client_sockfd, output, 100); printf("Send Output: %s\n", output); close(client_sockfd); } }
編譯這兩個文件,分別輸入命令dom
g++ client.cpp -o client g++ server.cpp -o server
運行這兩個程序前,先要關閉linux的防火牆。socket
方法爲在su權限下輸入命令ide
iptables -F
先在198.168.8.99上運行服務器server,而後再在192.168.8.123上運行client。在客戶端輸入字符串Hello World!,服務器端收到了這個字符串後會在前面加入字符串「[RETURN] 」而後返回給客戶端。效果以下圖所示:oop
server端學習
client端測試
關於這兩段代碼,須要注意兩點:
1)由於TCP鏈接在最後有一個TIME_WAIT狀態,所以在server程序停掉後,要隔一分鐘打開,才能用client從新鏈接上
2)G++的檢查比GCC要嚴格,服務器server.cpp代碼中的server_len和client_len,不能聲明爲int類型,而要聲明爲socklen_t類型
END