Search code examples
csocketsposixsystem-calls

Sending buffer in c socket programming with write


I'm creating a TCP chat room in C with fork(). Every client message should reach server with username and message in buffer, so I use strcpy(buffer, name) and strcat(buffer, ": ") to combine buffer and name. After that I use

n = write (sockfd, buffer, strlen(buffer))

to send buffer to server. So right here strange thing happens, my server show nothing, but after that when I use

n = read(sockfd, buffer, 255);
printf("%s", buffer);

string is correctly transferred. Here is my server code :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>

int main(){
    int port;
    int i = 1;
    printf("Enter port ->");
    scanf("%d", &port);

    int sockfd, newsockfd, n;
    char buffer[255];
    struct sockaddr_in6 serv_addr, cli_addr;
    int ret, flag;

    sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 
    if (sockfd < 0){
        printf("Error opening socket\n");
    }

    bzero((char *) &serv_addr, sizeof(serv_addr));
    flag = 1;
    ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
    if (ret < 0){
        printf("Set sock opt errror\n");
    }
    serv_addr.sin6_family = AF_INET6;
    serv_addr.sin6_addr = in6addr_any;
    serv_addr.sin6_port = htons(port);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
        printf("Error binding\n");
    }

    listen(sockfd, 5);
    int clilen = sizeof(cli_addr);
    int pid;

    while(1){
        newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
        if (newsockfd < 0){
            printf("Error on accept\n");
        }
        printf("New connection\n");
        pid = fork();
        if (pid < 0){
            printf("Error on pid\n");
        }
        if (pid == 0){
            close(sockfd);
            while(1){
                bzero(buffer, 255);
                n = read(newsockfd, buffer, 255);
                if (n < 0){
                    printf("Error on reading\n");
                }
                printf("%s", buffer);
                n = write(newsockfd, buffer, strlen(buffer));
                if (n < 0){
                    printf("Error on writing\n");
                }
            }
        } 
    }
    close(newsockfd);
    return 0;
}

and client code :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int main(){

    int port;
    printf("Enter port ->");
    scanf("%d", &port);
    char ip[10];
    printf("Enter ip -> ");
    scanf("%s", &ip);
    char name[20];
    printf("Enter name -> ");
    scanf("%s", &name);
    int sockfd, n;
    char buffer[255];
    struct sockaddr_in6 serv_addr;

    sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 
    if (sockfd < 0){
        printf("Error opening socket\n");
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin6_family = AF_INET6;
    inet_pton(AF_INET6, "ip", &serv_addr.sin6_addr);
    serv_addr.sin6_port = htons(port);
    if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
        printf("Connection failed\n");
    }

    while(1){

        bzero(buffer, 255);
        fgets(buffer, 255, stdin);
        strcpy(buffer, name);
        strcat(buffer, ": ");
        n = write (sockfd, buffer, strlen(buffer));
        bzero(buffer,255);
        if (n < 0){
            printf("Error on writing\n");
        }
        n = read(sockfd, buffer, 255);
        printf("%s", buffer);
        if (n < 0){
            printf("Error on reading\n");
        }
    }

    close(sockfd);
}

So maybe someone can see the problem why client can't send full buffer with name and message to server? I think the problem is something with name. When I send only the buffer that I get using

fgets(buffer, 255, stdin);

server works fine, but I need client username.

I tried to test strlen(name) and buffer and everything seems okay. What I tried to do is manually convert name to buffer like this :

 for (int i = 0; i < strlen(name); i++){
        buffer[i] = name[i];
 }

But then if it's the only way to do this, how should I push the buffer to the right entering name and don't lose message information?


Solution

  • Your code has plenty of errors :).

    First of all scanf takes as 1st argument a pointer, so when you want to use it for a string do:

    char ip[10];
    ...
    scanf("%s", ip);//THIS IS CORRECT
    

    not scanf("%s", &ip);.

    On the server the compiler was complaining about the type of clilen. accept is expecting a sockelen_t (or equivalently unsigned int) while you declared that variable as a simple int.


    Main problem

    It is the following. In the client you did strcpy(buffer, name); which copies name starting from the beginning of the buffer but the beginning of the buffer actually contained your data. You should do something like the following using strcat as you did in the next line:

        bzero(buffer, 255);
        fgets(buffer, 255, stdin);
        strcat(buffer, name);
        strcat(buffer, ": ");
    

    Further general advice

    Also note another thing: avoid mixing system call functions read write getchar with library functions such as fread, fwrite, fgets. I was having this problem with that causing by the fact that fgets might read more bytes than expected. In your case should be ok because you are using them for different purposes.