diff --git a/clamdscan/proto.c b/clamdscan/proto.c
index 8c7f3b5..c85199c 100644
--- a/clamdscan/proto.c
+++ b/clamdscan/proto.c
@@ -346,6 +346,7 @@ struct client_parallel_data {
     } *ids;
     unsigned int action_sources;
     unsigned int max_action_sources;
+    int restarts;
 };
 
 /* Sends a proper scan request to clamd and parses its replies
@@ -436,6 +437,101 @@ static void free_scanids(struct client_parallel_data *c)
     }
 }
 
+#define MAX_SESSION_RESTARTS 5
+
+/* Re-establish the IDSESSION after clamd unexpectedly closed the
+ * connection, and re-send every outstanding (unanswered) request.
+ * The head of the old outstanding list is the most recently sent
+ * request, i.e. the best suspect for having triggered the disconnect,
+ * so it is logged for diagnostic purposes.
+ * Returns 0 on success, nonzero if the session could not be restored
+ * (restart budget exhausted or reconnection failed). */
+static int session_restart(struct client_parallel_data *c)
+{
+    static const char zIDSESSION[] = "zIDSESSION";
+    struct SCANID *outstanding;
+    struct SCANID *cur;
+    unsigned int n = 0;
+
+    if (++c->restarts > MAX_SESSION_RESTARTS) {
+        logg(LOGG_ERROR, "Connection to clamd lost %d times, giving up\n",
+             MAX_SESSION_RESTARTS);
+        return 1;
+    }
+
+    outstanding = c->ids;
+    c->ids      = NULL;
+    c->lastid   = 0;
+
+    closesocket(c->sockd);
+    if ((c->sockd = dconnect(clamdopts)) < 0)
+        return 1;
+    if (sendln(c->sockd, zIDSESSION, sizeof(zIDSESSION))) {
+        closesocket(c->sockd);
+        return 1;
+    }
+
+    for (cur = outstanding; cur; cur = cur->next) n++;
+    logg(LOGG_WARNING,
+         "Connection to clamd lost (restart %d/%d); last request before "
+         "disconnect: %s; re-sending %u outstanding request(s)\n",
+         c->restarts, MAX_SESSION_RESTARTS,
+         outstanding ? outstanding->file : "(none)", n);
+
+    while ((cur = outstanding)) {
+        int res     = 0;
+        outstanding = cur->next;
+        switch (c->scantype) {
+#ifdef HAVE_FD_PASSING
+            case FILDES:
+                res = (NULL != cur->action_source)
+                          ? send_fdpass_fd(c->sockd, cur->action_source->scan_fd)
+                          : send_fdpass(c->sockd, cur->file);
+                break;
+#endif
+            case STREAM:
+                res = (NULL != cur->action_source)
+                          ? send_stream_fd_action(c->sockd, cur->action_source->scan_fd,
+                                                  cur->action_source->display_path, clamdopts)
+                          : send_stream(c->sockd, cur->file, clamdopts);
+                break;
+        }
+        if (res > 0) {
+            /* Re-queued under a fresh id in the new session. */
+            cur->id   = ++c->lastid;
+            cur->next = c->ids;
+            c->ids    = cur;
+            continue;
+        }
+        if (res == 0) {
+            /* Local failure (e.g. file vanished): count and drop. */
+            logg(LOGG_ERROR, "Failed to re-send %s after reconnect\n", cur->file);
+            c->errors++;
+            free((void *)cur->file);
+            if (NULL != cur->action_source) {
+                action_source_close(cur->action_source);
+                free(cur->action_source);
+                if (c->action_sources > 0)
+                    c->action_sources--;
+            }
+            free(cur);
+            continue;
+        }
+        /* res < 0: the new connection died as well. Stash everything
+         * (both the not-yet-resent tail and the already-resent entries;
+         * duplicates are renumbered and resent, which is harmless) and
+         * try again within the restart budget. */
+        cur->next = outstanding;
+        while ((cur = outstanding)) {
+            outstanding = cur->next;
+            cur->next   = c->ids;
+            c->ids      = cur;
+        }
+        return session_restart(c);
+    }
+    return 0;
+}
+
 /* FTW callback for scanning in IDSESSION mode
  * Returns SUCCESS on success, CL_EXXX or BREAK on error */
 static cl_error_t parallel_callback(STATBUF *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data)
