LINUX.ORG.RU

TCP. клиент:python, сервер: C


0

0

Проблема такая. Клиент на python'е устанавливает TCP-соединение с сервером (написанном на C). При этом сервер начинает беспрерывно получать нулевые байты. В чём проблема не знаю.

Заранее спасибо за ответ.


С чего начать? С клиента или сервера? :) Мало инфы короче. Может у тебя на сервере не блокирующиеся сокеты...

zJes ★★
()
Ответ на: комментарий от zJes

Сервер (приблизительно)
listenfd = socket(PF_INET, SOCK_STREAM, 0);
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family      = PF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port        = htons(SERV_PORT);
bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); 
listen(listenfd, 256); 
clilen = sizeof(cliaddr);
connfd = accept(listenfd, (SA *) &cliaddr, &clilen);

Клиент:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

FENix
() автор топика
Ответ на: комментарий от mv

А что дальше? Клиент ещё ничего не шлёт, а сервер уже радостно получает нулевые байты:
        while (1) {
		char c;
                //printf("%d >= %d o_O\n", readen, MAXBUFF);
		if (readen >= MAXBUFF)
		{
			printf("Error! Overflow buffer\n");
			return (-1);
		}
		if ( (read(sockfd, &c, 1)) < 0)
		{
			perror("read");
			return (-1);		
		}
		if (c == '\n')
		{
			break;
		}
                printf("%d", c);
		line[readen] = c;
		readen++;
	}

FENix
() автор топика
Ответ на: комментарий от mv

Имеется в виду, "прочитано 0 байт", а не 0 в c.

mv ★★★★★
()
Ответ на: комментарий от mv

Может я полный чайник, но разве TCP-сокет по умолчанию открывается в неблокирующем режиме?

FENix
() автор топика
Ответ на: комментарий от tailgunner

я больше уважаю select(). Хотя если всё действительно так, то лучше сменить тип сокета на блокирующий. Странно, что когда у меня был клиент на C такой проблемы не возникало...

FENix
() автор топика
Ответ на: комментарий от tailgunner

Неважно.

Так всё-таки, если мой сокет действительно неблокирующий, то как мне поможет select? Да, и _почему_ он неблокирующий?

FENix
() автор топика
Ответ на: комментарий от mv

> tcpdump'ом посмотри.

Идея хорошая.

FENix
() автор топика
Ответ на: комментарий от FENix

> Так всё-таки, если мой сокет действительно неблокирующий, то как мне поможет select?

Приостановит программу до момента, когда из сокета можно будет что-то прочитать.

> _почему_ он неблокирующий?

Я не думаю, что он неблокирующийся - тогда он давал бы EAGAIN, а код возврата read ты проверяешь. Больше всего похоже, что твой клиент завершается ничего не послав, а на сервере ты видишь EOF (read нуля байтов без ошибки).

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> "Так всё-таки, если мой сокет действительно неблокирующий, то как мне поможет select?" Приостановит программу до момента, когда из сокета можно будет что-то прочитать.

select() приостанавливает программу до тех пор пока вызов read() не приведёт к блокировке. Так что неблокирующему сокету он не поможет.

FENix
() автор топика
Ответ на: комментарий от FENix

> select() приостанавливает программу до тех пор пока вызов read() не приведёт к блокировке. Так что неблокирующему сокету он не поможет.

Ой, правда? А я-то думал, что select остановит программу до момента, когда можно будет вычитать минимум 1 байт без блокировки.

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

>The return value will be 0 when the peer has performed an orderly shutdown

read() возвращает 0. Вот только интересно, почему он делает это много раз подряд. /Ушёл читать man/

FENix
() автор топика
Ответ на: комментарий от mv

tcpdump говорит, что никакие пакеты не шлются (кроме стандартного рукопожатия):
listening on lo:6666, link-type EN10MB (Ethernet), capture size 96 bytes
00:17:47.007634 IP localhost.59467 > localhost.6666: S 2451069557:2451069557(0) win 32792 <mss 16396,sackOK,timestamp 45098996 0,nop,wscale 6>
00:17:47.008126 IP localhost.6666 > localhost.59467: S 2454574029:2454574029(0) ack 2451069558 win 32768 <mss 16396,sackOK,timestamp 45098996 45098996,nop,wscale 6>
00:17:47.008171 IP localhost.59467 > localhost.6666: . ack 1 win 513 <nop,nop,timestamp 45098996 45098996>
00:17:47.008849 IP localhost.59467 > localhost.6666: P 1:13(12) ack 1 win 513 <nop,nop,timestamp 45098996 45098996>
00:17:47.009003 IP localhost.6666 > localhost.59467: . ack 13 win 512 <nop,nop,timestamp 45098996 45098996>
00:17:47.009411 IP localhost.59467 > localhost.6666: F 13:13(0) ack 1 win 513 <nop,nop,timestamp 45098996 45098996>
00:17:47.046493 IP localhost.6666 > localhost.59467: . ack 14 win 512 <nop,nop,timestamp 45099006 45098996>
00:17:47.236087 IP localhost.6666 > localhost.59467: F 1:1(0) ack 14 win 512 <nop,nop,timestamp 45099053 45098996>
00:17:47.236131 IP localhost.59467 > localhost.6666: . ack 2 win 513 <nop,nop,timestamp 45099053 45099053

