I am writing a portable application in C which is supposed to work on mac and windows,
The socket code I am writing is using POSIX sockets Winsock api on windows has similar features.
The server is written in java and this is a client applicaiton which connects to server and then communicates,
I need to write a readline function Just as we have in C# or Java which reads a line terminated by newline character.
I am currently using following function but I do not think it is very efficient way to do it because I am reading character by character
//return number of bytes read or -1 on error
int readline(SOCKET s, char* pResponse)
{
char c = '0';
int status = 0;
int i = 0;
while(true)
{
status = recv(s,&c, 1,0);
if(status == SOCKET_ERROR)
{
//socket error
return -1;
}
else if(status == 0)
{
//closed ?
return -1;
}
else if(status > 0)
{
pResponse[i] = c;
i++;
if(c == '\n' || c == '\r\n')
{
break;
}
}
}
return i;
}
The sender/server will only send this line and wait for a response to come from client, so we can assume that it's okay to read entire socket data
So can I write this one better than this ? is reading char by char a performance issue ? any help would be appreciated.
Yes, you can do better. Readline should read larger amounts of data from the socket and if there is any data after the first '\n' it should leave that data in a buffer somewhere for later. This is called "buffering" and will require a solid knowledge of pointers and arrays.
Your socket library might already be buffering for you, in which case you don't need to do this, but probably not.
You should break, when the last character in your buffer is '\r' and the character before it is '\n'.
If you are going to treat the contents of the buffer as a c string, then replace the '\r' by a '\0' before you return.
If the file you are reading is a dos text file, and the \r\n suggests that it is, consider the case, that the final line in the file may, or may not have a \r\n. That is a case you are likely to run into.
Related
I am new to socket programming, I am writing an FTP server without the client
I have to access the server using netcat localhost port
void do_job(int fd)
{
i,client;
char command[DEFAULT_BUFLEN];
while((client =recv(fd, command, strlen(command), 0)) >0 )
{
if (strcmp(command,"LIST") ==0)
{
}
in the main function :
if ((pid=fork()) == 0) {
close(listenfd);
do_job(fd);
printf("Child finished their job!\n");
close(fd);
exit(0);
}
The code has numerous issues. You're appying strlen to an uninitialized array. This is undefined behavior, which in actual practice could return anything from 0 to values in excess of the array size.
The recv function fills buffers from a byte stream; it doesn't return null-terminated strings, and doesn't extract lines from the stream. recv will happily read a fragment of the network input stream which starts in the middle of one command and ends in the middle of another one.
In the actual FTP protocol, a command will not be a null-terminated string anyway.
FTP commands are "Telnet strings" terminated by the "Telnet end of
line code" [RFC 959, 4.1.3, P. 34]
Basically the whole approach is too simplistic to be workable; the program needs some sort of character stream abstraction over the network input, so that it can parse the protocol properly.
You need to add a null terminator to the string in order to use strcmp(). Also, if they type a line ending with newline, that character will be in command, so you need to include it in the string you compare with.
When calling recv() the third argument should be the max amount you can store in the buffer. strlen(command) returns the length of a string that's already in the buffer, but you haven't initialized it. You can use DEFAULT_BUFLEN, and subtract 1 to allow room for the null terminator that will be added.
void do_job(int fd)
{
i,client;
char command[DEFAULT_BUFLEN];
while((client =recv(fd, command, DEFAULT_BUFLEN - 1, 0)) >0 )
{
command[client] = '\0'; // add null terminator
if (strcmp(command,"LIST\n") ==0)
{
}
When I write between my server and my client, I make use of the rio functions (specified in the csapp.c), more specifically:
Rio_writen --> when writing to a socket
Rio_readlineb --> when reading from a socket
My problem is that whenever I send information from e.g. my client to my server, example:
Rio_writen(name_server_socket, "null", 4);
Rio_writen(name_server_socket, "\n", 1);
I expect my server when calling:
Rio_readlineb(&rio, name, MAXLINE);
char* lookup = name;
assert(lookup == "null"); --> fail
assert(strcmp(lookup, "null") == 0); --> fail
To assert that the message is equivalent to "null". However, when I use prints to show my messages on either site, this is what I get:
// client side message send
lookup_name: (null)|
// server side message received
lookup_name: (null)
|
I use the | character to see if anything is added after my message is sent (by printf("x: %s|",something)). In this case every message I sent get a newline character at the end, and I do not understand why.
Using the rio function, in particular Rio_getlineb to read inputs: it reads until it reaches the end of the line (provided a newline character). Thus, I'm expected to provide a newline character at the end of any message I sent (hence why i do (Rio_writen(name_server_socket, "\n", 1);) after each message sent).
Can anyone see what I am missing (or in any case know a solution to the problem)? All my setups are working correctly: sockets, rio_t etc. The messages are sent and received, but somehow add a newline at the end.
EDIT: The assertion is just a placeholder for doing something with the data. So adding a newline to the assertion might solve the assert, but not my problem: which is why a newline is added in the first place :-) My point is that I want to parse the "lookup" to a function that compares that value to a name, given in a struct. So I cannot just add a newline to the struct->name that I am looking at.
For what I see in the documentation:
– rio_readlineb reads a text line of up to maxlen bytes from file fd
and stores the line in usrbuf
Especially useful for reading text lines from network sockets
– Stopping conditions
maxlen bytes read
EOF encountered
Newline (‘ \n ’) encountered
That is, the same as fgets does.
And looking at the source code:
/* $begin rio_readlineb */
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen)
{
int n, rc;
char c, *bufp = usrbuf;
for (n = 1; n < maxlen; n++) {
if ((rc = rio_read(rp, &c, 1)) == 1) {
*bufp++ = c;
if (c == '\n') {
n++;
break;
}
It includes the trailing newline in the buffer.
This newline is very useful, the fact that the message does not include it is an indication that you have not read the whole line (in your case received the entire package), in which case you can continue requesting data.
What we usually do with fgets to skip the trailing newline is:
char *ptr = strchr(lookup, '\n');
if (ptr != NULL)
{
*ptr = '\0'; // Replace newline with NUL
}
I've been given this code for an assignment, there supposed to be errors in it but I can't actually figure out what this function is supposed to do, never mind figure out if there's any issues with it...
I am guessing that it's supposed to read the buffer line by line, but I've never seen it done this way before
The buffer that is sent to the function is empty.
int read_line(int sock, char *buffer) {
size_t length = 0;
while (1) {
char data;
int result = recv(sock, &data, 1, 0);
if ((result <= 0) || (data == EOF)){
perror("Connection closed");
exit(1);
}
buffer[length] = data;
length++;
if (length >= 2 && buffer[length-2] == '\r' && buffer[length-1] == '\n') {
buffer[length-2] = '\0';
return length;
}
}
}
Thanks in advance!
I'd say the purpose of this function is to read a line that ends with \r\n from socket stream and store it in a char array as a string, therefore the \0 (string termination character) placement.
Ok, so what's wrong with the code?
I'd start with the input parameter char *buffer - inside the function you do not know its size so you cannot check if it exceeds its size limit and it could lead to buffer overflow.
So it would be better to send buffer length as a parameter and check with every received byte if it can be stored.
EOF - it is defined as -1 and in this case actually doesn't make any sense, because nothing will be setting your data variable to EOF. The only thing you need to look out for is the end of socket stream (recv documentation). And here is an example for EOFusage.
Feel free to remove (data == EOF) from condition.
Let's say you are receiving everything regularly and you receive your last input and connection closes, so you enter this case:
if ((result <= 0) || (data == EOF)){
perror("Connection closed");
exit(1);
}
The problem here is that you won't process your last line and the program will just end. Although, I might be wrong here since I don't know when the connection is getting regularly shut down.
And a minor note here, result that equals to 0 isn't considered as an error, but a regular connection shutdown (or a 0-byte datagram was received).
I hope I haven't missed anything.
I tried running this code, but nothing is ever shown. (Yes, I ran it as root) If I can't get ngrep's output I guess I'll try to figure out how to use libpcap with c++ although I haven't been able to find any good examples.
int main(void)
{
FILE* fproc = popen("ngrep -d wlan0 GET");
char c;
do {
printf("%c", fgetc(fproc));
} while (c!=EOF);
}
So what about this code causes nothing to be show, and what do you suggest to easily parse ngrep's output, or some other way of capturing GET requests, maybe with libpcap
I see the possible potential problems:
You have no open mode for the popen call? Leaving this off is likely to result in either a core dump or a random value of the stack deciding whether it's a read or write pipe.
The c variable should be an int rather than a char since it has to be able to hold all characters plus an EOF indicator.
And, you're not actually assigning anything to c which would cause the loop to exit.
With that do loop, you're trying to output the EOF to the output stream at the end. Don't know off the top of my head if this is a bad thing but it's certainly not necessary.
Try this:
int main(void) {
int ch;
FILE* fproc;
if ((fproc = popen("ngrep -d wlan0 GET", "r")) < 0) {
fprintf (stderr, "Cannot open pipe\n");
return 1;
}
while ((ch = fgetc (fproc)) != EOF) {
printf ("%c", ch);
};
pclose (fproc);
return 0;
}
You should also be aware that the pipe is fully buffered by default so you may not get any information until the buffer is full.
I'm working on writing a IRC bot in C, and have ran into a snag.
In my main function, I create my socket and connect, all that happy stuff. Then I have a (almost) infinite loop to read what's being sent back from the server. I then pass what's read off to a helper function, processLine(char *line) - the problem is, that the following code reads until my buffer is full - I want it to only read text until a newline (\n) or carriage return (\r) occurs (thus ending that line)
while (buffer[0] && buffer[1]) {
for (i=0;i<BUFSIZE;i++) buffer[i]='\0';
if (recv(sock, buffer, BUFSIZE, 0) == SOCKET_ERROR)
processError();
processLine(buffer);
}
What ends up happening is that many lines get jammed all together, and I can't process the lines properly when that happens.
If you're not familiar with IRC protocols, a brief summary would be that when a message is sent, it often looks like this: :YourNickName!YourIdent#YourHostName PRIVMSG #someChannel :The rest on from here is the message sent...
and a login notice, for instance, is something like this: :the.hostname.of.the.server ### bla some text bla with ### being a code(?) used for processing - i.e. 372 is an indicator that the following text is part of the Message Of The Day.
When it's all jammed together, I can't read what number is for what line because I can't find where a line begins or ends!
I'd appreciate help with this very much!
P.S.: This is being compiled/ran on linux, but I eventually want to port it to windows, so I am making as much of it as I can multi-platform.
P.S.S.: Here's my processLine() code:
void processLine(const char *line) {
char *buffer, *words[MAX_WORDS], *aPtr;
char response[100];
int count = 0, i;
buffer = strdup(line);
printf("BLA %s", line);
while((aPtr = strsep(&buffer, " ")) && count < MAX_WORDS)
words[count++] = aPtr;
printf("DEBUG %s\n", words[1]);
if (strcmp(words[0], "PING") == 0) {
strcpy(response, "PONG ");
strcat(response, words[1]);
sendLine(NULL, response); /* This is a custom function, basically it's a send ALL function */
} else if (strcmp(words[1], "376") == 0) { /* We got logged in, send login responses (i.e. channel joins) */
sendLine(NULL, "JOIN #cbot");
}
}
The usual way to deal with this is to recv into a persistent buffer in your application, then pull a single line out and process it. Later you can process the remaining lines in the buffer before calling recv again. Keep in mind that the last line in the buffer may only be partially received; you have to deal with this case by re-entering recv to finish the line.
Here's an example (totally untested! also looks for a \n, not \r\n):
#define BUFFER_SIZE 1024
char inbuf[BUFFER_SIZE];
size_t inbuf_used = 0;
/* Final \n is replaced with \0 before calling process_line */
void process_line(char *lineptr);
void input_pump(int fd) {
size_t inbuf_remain = sizeof(inbuf) - inbuf_used;
if (inbuf_remain == 0) {
fprintf(stderr, "Line exceeded buffer length!\n");
abort();
}
ssize_t rv = recv(fd, (void*)&inbuf[inbuf_used], inbuf_remain, MSG_DONTWAIT);
if (rv == 0) {
fprintf(stderr, "Connection closed.\n");
abort();
}
if (rv < 0 && errno == EAGAIN) {
/* no data for now, call back when the socket is readable */
return;
}
if (rv < 0) {
perror("Connection error");
abort();
}
inbuf_used += rv;
/* Scan for newlines in the line buffer; we're careful here to deal with embedded \0s
* an evil server may send, as well as only processing lines that are complete.
*/
char *line_start = inbuf;
char *line_end;
while ( (line_end = (char*)memchr((void*)line_start, '\n', inbuf_used - (line_start - inbuf))))
{
*line_end = 0;
process_line(line_start);
line_start = line_end + 1;
}
/* Shift buffer down so the unprocessed data is at the start */
inbuf_used -= (line_start - inbuf);
memmove(innbuf, line_start, inbuf_used);
}
TCP doesn't offer any sequencing of that sort. As #bdonlan already said you should implement something like:
Continuously recv from the socket into a buffer
On each recv, check if the bytes received contain an \n
If an \n use everything up to that point from the buffer (and clear it)
I don't have a good feeling about this (I read somewhere that you shouldn't mix low-level I/O with stdio I/O) but you might be able to use fdopen.
All you would need to do is
use fdopen(3) to associate your socket with a FILE *
use setvbuf to tell stdio that you want it line-buffered (_IOLBF) as opposed to the default block-buffered.
At this point you should have effectively moved the work from your hands to stdio. Then you could go on using fgets and the like on the FILE *.