blob: 3c0768714bf7e69e3f8f4ce61ed9c4725c4ca424 [file] [log] [blame]
danb0b27ab2018-12-07 20:25:14 +00001/*
2** 2017 June 7
3**
4** The author disclaims copyright to this source code. In place of
5** a legal notice, here is a blessing:
6**
7** May you do good and not evil.
8** May you find forgiveness for yourself and forgive others.
9** May you share freely, never taking more than you give.
10**
11*************************************************************************
12**
13** Simple multi-threaded server used for informal testing of concurrency
14** between connections in different threads. Listens for tcp/ip connections
15** on port 9999 of the 127.0.0.1 interface only. To build:
16**
17** gcc -g $(TOP)/tool/tserver.c sqlite3.o -lpthread -o tserver
18**
19** To run using "x.db" as the db file:
20**
21** ./tserver x.db
22**
23** To connect, open a client socket on port 9999 and start sending commands.
24** Commands are either SQL - which must be terminated by a semi-colon, or
25** dot-commands, which must be terminated by a newline. If an SQL statement
26** is seen, it is prepared and added to an internal list.
27**
28** Dot-commands are:
29**
30** .list Display all SQL statements in the list.
31** .quit Disconnect.
32** .run Run all SQL statements in the list.
33** .repeats N Configure the number of repeats per ".run".
34** .seconds N Configure the number of seconds to ".run" for.
35** .mutex_commit Add a "COMMIT" protected by a g.commit_mutex
36** to the current SQL.
37** .stop Stop the tserver process - exit(0).
dan8b832e22018-12-18 16:24:21 +000038** .checkpoint N
39** .integrity_check
danb0b27ab2018-12-07 20:25:14 +000040**
41** Example input:
42**
43** BEGIN;
44** INSERT INTO t1 VALUES(randomblob(10), randomblob(100));
45** INSERT INTO t1 VALUES(randomblob(10), randomblob(100));
46** INSERT INTO t1 VALUES(randomblob(10), randomblob(100));
47** COMMIT;
48** .repeats 100000
49** .run
50**
51*/
52#define TSERVER_PORTNUMBER 9999
53
54#include <arpa/inet.h>
55#include <assert.h>
56#include <pthread.h>
57#include <signal.h>
58#include <stdint.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#include <sys/socket.h>
63#include <sys/time.h>
64#include <unistd.h>
65
66#include "sqlite3.h"
67
68#define TSERVER_DEFAULT_CHECKPOINT_THRESHOLD 3900
69
70/* Global variables */
71struct TserverGlobal {
72 char *zDatabaseName; /* Database used by this server */
73 char *zVfs;
74 sqlite3_mutex *commit_mutex;
75 sqlite3 *db; /* Global db handle */
76
77 /* The following use native pthreads instead of a portable interface. This
78 ** is because a condition variable, as well as a mutex, is required. */
79 pthread_mutex_t ckpt_mutex;
80 pthread_cond_t ckpt_cond;
81 int nThreshold; /* Checkpoint when wal is this large */
82 int bCkptRequired; /* True if wal checkpoint is required */
83 int nRun; /* Number of clients in ".run" */
84 int nWait; /* Number of clients waiting on ckpt_cond */
85};
86
87static struct TserverGlobal g = {0};
88
89typedef struct ClientSql ClientSql;
90struct ClientSql {
91 sqlite3_stmt *pStmt;
dan8b832e22018-12-18 16:24:21 +000092 int flags;
danb0b27ab2018-12-07 20:25:14 +000093};
94
dan8b832e22018-12-18 16:24:21 +000095#define TSERVER_CLIENTSQL_MUTEX 0x0001
96#define TSERVER_CLIENTSQL_INTEGRITY 0x0002
97
danb0b27ab2018-12-07 20:25:14 +000098typedef struct ClientCtx ClientCtx;
99struct ClientCtx {
100 sqlite3 *db; /* Database handle for this client */
101 int fd; /* Client fd */
102 int nRepeat; /* Number of times to repeat SQL */
103 int nSecond; /* Number of seconds to run for */
104 ClientSql *aPrepare; /* Array of prepared statements */
105 int nPrepare; /* Valid size of apPrepare[] */
106 int nAlloc; /* Allocated size of apPrepare[] */
107
108 int nClientThreshold; /* Threshold for checkpointing */
109 int bClientCkptRequired; /* True to do a checkpoint */
110};
111
112static int is_eol(int i){
113 return (i=='\n' || i=='\r');
114}
115static int is_whitespace(int i){
116 return (i==' ' || i=='\t' || is_eol(i));
117}
118
119/*
120** Implementation of SQL scalar function usleep().
121*/
122static void usleepFunc(
123 sqlite3_context *context,
124 int argc,
125 sqlite3_value **argv
126){
127 int nUs;
128 sqlite3_vfs *pVfs = (sqlite3_vfs*)sqlite3_user_data(context);
129 assert( argc==1 );
130 nUs = sqlite3_value_int64(argv[0]);
131 pVfs->xSleep(pVfs, nUs);
132}
133
134static void trim_string(const char **pzStr, int *pnStr){
135 const char *zStr = *pzStr;
136 int nStr = *pnStr;
137
138 while( nStr>0 && is_whitespace(zStr[0]) ){
139 zStr++;
140 nStr--;
141 }
142 while( nStr>0 && is_whitespace(zStr[nStr-1]) ){
143 nStr--;
144 }
145
146 *pzStr = zStr;
147 *pnStr = nStr;
148}
149
150static int send_message(ClientCtx *p, const char *zFmt, ...){
151 char *zMsg;
152 va_list ap; /* Vararg list */
153 va_start(ap, zFmt);
154 int res = -1;
155
156 zMsg = sqlite3_vmprintf(zFmt, ap);
157 if( zMsg ){
158 res = write(p->fd, zMsg, strlen(zMsg));
159 }
160 sqlite3_free(zMsg);
161 va_end(ap);
162
163 return (res<0);
164}
165
166static int handle_some_sql(ClientCtx *p, const char *zSql, int nSql){
167 const char *zTail = zSql;
168 int nTail = nSql;
169 int rc = SQLITE_OK;
170
171 while( rc==SQLITE_OK ){
172 if( p->nPrepare>=p->nAlloc ){
173 int nByte = (p->nPrepare+32) * sizeof(ClientSql);
174 ClientSql *aNew = sqlite3_realloc(p->aPrepare, nByte);
175 if( aNew ){
176 memset(&aNew[p->nPrepare], 0, sizeof(ClientSql)*32);
177 p->aPrepare = aNew;
178 p->nAlloc = p->nPrepare+32;
179 }else{
180 rc = SQLITE_NOMEM;
181 break;
182 }
183 }
184 rc = sqlite3_prepare_v2(
185 p->db, zTail, nTail, &p->aPrepare[p->nPrepare].pStmt, &zTail
186 );
187 if( rc!=SQLITE_OK ){
188 send_message(p, "error - %s (eec=%d)\n", sqlite3_errmsg(p->db),
189 sqlite3_extended_errcode(p->db)
190 );
191 rc = 1;
192 break;
193 }
194 if( p->aPrepare[p->nPrepare].pStmt==0 ){
195 break;
196 }
197 p->nPrepare++;
198 nTail = nSql - (zTail-zSql);
199 rc = send_message(p, "ok (%d SQL statements)\n", p->nPrepare);
200 }
201
202 return rc;
203}
204
dana4569122019-01-04 17:12:28 +0000205/*
206** Return a micro-seconds resolution timer.
207*/
danb0b27ab2018-12-07 20:25:14 +0000208static sqlite3_int64 get_timer(void){
209 struct timeval t;
210 gettimeofday(&t, 0);
dana4569122019-01-04 17:12:28 +0000211 return (sqlite3_int64)t.tv_usec + ((sqlite3_int64)t.tv_sec * 1000000);
danb0b27ab2018-12-07 20:25:14 +0000212}
213
214static void clear_sql(ClientCtx *p){
215 int j;
216 for(j=0; j<p->nPrepare; j++){
217 sqlite3_finalize(p->aPrepare[j].pStmt);
218 }
219 p->nPrepare = 0;
220}
221
222/*
223** The sqlite3_wal_hook() callback used by all client database connections.
224*/
225static int clientWalHook(void *pArg, sqlite3 *db, const char *zDb, int nFrame){
226 if( g.nThreshold>0 ){
227 if( nFrame>=g.nThreshold ){
228 g.bCkptRequired = 1;
229 }
230 }else{
231 ClientCtx *pCtx = (ClientCtx*)pArg;
232 if( pCtx->nClientThreshold && nFrame>=pCtx->nClientThreshold ){
233 pCtx->bClientCkptRequired = 1;
234 }
235 }
236 return SQLITE_OK;
237}
238
239static int handle_run_command(ClientCtx *p){
240 int i, j;
241 int nBusy = 0;
242 sqlite3_int64 t0 = get_timer();
243 sqlite3_int64 t1 = t0;
dana4569122019-01-04 17:12:28 +0000244 sqlite3_int64 tCommit = 0;
danb0b27ab2018-12-07 20:25:14 +0000245 int nT1 = 0;
246 int nTBusy1 = 0;
247 int rc = SQLITE_OK;
248
249 pthread_mutex_lock(&g.ckpt_mutex);
250 g.nRun++;
251 pthread_mutex_unlock(&g.ckpt_mutex);
252
danb0b27ab2018-12-07 20:25:14 +0000253 for(j=0; (p->nRepeat<=0 || j<p->nRepeat) && rc==SQLITE_OK; j++){
254 sqlite3_int64 t2;
255
256 for(i=0; i<p->nPrepare && rc==SQLITE_OK; i++){
257 sqlite3_stmt *pStmt = p->aPrepare[i].pStmt;
258
dan8b832e22018-12-18 16:24:21 +0000259 /* If the MUTEX flag is set, grab g.commit_mutex before executing
danb0b27ab2018-12-07 20:25:14 +0000260 ** the SQL statement (which is always "COMMIT" in this case). */
dan8b832e22018-12-18 16:24:21 +0000261 if( p->aPrepare[i].flags & TSERVER_CLIENTSQL_MUTEX ){
danb0b27ab2018-12-07 20:25:14 +0000262 sqlite3_mutex_enter(g.commit_mutex);
dana4569122019-01-04 17:12:28 +0000263 tCommit -= get_timer();
danb0b27ab2018-12-07 20:25:14 +0000264 }
265
266 /* Execute the statement */
dan8b832e22018-12-18 16:24:21 +0000267 if( p->aPrepare[i].flags & TSERVER_CLIENTSQL_INTEGRITY ){
268 sqlite3_step(pStmt);
dana4569122019-01-04 17:12:28 +0000269 if( sqlite3_stricmp("ok", (const char*)sqlite3_column_text(pStmt, 0)) ){
dan8b832e22018-12-18 16:24:21 +0000270 send_message(p, "error - integrity_check failed: %s\n",
271 sqlite3_column_text(pStmt, 0)
272 );
273 }
274 sqlite3_reset(pStmt);
275 }
danb0b27ab2018-12-07 20:25:14 +0000276 while( sqlite3_step(pStmt)==SQLITE_ROW );
277 rc = sqlite3_reset(pStmt);
278
279 /* Relinquish the g.commit_mutex mutex if required. */
dan8b832e22018-12-18 16:24:21 +0000280 if( p->aPrepare[i].flags & TSERVER_CLIENTSQL_MUTEX ){
dana4569122019-01-04 17:12:28 +0000281 tCommit += get_timer();
danb0b27ab2018-12-07 20:25:14 +0000282 sqlite3_mutex_leave(g.commit_mutex);
283 }
284
285 if( (rc & 0xFF)==SQLITE_BUSY ){
286 if( sqlite3_get_autocommit(p->db)==0 ){
287 sqlite3_exec(p->db, "ROLLBACK", 0, 0, 0);
288 }
289 nBusy++;
290 rc = SQLITE_OK;
291 break;
292 }
293 else if( rc!=SQLITE_OK ){
294 send_message(p, "error - %s (eec=%d)\n", sqlite3_errmsg(p->db),
295 sqlite3_extended_errcode(p->db)
296 );
297 }
298 }
299
300 t2 = get_timer();
dana4569122019-01-04 17:12:28 +0000301 if( t2>=(t1+1000000) ){
302 sqlite3_int64 nUs = (t2 - t1);
303 sqlite3_int64 nDone = (j+1 - nBusy - nT1);
danb0b27ab2018-12-07 20:25:14 +0000304
305 rc = send_message(
dana4569122019-01-04 17:12:28 +0000306 p, "(%d done @ %lld per second, %d busy)\n",
307 (int)nDone, (1000000*nDone + nUs/2) / nUs, nBusy - nTBusy1
308 );
danb0b27ab2018-12-07 20:25:14 +0000309 t1 = t2;
310 nT1 = j+1 - nBusy;
311 nTBusy1 = nBusy;
dana4569122019-01-04 17:12:28 +0000312 if( p->nSecond>0 && ((sqlite3_int64)p->nSecond*1000000)<=t1-t0 ) break;
danb0b27ab2018-12-07 20:25:14 +0000313 }
314
315 /* Global checkpoint handling. */
316 if( g.nThreshold>0 ){
317 pthread_mutex_lock(&g.ckpt_mutex);
318 if( rc==SQLITE_OK && g.bCkptRequired ){
319 if( g.nWait==g.nRun-1 ){
320 /* All other clients are already waiting on the condition variable.
321 ** Run the checkpoint, signal the condition and move on. */
322 rc = sqlite3_wal_checkpoint(p->db, "main");
323 g.bCkptRequired = 0;
324 pthread_cond_broadcast(&g.ckpt_cond);
325 }else{
326 assert( g.nWait<g.nRun-1 );
327 g.nWait++;
328 pthread_cond_wait(&g.ckpt_cond, &g.ckpt_mutex);
329 g.nWait--;
330 }
331 }
332 pthread_mutex_unlock(&g.ckpt_mutex);
333 }
334
335 if( rc==SQLITE_OK && p->bClientCkptRequired ){
336 rc = sqlite3_wal_checkpoint(p->db, "main");
dan8b832e22018-12-18 16:24:21 +0000337 if( rc==SQLITE_BUSY ) rc = SQLITE_OK;
danb0b27ab2018-12-07 20:25:14 +0000338 assert( rc==SQLITE_OK );
339 p->bClientCkptRequired = 0;
340 }
341 }
342
343 if( rc==SQLITE_OK ){
dana4569122019-01-04 17:12:28 +0000344 int nMs = (get_timer() - t0) / 1000;
danb0b27ab2018-12-07 20:25:14 +0000345 send_message(p, "ok (%d/%d SQLITE_BUSY)\n", nBusy, j);
346 if( p->nRepeat<=0 ){
dana4569122019-01-04 17:12:28 +0000347 send_message(p, "### ok %d busy %d ms %d commit-ms %d\n",
348 j-nBusy, nBusy, nMs, (int)(tCommit / 1000)
349 );
danb0b27ab2018-12-07 20:25:14 +0000350 }
351 }
352 clear_sql(p);
353
354 pthread_mutex_lock(&g.ckpt_mutex);
355 g.nRun--;
356 pthread_mutex_unlock(&g.ckpt_mutex);
357
358 return rc;
359}
360
361static int handle_dot_command(ClientCtx *p, const char *zCmd, int nCmd){
362 int n;
363 int rc = 0;
364 const char *z = &zCmd[1];
365 const char *zArg;
366 int nArg;
367
368 assert( zCmd[0]=='.' );
369 for(n=0; n<(nCmd-1); n++){
370 if( is_whitespace(z[n]) ) break;
371 }
372
373 zArg = &z[n];
374 nArg = nCmd-n;
375 trim_string(&zArg, &nArg);
376
377 if( n>=1 && n<=4 && 0==strncmp(z, "list", n) ){
378 int i;
379 for(i=0; rc==0 && i<p->nPrepare; i++){
380 const char *zSql = sqlite3_sql(p->aPrepare[i].pStmt);
381 int nSql = strlen(zSql);
382 trim_string(&zSql, &nSql);
383 rc = send_message(p, "%d: %.*s\n", i, nSql, zSql);
384 }
385 }
386
387 else if( n>=1 && n<=4 && 0==strncmp(z, "quit", n) ){
dan8b832e22018-12-18 16:24:21 +0000388 rc = -1;
danb0b27ab2018-12-07 20:25:14 +0000389 }
390
391 else if( n>=2 && n<=7 && 0==strncmp(z, "repeats", n) ){
392 if( nArg ){
393 p->nRepeat = strtol(zArg, 0, 0);
394 if( p->nRepeat>0 ) p->nSecond = 0;
395 }
396 rc = send_message(p, "ok (repeat=%d)\n", p->nRepeat);
397 }
398
399 else if( n>=2 && n<=3 && 0==strncmp(z, "run", n) ){
400 rc = handle_run_command(p);
401 }
402
403 else if( n>=2 && n<=7 && 0==strncmp(z, "seconds", n) ){
404 if( nArg ){
405 p->nSecond = strtol(zArg, 0, 0);
406 if( p->nSecond>0 ) p->nRepeat = 0;
407 }
408 rc = send_message(p, "ok (seconds=%d)\n", p->nSecond);
409 }
410
411 else if( n>=1 && n<=12 && 0==strncmp(z, "mutex_commit", n) ){
412 rc = handle_some_sql(p, "COMMIT;", 7);
413 if( rc==SQLITE_OK ){
dan8b832e22018-12-18 16:24:21 +0000414 p->aPrepare[p->nPrepare-1].flags |= TSERVER_CLIENTSQL_MUTEX;
danb0b27ab2018-12-07 20:25:14 +0000415 }
416 }
417
418 else if( n>=1 && n<=10 && 0==strncmp(z, "checkpoint", n) ){
419 if( nArg ){
420 p->nClientThreshold = strtol(zArg, 0, 0);
421 }
422 rc = send_message(p, "ok (checkpoint=%d)\n", p->nClientThreshold);
423 }
424
425 else if( n>=2 && n<=4 && 0==strncmp(z, "stop", n) ){
426 sqlite3_close(g.db);
427 exit(0);
428 }
429
dan8b832e22018-12-18 16:24:21 +0000430 else if( n>=2 && n<=15 && 0==strncmp(z, "integrity_check", n) ){
431 rc = handle_some_sql(p, "PRAGMA integrity_check;", 23);
432 if( rc==SQLITE_OK ){
433 p->aPrepare[p->nPrepare-1].flags |= TSERVER_CLIENTSQL_INTEGRITY;
434 }
435 }
436
danb0b27ab2018-12-07 20:25:14 +0000437 else{
438 send_message(p,
439 "unrecognized dot command: %.*s\n"
440 "should be \"list\", \"run\", \"repeats\", \"mutex_commit\", "
dan8b832e22018-12-18 16:24:21 +0000441 "\"checkpoint\", \"integrity_check\" or \"seconds\"\n", n, z
danb0b27ab2018-12-07 20:25:14 +0000442 );
443 rc = 1;
444 }
445
446 return rc;
447}
448
449static void *handle_client(void *pArg){
450 char zCmd[32*1024]; /* Read buffer */
451 int nCmd = 0; /* Valid bytes in zCmd[] */
452 int res; /* Result of read() call */
453 int rc = SQLITE_OK;
454
455 ClientCtx ctx;
456 memset(&ctx, 0, sizeof(ClientCtx));
457
458 ctx.fd = (int)(intptr_t)pArg;
459 ctx.nRepeat = 1;
460 rc = sqlite3_open_v2(g.zDatabaseName, &ctx.db,
461 SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, g.zVfs
462 );
463 if( rc!=SQLITE_OK ){
464 fprintf(stderr, "sqlite3_open(): %s\n", sqlite3_errmsg(ctx.db));
465 return 0;
466 }
467 sqlite3_create_function(
468 ctx.db, "usleep", 1, SQLITE_UTF8, (void*)sqlite3_vfs_find(0),
469 usleepFunc, 0, 0
470 );
471
472 /* Register the wal-hook with the new client connection */
473 sqlite3_wal_hook(ctx.db, clientWalHook, (void*)&ctx);
474
475 while( rc==SQLITE_OK ){
476 int i;
477 int iStart;
478 int nConsume;
479 res = read(ctx.fd, &zCmd[nCmd], sizeof(zCmd)-nCmd-1);
480 if( res<=0 ) break;
481 nCmd += res;
482 if( nCmd>=sizeof(zCmd)-1 ){
483 fprintf(stderr, "oversized (>32KiB) message\n");
484 res = 0;
485 break;
486 }
487 zCmd[nCmd] = '\0';
488
489 do {
490 nConsume = 0;
491
492 /* Gobble up any whitespace */
493 iStart = 0;
494 while( is_whitespace(zCmd[iStart]) ) iStart++;
495
496 if( zCmd[iStart]=='.' ){
497 /* This is a dot-command. Search for end-of-line. */
498 for(i=iStart; i<nCmd; i++){
499 if( is_eol(zCmd[i]) ){
500 rc = handle_dot_command(&ctx, &zCmd[iStart], i-iStart);
501 nConsume = i+1;
502 break;
503 }
504 }
505 }else{
506
507 int iSemi;
508 char c = 0;
509 for(iSemi=iStart; iSemi<nCmd; iSemi++){
510 if( zCmd[iSemi]==';' ){
511 c = zCmd[iSemi+1];
512 zCmd[iSemi+1] = '\0';
513 break;
514 }
515 }
516
517 if( iSemi<nCmd ){
518 if( sqlite3_complete(zCmd) ){
519 rc = handle_some_sql(&ctx, zCmd, iSemi+1);
520 nConsume = iSemi+1;
521 }
522
523 if( c ){
524 zCmd[iSemi+1] = c;
525 }
526 }
527 }
528
529 if( nConsume>0 ){
530 nCmd = nCmd-nConsume;
531 if( nCmd>0 ){
532 memmove(zCmd, &zCmd[nConsume], nCmd);
533 }
534 }
535 }while( rc==SQLITE_OK && nConsume>0 );
536 }
537
dan8b832e22018-12-18 16:24:21 +0000538 fprintf(stdout, "Client %d disconnects (rc=%d)\n", ctx.fd, rc);
danb0b27ab2018-12-07 20:25:14 +0000539 fflush(stdout);
540 close(ctx.fd);
541 clear_sql(&ctx);
542 sqlite3_free(ctx.aPrepare);
543 sqlite3_close(ctx.db);
544 return 0;
545}
546
547static void usage(const char *zExec){
548 fprintf(stderr, "Usage: %s ?-vfs VFS? DATABASE\n", zExec);
549 exit(1);
550}
551
552int main(int argc, char *argv[]) {
553 int sfd;
554 int rc;
555 int yes = 1;
556 struct sockaddr_in server;
557 int i;
558
559 /* Ignore SIGPIPE. Otherwise the server exits if a client disconnects
560 ** abruptly. */
561 signal(SIGPIPE, SIG_IGN);
562
563 g.nThreshold = TSERVER_DEFAULT_CHECKPOINT_THRESHOLD;
564 if( (argc%2) ) usage(argv[0]);
565 for(i=1; i<(argc-1); i+=2){
566 int n = strlen(argv[i]);
567 if( n>=2 && 0==sqlite3_strnicmp("-walautocheckpoint", argv[i], n) ){
568 g.nThreshold = strtol(argv[i+1], 0, 0);
569 }else
570 if( n>=2 && 0==sqlite3_strnicmp("-vfs", argv[i], n) ){
571 g.zVfs = argv[i+1];
572 }
573 }
574 g.zDatabaseName = argv[argc-1];
575
576 g.commit_mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
577 pthread_mutex_init(&g.ckpt_mutex, 0);
578 pthread_cond_init(&g.ckpt_cond, 0);
579
580 rc = sqlite3_open_v2(g.zDatabaseName, &g.db,
581 SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, g.zVfs
582 );
583 if( rc!=SQLITE_OK ){
584 fprintf(stderr, "sqlite3_open(): %s\n", sqlite3_errmsg(g.db));
585 return 1;
586 }
587
588 rc = sqlite3_exec(g.db, "SELECT * FROM sqlite_master", 0, 0, 0);
589 if( rc!=SQLITE_OK ){
590 fprintf(stderr, "sqlite3_exec(): %s\n", sqlite3_errmsg(g.db));
591 return 1;
592 }
593
594 sfd = socket(AF_INET, SOCK_STREAM, 0);
595 if( sfd<0 ){
596 fprintf(stderr, "socket() failed\n");
597 return 1;
598 }
599
600 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
601 if( rc<0 ){
602 perror("setsockopt");
603 return 1;
604 }
605
606 memset(&server, 0, sizeof(server));
607 server.sin_family = AF_INET;
608 server.sin_addr.s_addr = inet_addr("127.0.0.1");
609 server.sin_port = htons(TSERVER_PORTNUMBER);
610
611 rc = bind(sfd, (struct sockaddr *)&server, sizeof(struct sockaddr));
612 if( rc<0 ){
613 fprintf(stderr, "bind() failed\n");
614 return 1;
615 }
616
617 rc = listen(sfd, 8);
618 if( rc<0 ){
619 fprintf(stderr, "listen() failed\n");
620 return 1;
621 }
622
623 while( 1 ){
624 pthread_t tid;
625 int cfd = accept(sfd, NULL, NULL);
626 if( cfd<0 ){
627 perror("accept()");
628 return 1;
629 }
630
631 fprintf(stdout, "Client %d connects\n", cfd);
632 fflush(stdout);
633 rc = pthread_create(&tid, NULL, handle_client, (void*)(intptr_t)cfd);
634 if( rc!=0 ){
635 perror("pthread_create()");
636 return 1;
637 }
638
639 pthread_detach(tid);
640 }
641
642 return 0;
643}