FENix
() автор топика
Ответ на: комментарий от tailgunner

> А я-то думал, что select остановит программу до момента, когда можно будет вычитать минимум 1 байт без блокировки.

select() and  pselect()  allow  a  program  to  monitor  multiple  file
       descriptors,  waiting  until one or more of the file descriptors become
       "ready" for some class of I/O operation (e.g., input possible).  A file
       descriptor  is considered ready if it is possible to perform the corre‐
       sponding I/O operation (e.g., read(2)) without blocking.

FENix
() автор топика
Ответ на: комментарий от zJes

Похоже проблема всё-таки в клиенте.
Клиент на С устанавливает соединение примерно так:

        sockfd = socket(PF_INET, SOCK_STREAM, 0);   
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = PF_INET;
	servaddr.sin_port = htons(SERV_PORT);
	inet_pton(PF_INET, argv[1], &servaddr.sin_addr); 
	connect(sockfd, (SA *) &servaddr, sizeof(servaddr));

И никакой проблемы нет.

FENix
() автор топика
Ответ на: комментарий от tailgunner

> Больше всего похоже, что твой клиент завершается ничего не послав, а на сервере ты видишь EOF (read нуля байтов без ошибки).

Серверу SIGPIPE бы прилетел.

mv ★★★★★
()
Ответ на: комментарий от mv

нет, там ничего серьёзного. Я просто решил поизучать python, переписав программу, которую мы писали в институте. Там есть много вещей, которые к делу не относятся.

FENix
() автор топика
Ответ на: комментарий от FENix

> read() возвращает 0. Вот только интересно, почему он делает это много раз подряд

Наверное потому, что вы упорно продолжаете читать закрытый со стороны клиента сокет. Больше из этого сокета вы никогда ничего не прочитаете.

Покажите полный код.

anonymous
()
Ответ на: комментарий от FENix

Клиент:

#!/usr/local/bin/python3.0
from tkinter import *
import socket
import random

class Plane:
    def landing():    #ask for landing
        print("coming soon")
        x = random.randint(1,999)
        sendstr = '1 ' + str(x) + '\n';
        #Application.connect.s.send(b'1')

    def takeoff():    #ask for taking off
        print("coming soon")
    def recover():    #recover lost plane signal
        print("coming soon")

class Application(Frame):
    def say_hi(self):    #this is not really useful:)
        print("hi there, everyone!")

    def info_window(self):
        newWin = Toplevel()
        newWin.label = Label(newWin, font="terminus 11", text="A simple gui client for the airport server.\n Written using Tkinter. \n ©2008 Aleksandr Chuklin")
        newWin.label.grid()

    def createWidgets(self):
        self.quit_button = Button(self, text="Quit", fg="red", command=self.quit)
        self.quit_button.grid(row=0, column=0, pady=15)

        self.hi_there = Button(self, text="Hello!", command=self.say_hi)
        self.hi_there.grid(row=0, column=1, pady=15)

        self.inf_but = Button(self, bitmap="question", command=self.info_window)
        self.inf_but.grid(row=0, column=2, pady=15, padx=10)

        self.lan_but = Button(self, text="landing", command=Plane.landing) 
        self.lan_but.grid(row=1, column=0, pady=15, padx=10)

        self.tak_but = Button(self, text="take off", command=Plane.takeoff)
        self.tak_but.grid(row=1, column=1, pady=15, padx=10)

        self.rec_but = Button(self, text="recover", command=Plane.recover)  
        self.rec_but.grid(row=1, column=2, pady=15, padx=10)

        self.main_text = StringVar()
        self.main_label = Label(self, bg="grey", font="terminus 11", textvariable=self.main_text)
        self.main_text.set("Connecting...")
        self.main_label.grid(row=2) 

    def connect(self):
        HOST = '127.0.0.1'    # The remote host
        PORT = 6666              # The same port as used by the server
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((HOST, PORT))
        self.main_text.set("Connection completed.")
        s.send(b'Hello, world')

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.grid()
        self.createWidgets()
        self.connect()

