├── README.md ├── client.cpp └── server.cpp /README.md: -------------------------------------------------------------------------------- 1 | # C++ Sockets - Simple server and client chat (linux) 2 | 3 | A simple socket programming which creates a connection between two terminals on linux. This was my second semester final project, so I thought I'll share with you. 4 | 5 | Video: https://www.youtube.com/watch?v=IydkqseK6oQ 6 | 7 | # Requirements 8 | 9 | 1. Ubuntu 12.0 LTS or higher 10 | 2. G++ compiler for Ubuntu 11 | 3. A text editor 12 | 13 | # Compilation 14 | 15 | 1. Compile the server.cpp file first and then the client.cpp file. 16 | 2. To send a message, run your client.cpp and type "your-message *" *Notice that you need to add an asterisk at the end of each sentence to send a message. 17 | 3. Same goes for server.cpp 18 | 19 | # Limitations 20 | 21 | This code is limited to one client only. There is only one-to-one connection. To have multiple connections you need to know about threading. 22 | -------------------------------------------------------------------------------- /client.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * Simple chat program (client side).cpp - http://github.com/hassanyf 3 | * Version - 2.0.1 4 | * 5 | * Copyright (c) 2016 Hassan M. Yousuf 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace std; 19 | 20 | int main() 21 | { 22 | /* ---------- INITIALIZING VARIABLES ---------- */ 23 | 24 | /* 25 | 1. client is a file descriptor to store the values 26 | returned by the socket system call and the accept 27 | system call. 28 | 29 | 2. portNum is for storing port number on which 30 | the accepts connections 31 | 32 | 3. isExit is bool variable which will be used to 33 | end the loop 34 | 35 | 4. The client reads characters from the socket 36 | connection into a dynamic variable (buffer). 37 | 38 | 5. A sockaddr_in is a structure containing an internet 39 | address. This structure is already defined in netinet/in.h, so 40 | we don't need to declare it again. 41 | 42 | DEFINITION: 43 | 44 | struct sockaddr_in 45 | { 46 | short sin_family; 47 | u_short sin_port; 48 | struct in_addr sin_addr; 49 | char sin_zero[8]; 50 | }; 51 | 52 | 6. serv_addr will contain the address of the server 53 | 54 | */ 55 | 56 | int client; 57 | int portNum = 1500; // NOTE that the port number is same for both client and server 58 | bool isExit = false; 59 | int bufsize = 1024; 60 | char buffer[bufsize]; 61 | char* ip = "127.0.0.1"; 62 | 63 | struct sockaddr_in server_addr; 64 | 65 | client = socket(AF_INET, SOCK_STREAM, 0); 66 | 67 | /* ---------- ESTABLISHING SOCKET CONNECTION ----------*/ 68 | /* --------------- socket() function ------------------*/ 69 | 70 | if (client < 0) 71 | { 72 | cout << "\nError establishing socket..." << endl; 73 | exit(1); 74 | } 75 | 76 | /* 77 | The socket() function creates a new socket. 78 | It takes 3 arguments, 79 | 80 | a. AF_INET: address domain of the socket. 81 | b. SOCK_STREAM: Type of socket. a stream socket in 82 | which characters are read in a continuous stream (TCP) 83 | c. Third is a protocol argument: should always be 0. The 84 | OS will choose the most appropiate protocol. 85 | 86 | This will return a small integer and is used for all 87 | references to this socket. If the socket call fails, 88 | it returns -1. 89 | 90 | */ 91 | 92 | cout << "\n=> Socket client has been created..." << endl; 93 | 94 | /* 95 | The variable serv_addr is a structure of sockaddr_in. 96 | sin_family contains a code for the address family. 97 | It should always be set to AF_INET. 98 | 99 | htons() converts the port number from host byte order 100 | to a port number in network byte order. 101 | 102 | */ 103 | 104 | server_addr.sin_family = AF_INET; 105 | server_addr.sin_port = htons(portNum); 106 | 107 | // this function returns returns 1 if the IP is valid 108 | // and 0 if invalid 109 | // inet_pton converts IP to packets 110 | // inet_ntoa converts back packets to IP 111 | //inet_pton(AF_INET, ip, &server_addr.sin_addr); 112 | 113 | /*if (connect(client,(struct sockaddr *)&server_addr, sizeof(server_addr)) == 0) 114 | cout << "=> Connection to the server " << inet_ntoa(server_addr.sin_addr) << " with port number: " << portNum << endl;*/ 115 | 116 | 117 | /* ---------- CONNECTING THE SOCKET ---------- */ 118 | /* ---------------- connect() ---------------- */ 119 | 120 | if (connect(client,(struct sockaddr *)&server_addr, sizeof(server_addr)) == 0) 121 | cout << "=> Connection to the server port number: " << portNum << endl; 122 | 123 | /* 124 | The connect function is called by the client to 125 | establish a connection to the server. It takes 126 | three arguments, the socket file descriptor, the 127 | address of the host to which it wants to connect 128 | (including the port number), and the size of this 129 | address. 130 | 131 | This function returns 0 on success and -1 132 | if it fails. 133 | 134 | Note that the client needs to know the port number of 135 | the server but not its own port number. 136 | */ 137 | 138 | cout << "=> Awaiting confirmation from the server..." << endl; //line 40 139 | recv(client, buffer, bufsize, 0); 140 | cout << "=> Connection confirmed, you are good to go..."; 141 | 142 | cout << "\n\n=> Enter # to end the connection\n" << endl; 143 | 144 | // Once it reaches here, the client can send a message first. 145 | 146 | do { 147 | cout << "Client: "; 148 | do { 149 | cin >> buffer; 150 | send(client, buffer, bufsize, 0); 151 | if (*buffer == '#') { 152 | send(client, buffer, bufsize, 0); 153 | *buffer = '*'; 154 | isExit = true; 155 | } 156 | } while (*buffer != 42); 157 | 158 | cout << "Server: "; 159 | do { 160 | recv(client, buffer, bufsize, 0); 161 | cout << buffer << " "; 162 | if (*buffer == '#') { 163 | *buffer = '*'; 164 | isExit = true; 165 | } 166 | 167 | } while (*buffer != 42); 168 | cout << endl; 169 | 170 | } while (!isExit); 171 | 172 | /* ---------------- CLOSE CALL ------------- */ 173 | /* ----------------- close() --------------- */ 174 | 175 | /* 176 | Once the server presses # to end the connection, 177 | the loop will break and it will close the server 178 | socket connection and the client connection. 179 | */ 180 | 181 | cout << "\n=> Connection terminated.\nGoodbye...\n"; 182 | 183 | close(client); 184 | return 0; 185 | } -------------------------------------------------------------------------------- /server.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * Simple chat program (server side).cpp - http://github.com/hassanyf 3 | * Version - 2.0.1 4 | * 5 | * Copyright (c) 2016 Hassan M. Yousuf 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace std; 18 | 19 | int main() 20 | { 21 | /* ---------- INITIALIZING VARIABLES ---------- */ 22 | 23 | /* 24 | 1. client/server are two file descriptors 25 | These two variables store the values returned 26 | by the socket system call and the accept system call. 27 | 28 | 2. portNum is for storing port number on which 29 | the accepts connections 30 | 31 | 3. isExit is bool variable which will be used to 32 | end the loop 33 | 34 | 4. The server reads characters from the socket 35 | connection into a dynamic variable (buffer). 36 | 37 | 5. A sockaddr_in is a structure containing an internet 38 | address. This structure is already defined in netinet/in.h, so 39 | we don't need to declare it again. 40 | 41 | DEFINITION: 42 | 43 | struct sockaddr_in 44 | { 45 | short sin_family; 46 | u_short sin_port; 47 | struct in_addr sin_addr; 48 | char sin_zero[8]; 49 | }; 50 | 51 | 6. serv_addr will contain the address of the server 52 | 53 | 7. socklen_t is an intr type of width of at least 32 bits 54 | 55 | 56 | */ 57 | int client, server; 58 | int portNum = 1500; 59 | bool isExit = false; 60 | int bufsize = 1024; 61 | char buffer[bufsize]; 62 | 63 | struct sockaddr_in server_addr; 64 | socklen_t size; 65 | 66 | /* ---------- ESTABLISHING SOCKET CONNECTION ----------*/ 67 | /* --------------- socket() function ------------------*/ 68 | 69 | client = socket(AF_INET, SOCK_STREAM, 0); 70 | 71 | if (client < 0) 72 | { 73 | cout << "\nError establishing socket..." << endl; 74 | exit(1); 75 | } 76 | 77 | /* 78 | The socket() function creates a new socket. 79 | It takes 3 arguments, 80 | 81 | a. AF_INET: address domain of the socket. 82 | b. SOCK_STREAM: Type of socket. a stream socket in 83 | which characters are read in a continuous stream (TCP) 84 | c. Third is a protocol argument: should always be 0. The 85 | OS will choose the most appropiate protocol. 86 | 87 | This will return a small integer and is used for all 88 | references to this socket. If the socket call fails, 89 | it returns -1. 90 | 91 | */ 92 | 93 | cout << "\n=> Socket server has been created..." << endl; 94 | 95 | /* 96 | The variable serv_addr is a structure of sockaddr_in. 97 | sin_family contains a code for the address family. 98 | It should always be set to AF_INET. 99 | 100 | INADDR_ANY contains the IP address of the host. For 101 | server code, this will always be the IP address of 102 | the machine on which the server is running. 103 | 104 | htons() converts the port number from host byte order 105 | to a port number in network byte order. 106 | 107 | */ 108 | 109 | server_addr.sin_family = AF_INET; 110 | server_addr.sin_addr.s_addr = htons(INADDR_ANY); 111 | server_addr.sin_port = htons(portNum); 112 | 113 | /* ---------- BINDING THE SOCKET ---------- */ 114 | /* ---------------- bind() ---------------- */ 115 | 116 | 117 | if ((bind(client, (struct sockaddr*)&server_addr,sizeof(server_addr))) < 0) 118 | { 119 | cout << "=> Error binding connection, the socket has already been established..." << endl; 120 | return -1; 121 | } 122 | 123 | /* 124 | The bind() system call binds a socket to an address, 125 | in this case the address of the current host and port number 126 | on which the server will run. It takes three arguments, 127 | the socket file descriptor. The second argument is a pointer 128 | to a structure of type sockaddr, this must be cast to 129 | the correct type. 130 | */ 131 | 132 | size = sizeof(server_addr); 133 | cout << "=> Looking for clients..." << endl; 134 | 135 | /* ------------- LISTENING CALL ------------- */ 136 | /* ---------------- listen() ---------------- */ 137 | 138 | listen(client, 1); 139 | 140 | /* 141 | The listen system call allows the process to listen 142 | on the socket for connections. 143 | 144 | The program will be stay idle here if there are no 145 | incomming connections. 146 | 147 | The first argument is the socket file descriptor, 148 | and the second is the size for the number of clients 149 | i.e the number of connections that the server can 150 | handle while the process is handling a particular 151 | connection. The maximum size permitted by most 152 | systems is 5. 153 | 154 | */ 155 | 156 | /* ------------- ACCEPTING CLIENTS ------------- */ 157 | /* ----------------- listen() ------------------- */ 158 | 159 | /* 160 | The accept() system call causes the process to block 161 | until a client connects to the server. Thus, it wakes 162 | up the process when a connection from a client has been 163 | successfully established. It returns a new file descriptor, 164 | and all communication on this connection should be done 165 | using the new file descriptor. The second argument is a 166 | reference pointer to the address of the client on the other 167 | end of the connection, and the third argument is the size 168 | of this structure. 169 | */ 170 | 171 | int clientCount = 1; 172 | server = accept(client,(struct sockaddr *)&server_addr,&size); 173 | 174 | // first check if it is valid or not 175 | if (server < 0) 176 | cout << "=> Error on accepting..." << endl; 177 | 178 | while (server > 0) 179 | { 180 | strcpy(buffer, "=> Server connected...\n"); 181 | send(server, buffer, bufsize, 0); 182 | cout << "=> Connected with the client #" << clientCount << ", you are good to go..." << endl; 183 | cout << "\n=> Enter # to end the connection\n" << endl; 184 | 185 | /* 186 | Note that we would only get to this point after a 187 | client has successfully connected to our server. 188 | This reads from the socket. Note that the read() 189 | will block until there is something for it to read 190 | in the socket, i.e. after the client has executed a 191 | the send(). 192 | 193 | It will read either the total number of characters 194 | in the socket or 1024 195 | */ 196 | 197 | cout << "Client: "; 198 | do { 199 | recv(server, buffer, bufsize, 0); 200 | cout << buffer << " "; 201 | if (*buffer == '#') { 202 | *buffer = '*'; 203 | isExit = true; 204 | } 205 | } while (*buffer != '*'); 206 | 207 | do { 208 | cout << "\nServer: "; 209 | do { 210 | cin >> buffer; 211 | send(server, buffer, bufsize, 0); 212 | if (*buffer == '#') { 213 | send(server, buffer, bufsize, 0); 214 | *buffer = '*'; 215 | isExit = true; 216 | } 217 | } while (*buffer != '*'); 218 | 219 | cout << "Client: "; 220 | do { 221 | recv(server, buffer, bufsize, 0); 222 | cout << buffer << " "; 223 | if (*buffer == '#') { 224 | *buffer == '*'; 225 | isExit = true; 226 | } 227 | } while (*buffer != '*'); 228 | } while (!isExit); 229 | 230 | /* 231 | Once a connection has been established, both ends 232 | can both read and write to the connection. Naturally, 233 | everything written by the client will be read by the 234 | server, and everything written by the server will be 235 | read by the client. 236 | */ 237 | 238 | /* ---------------- CLOSE CALL ------------- */ 239 | /* ----------------- close() --------------- */ 240 | 241 | /* 242 | Once the server presses # to end the connection, 243 | the loop will break and it will close the server 244 | socket connection and the client connection. 245 | */ 246 | 247 | // inet_ntoa converts packet data to IP, which was taken from client 248 | cout << "\n\n=> Connection terminated with IP " << inet_ntoa(server_addr.sin_addr); 249 | close(server); 250 | cout << "\nGoodbye..." << endl; 251 | isExit = false; 252 | exit(1); 253 | } 254 | 255 | close(client); 256 | return 0; 257 | } --------------------------------------------------------------------------------