-
Notifications
You must be signed in to change notification settings - Fork 6
/
tcp-client.c
156 lines (142 loc) · 5.79 KB
/
tcp-client.c
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <errno.h>
#include <fcntl.h> // for opening socket
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h> // for closing socket
#include <sys/socket.h>
#include <sys/types.h>
#define BUFFER_SIZE 1024
/**
* The entrance of the server application.
*
* @param argc the number of arguments
* @param argv a pointer to a char array that stores arguments
* @return 0 if the application exited normally
*/
int main(int argc, char *argv[]) {
if ( argc != 3 ) {
fprintf(stderr," Usage: %s Host PortNumber\n",argv[0]);
return EXIT_FAILURE;
}
struct hostent* pHost = gethostbyname(argv[1]);
if ( pHost == NULL ) {
fprintf(stderr, "Usage: %s Host PortNumber\n", argv[0]);
return EXIT_FAILURE;
}
int portNumber = atoi(argv[2]);
if ( portNumber <= 0 ) {
fprintf(stderr, "Usage: %s Host PortNumber\n", argv[0]);
return EXIT_FAILURE;
}
/*
* Create socket file descriptor.
* Function Prototype: int socket(int domain, int type,int protocol)
* Defined in sys/socket.h
*
* @param domain: AF_INET stands for Internet, AF_UNIX can only communicate between UNIX systems.
* @param type the prototype to use, SOCK_STREAM stands for TCP and SOCK_DGRAM stands for UDP
* @param protocol if type is specified, this parameter can be assigned to 0.
* @return -1 if socket is failed to create
*/
int tcpSocketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if ( tcpSocketFileDescriptor == -1 ) {
fprintf(stderr, "[ERROR] Failed to create socket: %s\n", strerror(errno));
return EXIT_FAILURE;
}
/*
* Initialize sockaddr struct.
*
* The structure of sockaddr:
*
* struct sockaddr{
* unisgned short as_family;
* char sa_data[14];
* };
*
* To keep compatibility in different OS, sockaddr_in is used:
*
* struct sockaddr_in{
* unsigned short sin_family; // assigned to AF_INET generally
* unsigned short int sin_port; // the port number listens to
* struct in_addr sin_addr; // assigned to INADDR_ANY for communicating with any hosts
* unsigned char sin_zero[8]; // stuffing bits
* };
*
* Both of them are defined in netinet/in.h
*/
socklen_t sockaddrSize = sizeof(struct sockaddr);
struct sockaddr_in serverSocketAddress;
bzero(&serverSocketAddress, sockaddrSize);
serverSocketAddress.sin_family = AF_INET;
serverSocketAddress.sin_addr=*((struct in_addr *)pHost->h_addr);
serverSocketAddress.sin_port = htons(portNumber);
/*
* Connect to server.
* Function prototype: int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
* Defined in sys/socket.h and sys/types.h
*
* @param sockfd the socket file descriptor
* @param my_addr the specified address of server
* @param addrlen the size of the struct sockaddr
* @return -1 if the operation failed
*/
if ( connect(tcpSocketFileDescriptor, (struct sockaddr *)(&serverSocketAddress), sockaddrSize) == -1 ) {
fprintf(stderr, "[ERROR] Failed to connect to server: %s\n", strerror(errno));
return EXIT_FAILURE;
}
char inputBuffer[BUFFER_SIZE] = {0};
char outputBuffer[BUFFER_SIZE] = {0};
fprintf(stderr, "[INFO] Congratulations! Connection established with server.\nType \'BYE\' to disconnect.\n");
do {
// Send a message to server
fprintf(stderr, "> ");
fgets(outputBuffer, BUFFER_SIZE, stdin);
// Remove \n character at the end of the string
outputBuffer[strlen(outputBuffer) - 1] = 0;
if ( send(tcpSocketFileDescriptor, outputBuffer, strlen(outputBuffer) + 1, 0) == -1 ) {
fprintf(stderr, "[ERROR] An error occurred while sending message to the server: %s\nThe connection is going to close.\n", strerror(errno));
break;
}
if ( strcmp("BYE", outputBuffer) == 0 ) {
// Stop sending message to server
break;
} else if ( strncmp("GET", outputBuffer, 3) ==0 ) {
// Receive a message to confirm whether the file exists
recv(tcpSocketFileDescriptor, inputBuffer, BUFFER_SIZE, 0);
if ( strncmp("ACCEPT", inputBuffer, 6) != 0 ) {
fprintf(stderr, "[WARN] Server refused to send this file. Maybe file does not exist.\n");
continue;
}
// Receive file stream
fprintf(stderr, "> Save to: ");
scanf("%s", outputBuffer);
FILE* outputFile = fopen(outputBuffer, "wb");
int readBytes = 0;
while ( (readBytes = recv(tcpSocketFileDescriptor, inputBuffer, BUFFER_SIZE, 0)) > 0 ) {
fprintf(stderr, "[INFO] Received %lu bytes\n", strlen(inputBuffer));
fwrite(inputBuffer, sizeof(char), readBytes, outputFile);
memset(inputBuffer, 0, BUFFER_SIZE);
if ( readBytes < BUFFER_SIZE ) {
break;
}
}
fclose(outputFile);
} else {
// Receive a message from client
int readBytes = recv(tcpSocketFileDescriptor, inputBuffer, BUFFER_SIZE, 0);
if ( readBytes < 0 ) {
fprintf(stderr, "[ERROR] An error occurred while receiving message from the server: %s\nThe connection is going to close.\n", strerror(errno));
break;
}
fprintf(stderr, "[INFO] Received a message from server: %s\n", inputBuffer);
memset(inputBuffer, 0, BUFFER_SIZE);
}
} while ( 1 );
/*
* Close socket for client.
*/
close(tcpSocketFileDescriptor);
return EXIT_SUCCESS;
}