root = Tk()
app = Application(master=root)
app.master.title("pyAirport") 
print("pyAirport is running...")
app.mainloop()
root.destroy()

FENix
() автор топика
Ответ на: комментарий от FENix

сервер:

#include	"airport.h"

int semid;

int main() {
	int		listenfd, connfd;
	pid_t				childpid;
	socklen_t			clilen;
	struct sockaddr_in	cliaddr, servaddr;
	key_t 			key;
	struct sembuf s[3];
	setbuf(stdout, (char*)NULL);	//switch of buffering for stdout
	
	if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		exit(1);
	}
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family      = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port        = htons(SERV_PORT);

	bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); //FIXME: to check for error 

	listen(listenfd, 256); //FIXME: to check for error 

	if ((key = ftok ("server", 0)) < 0)
	{
		perror ("ftok");
		exit (1);
	}	
// 0 sem -- number of free lines
// 1st sem -- if 0 line is free?
// 2st sem -- if 1 line is free?
	if ((semid = semget (key, 3, 0666 | IPC_CREAT)) < 0)
	{
		perror ("semget");
		exit (1);
	}
	s[0].sem_num = 0;
	s[0].sem_op = 2;
	s[0].sem_flg = 0;
	s[1].sem_num = 1;
	s[1].sem_op = 1;
	s[1].sem_flg = 0;
	s[2].sem_num = 2;
	s[2].sem_op = 1;
	s[2].sem_flg = 0;
	if (semop (semid, &s[0], 3) < 0)	//initialization of semaphores
	{
		perror("semop");
		return 1;
	}
	

	for ( ; ; ) {
		clilen = sizeof(cliaddr);
		connfd = accept(listenfd, (SA *) &cliaddr, &clilen); //FIXME: to check for error 

		if ( (childpid = fork()) == 0) {	/* child process */ //FIXME: to check for error 
			close(listenfd);	/* close listening socket */ //FIXME: to check for error 
			if (request(connfd) == 0) {	/* process the request */
				exit(0);
			}
			else exit(1);	
		}
		close(connfd);			/* parent closes connected socket */ //FIXME: to check for error 
	}
}

int request(int sockfd) {
	char line[MAXBUFF];
	size_t readen = 0;
	//0 -- take_off, 1 -- landing
	//2 -- recover first line, 3 -- recover second line
	int type;	
	unsigned int planeid;
	struct sembuf s[3];
	int num;	//number of free line
	while (1) {
		char c = '$';
		int retstatus;
                //printf("%d >= %d o_O\n", readen, MAXBUFF);
		if (readen >= MAXBUFF)
		{
			printf("Error! Overflow buffer\n");
			return (-1);
		}
		if ( (retstatus = read(sockfd, &c, 1)) < 0)
		{
			perror("read");
			return (-1);		
		}
		if (c == '\n')
		{
			break;
		}
                printf("c = %c retstatus = %d \n", c, retstatus);
		line[readen] = c;
		readen++;
	}
	line[readen] = '\0';
	if (sscanf(line, "%d %d", &type, &planeid) != 2)
	{
		perror("scanf");
		return (-1);
	}		
	
	switch (type) {
	case 0: 
		break;
	case 1: 
		break;
	case 2:
		printf("Connection with the plane %d has been recovered. Recovering 0 line...\n",planeid);
		s[0].sem_num = 1;
		s[0].sem_op = 0;		// 0th line must be occupied
		s[0].sem_flg = IPC_NOWAIT;
		s[1].sem_num = 1;
		s[1].sem_op = 1;
		s[1].sem_flg = 0;
		s[2].sem_num = 0;
		s[2].sem_op = 1;
		s[2].sem_flg = 0;
		if (semop (semid, &s[0], 3) < 0)	//freeing line
		{
			if (errno != EAGAIN)
			{
				perror("semop");
				return (-1);
			}
			else
			{
				printf("Trying to recover line which is not occupied!\n");
				return (-1);
			}
		}
		return 0;		
	case 3:
		printf("Connection with the plane %d has been recovered. Recovering 1 line...\n",planeid);
		s[0].sem_num = 2;
		s[0].sem_op = 0;		// 0th line must be occupied
		s[0].sem_flg = IPC_NOWAIT;
		s[1].sem_num = 2;
		s[1].sem_op = 1;
		s[1].sem_flg = 0;
		s[2].sem_num = 0;
		s[2].sem_op = 1;
		s[2].sem_flg = 0;
		if (semop (semid, &s[0], 3) < 0)	//freeing line
		{
			if (errno != EAGAIN)
			{
				perror("semop");
				return (-1);
			}
			else
			{
				printf("Trying to recover line which is not occupied!\n");
				return (-1);
			}
		}
		return 0;		
	default: 
		printf("Error. Wrong data has been sent\n");
		return (-1);
	}
	
	printf("Request from plane %d\n", planeid);
	s[0].sem_num = 0;
	s[0].sem_op = -1;
	s[0].sem_flg = 0;
	s[1].sem_num = 1;
	s[1].sem_op = -1;
	s[1].sem_flg = IPC_NOWAIT;
	num = 0;
	if (semop (semid, &s[0], 2) < 0)	
	{
		if (errno != EAGAIN)
		{
			perror("semop");
			return (-1);
		}
		else
		{
			num = 1;
			s[0].sem_num = 0;
			s[0].sem_op = -1;
			s[0].sem_flg = 0;
			s[1].sem_num = 2;
			s[1].sem_op = -1;
			s[1].sem_flg = 0;
			if (semop (semid, &s[0], 2) < 0)	
			{
				perror("semop");	
				return (-1);
			}
		}
	}
	snprintf (line, sizeof(line), "Permit. Line %d\n", num);	
	if (write(sockfd, line, strlen(line)) < 0)
	{
		perror("write"); 	
		//connection with plane was lost! Panic =)		
		return 1;
	}	
	if ((readen = read(sockfd, &line[0], 1)) < 0)
	{
		perror("read");
		//connection with the plane was lost! Panic =)		
		return 1;
	}
	if (readen == 0)
	{
		printf("Connection was lost! Waiting for the signal from the plane %d\n", planeid);
		//connection with the plane was lost! Panic =)		
		return 1;
	}
	printf("Plane %d has finished its operation\n", planeid);
	s[0].sem_num = num + 1;
	s[0].sem_op = 1;
	s[0].sem_flg = 0;
	s[1].sem_num = 0;
	s[1].sem_op = 1;
	s[1].sem_flg = 0;
	if (semop (semid, &s[0], 2) < 0)	//freeing line
	{
		perror("semop");
		return (-1);
	}
	
	return 0;			
}

