Penguin
Blame: NetworkProgrammingOld
EditPageHistoryDiffInfoLikePages
Annotated edit history of NetworkProgrammingOld version 4, including all changes. View license author blame.
Rev Author # Line
3 AristotlePagaltzis 1 InNeedOfRefactor
1 PerryLorier 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 }
4 MattBrown 27 server=socket(PF_INET,SOCK_STREAM,protocol->p_proto);
1 PerryLorier 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 }

PHP Warning

lib/blame.php:177: Warning: Invalid argument supplied for foreach()