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 | } |
lib/blame.php:177: Warning: Invalid argument supplied for foreach()