FENix
() автор топика
Ответ на: комментарий от FENix

клиент на C:

#include	"airport.h"

int main(int argc, char **argv)
{
	int	sockfd;
	struct sockaddr_in servaddr;
	char line[MAXBUFF];
	int planeid;
	int readen = 0;

	if (argc != 3) {
		printf("usage: %s <IP address> <plane identifier>\n", argv[0]);
		exit(1);
	}
	if (sscanf(argv[2], "%d", &planeid) != 1)
	{
		perror("scanf");
		return (-1);
	}		
	if (planeid <= 0) {
		printf("usage: %s <IP address> <plane identifier>\n", argv[0]);
		exit(1);
	}
	
	sockfd = socket(PF_INET, SOCK_STREAM, 0); //FIXME: to check for error  

	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = PF_INET;
	servaddr.sin_port = htons(SERV_PORT);
	inet_pton(PF_INET, argv[1], &servaddr.sin_addr); //FIXME: to check for error
	connect(sockfd, (SA *) &servaddr, sizeof(servaddr)); //FIXME: to check for error  
	snprintf (line, sizeof(line), "1 %d\n", planeid);	
	write(sockfd, line, strlen(line)); //sending request //FIXME:..
	while (1) {			//waiting for reply
		char c;
		if (readen >= MAXBUFF)
		{
			printf("Error!\n");
			return (-1);
		}
		if ( (read(sockfd, &c, 1)) < 0)
		{
			perror("read");
			return (-1);		
		}
		if (c == '\n')
		{
			break;
		}
		line[readen] = c;
		readen++;
	}
	line[readen] = '\0';
	printf("%s\n", line);
	sleep(OP_TIME);		//The plain is landing now
	write(sockfd, &line[0], 1); //sending OK to server //FIXME:...
	exit(0);
}

FENix
() автор топика
Ответ на: комментарий от FENix

>    def connect(self):
>        HOST = '127.0.0.1'    # The remote host
>        PORT = 6666              # The same port as used by the server
>        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>        s.connect((HOST, PORT))
>        self.main_text.set("Connection completed.")
>        s.send(b'Hello, world')

s - локальная переменная, и сборщик мусора может добраться до нее 
прежде, чем данные уйдут в сеть. Попробуй self.s

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> s - локальная переменная, и сборщик мусора может добраться до нее прежде, чем данные уйдут в сеть. Попробуй self.s

Идею понял, но можно поконкретнее? Я в Python'е полный профан.

FENix
() автор топика
Ответ на: комментарий от tailgunner

Уже разобрался. Всем большое спасибо за советы. Буду дальше читать tutorial :)

FENix
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.