@@ -494,8 +590,10 @@ static cl_error_t parallel_callback(STATBUF *sb, char *filename, const char *pat
     if (action) {
         while (c->action_sources >= c->max_action_sources) {
             if (dspresult(c)) {
-                status = CL_BREAK;
-                goto done;
+                if (session_restart(c)) {
+                    status = CL_BREAK;
+                    goto done;
+                }
             }
         }
 
@@ -531,28 +629,42 @@ static cl_error_t parallel_callback(STATBUF *sb, char *filename, const char *pat
         }
         if (FD_ISSET(c->sockd, &rfds)) {
             if (dspresult(c)) {
-                status = CL_BREAK;
-                goto done;
-            } else
-                continue;
+                if (session_restart(c)) {
+                    status = CL_BREAK;
+                    goto done;
+                }
+            }
+            continue;
         }
         if (FD_ISSET(c->sockd, &wfds)) break;
     }
 
-    switch (c->scantype) {
+    while (1) {
+        switch (c->scantype) {
 #ifdef HAVE_FD_PASSING
-        case FILDES:
-            res = (NULL != action_source) ? send_fdpass_fd(c->sockd, action_source->scan_fd) : send_fdpass(c->sockd, scan_path);
-            break;
+            case FILDES:
+                res = (NULL != action_source) ? send_fdpass_fd(c->sockd, action_source->scan_fd) : send_fdpass(c->sockd, scan_path);
+                break;
 #endif
-        case STREAM:
-            res = (NULL != action_source) ? send_stream_fd_action(c->sockd, action_source->scan_fd, action_source->display_path, clamdopts) : send_stream(c->sockd, scan_path, clamdopts);
+            case STREAM:
+                res = (NULL != action_source) ? send_stream_fd_action(c->sockd, action_source->scan_fd, action_source->display_path, clamdopts) : send_stream(c->sockd, scan_path, clamdopts);
+                break;
+        }
+        if (res >= 0)
             break;
+        /* Connection-level send failure: try to restore the session,
+         * then retry sending the current file. */
+        if (session_restart(c)) {
+            c->printok = 0;
+            c->errors++;
+            status = CL_BREAK;
+            goto done;
+        }
     }
-    if (res <= 0) {
+    if (res == 0) {
         c->printok = 0;
         c->errors++;
-        status = res ? CL_BREAK : CL_SUCCESS;
+        status = CL_SUCCESS;
         goto done;
     }
 
@@ -619,6 +731,7 @@ int parallel_client_scan(char *file, int scantype, int *infected, int *err, int
     cdata.printok            = printinfected ^ 1;
     cdata.action_sources     = 0;
     cdata.max_action_sources = get_max_action_sources();
+    cdata.restarts           = 0;
     client_walk_policy_init(&cdata.walk_policy, file);
     data.data                = &cdata;
 
@@ -633,7 +746,14 @@ int parallel_client_scan(char *file, int scantype, int *infected, int *err, int
     }
 
     sendln(cdata.sockd, zEND, sizeof(zEND));
-    while (cdata.ids && !dspresult(&cdata)) continue;
+    while (cdata.ids) {
+        if (dspresult(&cdata)) {
+            if (session_restart(&cdata))
+                break;
+            /* New session: terminate it too so remaining replies flush. */
+            sendln(cdata.sockd, zEND, sizeof(zEND));
+        }
+    }
     closesocket(cdata.sockd);
 
     *infected += cdata.infected;
