Writing an echo server in libev and C++ (2011)

mooreds1 pts0 comments

Writing an echo server in libev and c++ | SkiToy Blog

Writing an echo server in libev and c++

calendar

Jun 28, 2011<br>· 4 min read · libev<br>c++<br>echo<br>programming

Share on:

twitter

facebook

linkedin

copy

Looking at event IO frameworks and found some really good comments about the “ev” library, the challenge is that I would rather work in C++ than pure C – I like methods.. Found that the documentation is lacking for the C++ side of the library and needed to build a test harness.

This is a very basic TCP Echo server using libev in c++. The C++ side is only using std::list and the ev side is playing with loop, signals and basic socket IO. It should be a useful bread crumb for anybody trying to build their own service, which of course is my next big activity.

Update : Fixed the small file descriptor leak and now available as a Gist – https://gist.github.com/3364414

1#include<br>2#include<br>3#include<br>4#include<br>5#include<br>6#include<br>7#include<br>8#include<br>9#include<br>10#include<br>11<br>12//<br>13// Buffer class - allow for output buffering such that it can be written out<br>14// into async pieces<br>15//<br>16struct Buffer {<br>17 char *data;<br>18 ssize_t len;<br>19 ssize_t pos;<br>20<br>21 Buffer(const char *bytes, ssize_t nbytes) {<br>22 pos = 0;<br>23 len = nbytes;<br>24 data = new char[nbytes];<br>25 memcpy(data, bytes, nbytes);<br>26 }<br>27<br>28 virtual ~Buffer() {<br>29 delete [] data;<br>30 }<br>31<br>32 char *dpos() {<br>33 return data + pos;<br>34 }<br>35<br>36 ssize_t nbytes() {<br>37 return len - pos;<br>38 }<br>39};<br>40<br>41//<br>42// A single instance of a non-blocking Echo handler<br>43//<br>44class EchoInstance {<br>45private:<br>46 ev::io io;<br>47 static int total_clients;<br>48 int sfd;<br>49<br>50 // Buffers that are pending write<br>51 std::listBuffer*> write_queue;<br>52<br>53 // Generic callback<br>54 void callback(ev::io &watcher, int revents) {<br>55 if (EV_ERROR & revents) {<br>56 perror("got invalid event");<br>57 return;<br>58 }<br>59<br>60 if (revents & EV_READ)<br>61 read_cb(watcher);<br>62<br>63 if (revents & EV_WRITE)<br>64 write_cb(watcher);<br>65<br>66 if (write_queue.empty()) {<br>67 io.set(ev::READ);<br>68 } else {<br>69 io.set(ev::READ|ev::WRITE);<br>70 }<br>71 }<br>72<br>73 // Socket is writable<br>74 void write_cb(ev::io &watcher) {<br>75 if (write_queue.empty()) {<br>76 io.set(ev::READ);<br>77 return;<br>78 }<br>79<br>80 Buffer* buffer = write_queue.front();<br>81<br>82 ssize_t written = write(watcher.fd, buffer->dpos(), buffer->nbytes());<br>83 if (written 0) {<br>84 perror("read error");<br>85 return;<br>86 }<br>87<br>88 buffer->pos += written;<br>89 if (buffer->nbytes() == 0) {<br>90 write_queue.pop_front();<br>91 delete buffer;<br>92 }<br>93 }<br>94<br>95 // Receive message from client socket<br>96 void read_cb(ev::io &watcher) {<br>97 char buffer[1024];<br>98<br>99 ssize_t nread = recv(watcher.fd, buffer, sizeof(buffer), 0);<br>100<br>101 if (nread 0) {<br>102 perror("read error");<br>103 return;<br>104 }<br>105<br>106 if (nread == 0) {<br>107 // Gack - we're deleting ourself inside of ourself!<br>108 delete this;<br>109 } else {<br>110 // Send message bach to the client<br>111 write_queue.push_back(new Buffer(buffer, nread));<br>112 }<br>113 }<br>114<br>115 // effictivly a close and a destroy<br>116 virtual ~EchoInstance() {<br>117 // Stop and free watcher if client socket is closing<br>118 io.stop();<br>119<br>120 close(sfd);<br>121<br>122 printf("%d client(s) connected.\n", --total_clients);<br>123 }<br>124<br>125public:<br>126 EchoInstance(int s) : sfd(s) {<br>127 fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0) | O_NONBLOCK);<br>128<br>129 printf("Got connection\n");<br>130 total_clients++;<br>131<br>132 io.setEchoInstance, &EchoInstance::callback>(this);<br>133<br>134 io.start(s, ev::READ);<br>135 }<br>136};<br>137<br>138class EchoServer {<br>139private:<br>140 ev::io io;<br>141 ev::sig sio;<br>142 int s;<br>143<br>144public:<br>145<br>146 void io_accept(ev::io &watcher, int revents) {<br>147 if (EV_ERROR & revents) {<br>148 perror("got invalid event");<br>149 return;<br>150 }<br>151<br>152 struct sockaddr_in client_addr;<br>153 socklen_t client_len = sizeof(client_addr);<br>154<br>155 int client_sd = accept(watcher.fd, (struct sockaddr *)&client_addr, &client_len);<br>156<br>157 if (client_sd 0) {<br>158 perror("accept error");<br>159 return;<br>160 }<br>161<br>162 EchoInstance *client = new EchoInstance(client_sd);<br>163 }<br>164<br>165 static void signal_cb(ev::sig &signal, int revents) {<br>166 signal.loop.break_loop();<br>167 }<br>168<br>169 EchoServer(int port) {<br>170 printf("Listening on port %d\n", port);<br>171<br>172 struct sockaddr_in addr;<br>173<br>174 s = socket(PF_INET, SOCK_STREAM, 0);<br>175<br>176 addr.sin_family = AF_INET;<br>177 addr.sin_port = htons(port);<br>178 addr.sin_addr.s_addr = INADDR_ANY;<br>179<br>180 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) != 0) {<br>181 perror("bind");<br>182 }<br>183<br>184 fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0) | O_NONBLOCK);<br>185<br>186 listen(s, 5);<br>187<br>188 io.setEchoServer, &EchoServer::io_accept>(this);<br>189 io.start(s, ev::READ);<br>190<br>191 sio.setEchoServer::signal_cb>();<br>192 sio.start(SIGINT);<br>193 }<br>194<br>195 virtual ~EchoServer() {<br>196 shutdown(s, SHUT_RDWR);<br>197 close(s);<br>198 }<br>199};<br>200<br>201int EchoInstance::total_clients = 0;<br>202<br>203int main(int argc, char **argv)<br>204{<br>205 int port = 8192;<br>206<br>207 if (argc > 1)<br>208 port = atoi(argv[1]);<br>209<br>210 ev::default_loop loop;<br>211 EchoServer echo(port);<br>212<br>213...

buffer include watcher read return echo

Related Articles