| Rev | Author | # | Line |
|---|---|---|---|
| 1 | PerryLorier | 1 | InNeedOfRefactor |
| 2 | |||
| 3 | AristotlePagaltzis | 3 | This page is deprecated, see NetworkProgramming |
| 1 | PerryLorier | 4 | |
| 5 | This is to describe how to write networking programs in [C] using the [BSD] Socket Layer [API]. This mostly applies to almost all other languages with only minor modifications. | ||
| 6 | |||
| 7 | !!!General structure for a server setup | ||
| 8 | # Create a socket of the protocol family (Inet, Inet6, Unix etc). This is usually done with a socket(2) call, but can also be created with socketpair(2). You really should read the socket(2) man page for the differnet protocol families. | ||
| 9 | # Bind it to an address of the correct address family (Inet, Inet6, Unix etc). This is done with a bind(2) call. | ||
| 10 | # Listen on that address. This is done with a listen(2) call. | ||
| 11 | # Accept a client connection. This is done with an accept(2) call. | ||
| 12 | |||
| 13 | !!!General structure for a client setup | ||
| 14 | # Create a socket of the correct protocol family (inet, inet6, unix etc) | ||
| 15 | # Connect to an address of the correct address family (Inet, Inet6, Unix etc), this may involve gethostbyname(3) for internet domains. | ||
| 16 | |||
| 17 | !!!How to create a [TCP] Server (tcp(7)) | ||
| 18 | ;1:[TCP] is a streaming socket from the internet family of protocols, so first we create a socket of the correct type: | ||
| 19 | struct protoent *protocol; | ||
| 20 | int serverfd; | ||
| 21 | |||
| 22 | protocol=getprotobyname("tcp"); | ||
| 23 | if (!protocol) { | ||
| 24 | printf("Could not getprotobyname"); | ||
| 25 | !exit(1); | ||
| 26 | } | ||
| 27 | server=socket(PF_INET,SOCK_STREAM,protocol->p_proto); | ||
| 28 | if (server<0) { | ||
| 29 | perror("socket"); | ||
| 30 | !exit(2); | ||
| 31 | } | ||
| 2 | ColinMcKinnon | 32 | |
| 1 | PerryLorier | 33 | ;2: Then we create an address to bind it to. We don't care which of our local IP's we bind it to so we choose "INADDR_ANY" (all interfaces) |
| 34 | struct sockaddr_in address; | ||
| 35 | address.sin_family = AF_INET; | ||
| 36 | address.sin_port = htons(''port goes here'') | ||
| 37 | address.sin_addr.s_addr = INADDR_ANY; | ||
| 38 | |||
| 39 | ;3: Then we bind the port to that socket. | ||
| 40 | if (bind(serverfd,&address,sizeof(address))<0) { | ||
| 41 | perror("bind"); | ||
| 42 | !exit(3); | ||
| 43 | } | ||
| 44 | |||
| 45 | ;4: Now we start listening on that port. The 5 is the number of outstanding connections that will be queued by the kernel before you get a chance to accept(2) them. | ||
| 46 | if (listen(serverfd,5)<0) { | ||
| 47 | perror("listen"); | ||
| 48 | !exit(4); | ||
| 49 | } | ||
| 50 | |||
| 51 | ;5: And now we wait for an incoming connection | ||
| 52 | int sockfd; | ||
| 53 | struct sockaddr_in clientaddress; | ||
| 54 | |||
| 55 | sockfd=accept(serverfd,&clientaddress,sizeof(clientaddress)); | ||
| 56 | |||
| 57 | if (sockfd<0) { | ||
| 58 | perror("accept"); | ||
| 59 | !exit(5); | ||
| 60 | } | ||
| 61 | |||
| 62 | This creates a new socket (called "sockfd") that we can talk to the client that connected with. We can go back and do the accept(2) again to get another client socket etc. | ||
| 63 | |||
| 64 | !!!How to create a [TCP] Client. (tcp(7)) | ||
| 65 | ;1: First you need to create a socket as in TCP Server step 1 above, note the rest of this example uses "sockfd" not "serverfd" | ||
| 66 | |||
| 67 | ;2: Optionally you can bind the socket to a local address using step 2 and 3 above. Note, this isn't necessary, or usually even desired. | ||
| 68 | |||
| 69 | ;3: Next you want to create an address to connect out to: | ||
| 70 | struct sockaddr_in address; | ||
| 71 | |||
| 72 | address.sin_family = AF_INET; | ||
| 73 | address.sin_port = htons(''port goes here''); | ||
| 74 | |||
| 75 | struct hostent *host; | ||
| 76 | int i=0; | ||
| 77 | int success=0; | ||
| 78 | host = gethostbyname("''host.name.goes.here''"); | ||
| 79 | if (!host) { | ||
| 80 | fprintf(stderr,"gethostbyname: %s",hstrerror(herrno)); | ||
| 81 | !exit(5); | ||
| 82 | } | ||
| 83 | for (i=0;i<host->h_length;i++) { | ||
| 84 | if(connect(sockfd,&address,sizeof(address))==0) { | ||
| 85 | success=1; | ||
| 86 | break; | ||
| 87 | } | ||
| 88 | fprintf(stderr,"connect(%s):%s",inet_ntoa(address.sin_addr),strerror(errno)); | ||
| 89 | } | ||
| 90 | if (!success) { | ||
| 91 | fprintf(stderr,"hostname is unreachable"); | ||
| 92 | !exit(6); | ||
| 93 | } | ||
| 94 | |||
| 95 | Done! | ||
| 96 | |||
| 97 | !!!How to talk over a [TCP] socket. (tcp(7)) | ||
| 98 | Now, after you have [TCP] connection, either by having accept(2) or connect(2) return, you want to send traffic over it. | ||
| 99 | |||
| 100 | !!To Read Data: | ||
| 101 | char buffer[[65535]; | ||
| 102 | int bytes; | ||
| 103 | bytes=read(sockfd,buffer,sizeof(buffer)); | ||
| 104 | if (bytes<0) { | ||
| 105 | perror("read"); | ||
| 106 | } | ||
| 107 | else if (bytes==0) { | ||
| 108 | close(sockfd); | ||
| 109 | printf("Socket closed"); | ||
| 110 | exit(0); | ||
| 111 | } | ||
| 112 | ''process data'' | ||
| 113 | |||
| 114 | The number of bytes read is in "bytes", if bytes is 0, then the other end closed the connection. | ||
| 115 | |||
| 116 | !!To Write Data | ||
| 117 | char buffer[[65535]; | ||
| 118 | int bytes; | ||
| 119 | int remaining; | ||
| 120 | ''populate buffer with data, set bytes to being the number of bytes to write, eg: | ||
| 121 | strcpy(buffer,"Hello World"); | ||
| 122 | bytes=strlen(buffer); '' | ||
| 123 | remaining=bytes; | ||
| 124 | while(remaining>0) { | ||
| 125 | ret=write(sockfd,buffer+(bytes-remaining),remaining); | ||
| 126 | if (ret<0) { | ||
| 127 | perror("write"); | ||
| 128 | close(ret); | ||
| 129 | !exit(8); | ||
| 130 | } | ||
| 131 | remaining=remaining-ret; | ||
| 132 | } | ||
| 133 | |||
| 134 | |||
| 135 | !!!How to write a [UDP] Server/Client (udp(7)) | ||
| 136 | ;1: Creating [UDP] is similar to [TCP] above, but instead of using SOCK_STREAM you use SOCK_DGRAM, and instead of "tcp" you use "udp" (obviously). | ||
| 137 | |||
| 138 | ;2: If you are writing a server style UDP socket, then you probably care which port you connect to, so you bind as you do in the [TCP] example. This step is optional. | ||
| 139 | |||
| 140 | ;3: If you are only going to send to one host you can use connect(2) (as above) to bind the other end of the connection to a socket. This step is optional. | ||
| 141 | |||
| 142 | ;4: to send: | ||
| 143 | * You can use write(2), this assumes that you have used connect(2) above as you can't specify the destination, and that you don't want any special processing. | ||
| 144 | * You can use send(2), this assumes that you have used connect(2) above, but you can specify special flags | ||
| 145 | * You can use sendto(2) to send to a specific address and with specific flags. | ||
| 146 | ;: Remember: | ||
| 147 | * You have to send your entire message in one operation, multiple send(2)'s/write(2)/sendto(2) will be sent in seperate packets | ||
| 148 | * UDP is unreliable, your packet may not arrive at the other end, and multiple packets may arrive out of order. | ||
| 149 | * UDP has no flow control, you could flood the other end of the link with data, or overload intermediate bandwidth (usually your own) | ||
| 150 | |||
| 151 | ;5: to recieve: | ||
| 152 | * You can use read(2) when you really don't care about where it comes from or want to do any special processing. | ||
| 153 | * You can use recv(2) when you don't care where the data comes from but you can do special processing (PEEKing etc) | ||
| 154 | * You can use recvfrom(2) when you want to know where the packet came from. | ||
| 155 | |||
| 156 | !!!Differences between [TCP]/[UDP] (ip(7)) and Unix Domain Sockets (unix(7)) | ||
| 157 | Unix domain sockets can be stream orientated (like [TCP]) or datagram orientated (like [UDP]). The only differences are: | ||
| 158 | * You use PF_UNIX for the protocol family instead of PF_INET | ||
| 159 | * Instead of sockaddr_in, you use sockaddr_un. | ||
| 160 | * You use AF_UNIX for the address family instead of AF_INET. | ||
| 161 | * sockaddr_un doesn't have sin_port or sin_addr, but sun_path which is a string of where to put the socket, for instance: | ||
| 162 | struct sockaddr_un address; | ||
| 163 | address.sun_family=AF_UNIX; | ||
| 164 | strcpy(address.sun_path,"/tmp/.``mysock``); | ||
| 165 | * Remember to unlink(2) your sockets when your finished with them, bind(2) will fail if a socket already exists. | ||
| 166 | * Unix domain sockets are reliable (even the datagrams) | ||
| 167 | * Instead of creating a socket with socket(2)/bind(2)/connect(2) etc you can create one with socketpair(2) | ||
| 168 | * You can pass file descriptors over unix domain sockets. | ||
| 169 | |||
| 170 | !!!Tricks 'n Traps | ||
| 171 | * Don't read(2) or write(2) on a server socket, it won't work, you have to use the client socket returned from accept(2) when writing servers. | ||
| 172 | * fdopen(3) gives you a FILE * from a fd, meaning you can use a socket like a normal file, with fread(3), fscanf(3) etc. | ||
| 173 | * ports and addresses are in NetworkByteOrder, so you should use htons(3) on ports, and htonl(3) on [IPv4] addresses. This is very evident on IBM [PC]'s since they are LittleEndian | ||
| 174 | * [UDP] and [TCP] ports below 1024 can only be bound by the superuser, and people with CAP_NET_BIND_SERVICE. | ||
| 175 | * Port 0 means "any available port" and will be assigned by the kernel. | ||
| 176 | * Port 1 to 511 are available for root only services. These services are considered "trusted" because only root can run them, so normal users can't "hijack" the port. | ||
| 177 | * Port 512 to 1023 are assigned by the kernel when a root owned program asks for "any available port" | ||
| 178 | * Port 1024 to 4999 are assigned by the kernel when a non-root owned program asks for "any available port" | ||
| 179 | * Port 5000+ are available for use by non-root owned programs to bind to. | ||
| 180 | |||
| 181 | !!!Network server programming styles (and examples). | ||
| 182 | The traditional Unix server programming style is the fork(2)'ing server. This means you have one server which accept(2)'s on a socket, and then when a client connects fork(2)'s a new process to deal with that connection, and then returns around the loop and accept(2)'s again. For example, a hello world server: | ||
| 183 | #include <sys/types.h> | ||
| 184 | #include <sys/socket.h> | ||
| 185 | #include <netinet/in.h> | ||
| 186 | #include <netdb.h> | ||
| 187 | |||
| 188 | /* make_tcp_socket | ||
| 189 | * parameters: | ||
| 190 | * none | ||
| 191 | * returns: | ||
| 192 | * a tcp socket on success | ||
| 193 | * -1 on failure | ||
| 194 | * side effects: | ||
| 195 | * may output error messages to stderr on failure | ||
| 196 | * creates a tcp socket | ||
| 197 | */ | ||
| 198 | int make_tcp_socket(void) { | ||
| 199 | struct protoent *protocol; | ||
| 200 | int serverfd; | ||
| 201 | |||
| 202 | protocol=getprotobyname("tcp"); | ||
| 203 | if (!protocol) { | ||
| 204 | fprintf(stderr,"Could not getprotobyname"); | ||
| 205 | return -1; | ||
| 206 | } | ||
| 207 | server=socket(PF_INET,SOCK_STREAM,protocol.p_proto); | ||
| 208 | if (server<0) { | ||
| 209 | perror("socket"); | ||
| 210 | return -1; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | /* get_port | ||
| 215 | * parameters: | ||
| 216 | * servername, the name of a service to look up in /etc/services | ||
| 217 | * protocol, the name of a protocol from /etc/protocols (eg: "tcp", "udp") | ||
| 218 | * defaultport, optional default port to use if the service is not found in /etc/services | ||
| 219 | * returns: | ||
| 220 | * a port, in network byte order | ||
| 221 | * or | ||
| 222 | * -1 on failure | ||
| 223 | * side effects: | ||
| 224 | * may output a message on stderr for failures | ||
| 225 | */ | ||
| 226 | int get_port(char *servername,char *protocol,int defaultport=0) | ||
| 227 | { | ||
| 228 | struct servent *service; | ||
| 229 | service = getservbyname(servername,protocol); | ||
| 230 | |||
| 231 | /* we found the service! */ | ||
| 232 | if (service) | ||
| 233 | return service->s_port; | ||
| 234 | /* We didn't find it, is the port valid? */ | ||
| 235 | else if (port>0 && port<65535) | ||
| 236 | return htons(port); | ||
| 237 | /* Give up */ | ||
| 238 | else { | ||
| 239 | fprintf(stderr,"Can't find port for service \"%s\"",servername); | ||
| 240 | return -1; | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | /* make_tcp_server | ||
| 245 | * parameters: | ||
| 246 | * servername, a service from /etc/services | ||
| 247 | * port, optional port to use if the service is not found in /etc/services | ||
| 248 | * returns: | ||
| 249 | * a tcp socket bound to the correct port on all interfaces | ||
| 250 | * side effects: | ||
| 251 | * may output messages to stderr on failures | ||
| 252 | * creates a tcp socket which the client will need to close when finished with | ||
| 253 | */ | ||
| 254 | int make_tcp_server(char *servername,int port=0) | ||
| 255 | { | ||
| 256 | int serverfd; | ||
| 257 | struct sockaddr_in address; | ||
| 258 | |||
| 259 | serverfd = make_tcp_socket(); | ||
| 260 | if (serverfd<0) | ||
| 261 | return -1; | ||
| 262 | |||
| 263 | port = get_port(servername,"tcp",port); | ||
| 264 | |||
| 265 | if (port<0) { | ||
| 266 | fprintf(stderr,"Can not look up port for service \"%s\", try adding it to /etc/services\n",servername); | ||
| 267 | close(serverfd); | ||
| 268 | return -1; | ||
| 269 | } | ||
| 270 | |||
| 271 | /* Create an address structure to bind to */ | ||
| 272 | address.sin_family = AF_INET; | ||
| 273 | address.sin_port | ||
| 274 | address.sin_addr.s_addr=INADDR_ANY; | ||
| 275 | |||
| 276 | /* Bind to the port */ | ||
| 277 | if (bind(serverfd,&address,sizeof(address))<0) { | ||
| 278 | perror("bind"); | ||
| 279 | close(serverfd); | ||
| 280 | return -1; | ||
| 281 | } | ||
| 282 | |||
| 283 | /* Listen on the socket */ | ||
| 284 | if (listen(serverfd,5)<0) { | ||
| 285 | perror("listen"); | ||
| 286 | close(serverfd); | ||
| 287 | return -1; | ||
| 288 | } | ||
| 289 | |||
| 290 | return serverfd; | ||
| 291 | } | ||
| 292 | |||
| 293 | /* Whatever you want to do for clients */ | ||
| 294 | int handle_client(int clientfd) | ||
| 295 | { | ||
| 296 | char *message = "Hello World\n"; | ||
| 297 | write(clientfd,message,strlen(message)); | ||
| 298 | close(clientfd); | ||
| 299 | exit(0); | ||
| 300 | } | ||
| 301 | |||
| 302 | int main(int argc,char **argv) | ||
| 303 | { | ||
| 304 | int serverport; | ||
| 305 | /* Create the server socket */ | ||
| 306 | serverfd = make_tcp_server("hello",5432); | ||
| 307 | if (serverfd<0) | ||
| 308 | return 1; | ||
| 309 | |||
| 310 | /* The main loop */ | ||
| 311 | for(;;) { | ||
| 312 | int clientfd; | ||
| 313 | |||
| 314 | /* Accept a connection */ | ||
| 315 | clientfd = accept(serverfd); | ||
| 316 | if (clientfd<0) { | ||
| 317 | perror("accept"); | ||
| 318 | /* Don't just abort here, since it's fairly common to get an accept(2) failure, especially when people are portscanning */ | ||
| 319 | } | ||
| 320 | else if (fork()==0) { | ||
| 321 | /* Client "Thread" */ | ||
| 322 | /* The client doesn't need the server fd anymore, so close it */ | ||
| 323 | close(serverfd); | ||
| 324 | handle_client(clientfd); | ||
| 325 | } | ||
| 326 | else { | ||
| 327 | /* Server "Thread" */ | ||
| 328 | /* Important: Close the fd, otherwise we'll eventually run out of file descripters after we've processed enough clients */ | ||
| 329 | close(clientfd); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | return 0; /* NOT REACHED */ | ||
| 333 | } |
lib/blame.php:177: Warning: Invalid argument supplied for foreach()