Показать сообщение отдельно
Старый 27.11.2014, 09:00   #268
Жека
Дэвелопер
 
Регистрация: 04.09.2005
Адрес: Красноярск
Сообщений: 1,376
Написано 491 полезных сообщений
(для 886 пользователей)
Ответ: Вопрос-Ответ (для новичков BlitzMax)

Я когда-то делал простенький клиент-сервер на максе, на основе TCP.

Суть:
1. клиенты представляют из себя движущиеся объекты (в пределах экрана)
2. сервер должен следить за перемещениями этих объектов, отображая всех на экране

Данные от клиентов передаются 1 раз в секунду, можно изменить "timeSendPosInterval = 1000".

Клиенты могут отправлять текстовую инфу серверу, она отобразится в поле "info".

Подключение происходит на локальный адрес, можно изменить: "remoteIp = HostIp("127.0.0.1")".

По умолчанию порт для подключения 50000, при наличии файла port.txt в корне с exe-шниками - порт загружается оттуда.

Примечание: для получения русских символов есть простецкий конвертер в файле utils.bmx.

Запуск:
1. запустить tcpServer
2. запустить tcpClient один или несколько раз

tcpClient.bmx
SuperStrict

Include "utils.bmx"

AppTitle = "tcpServer"



Global maxLines:Int = 20

Global strTmp:String = ""

Global info:String


Graphics 800, 600

SetImageFont(LoadImageFont("arial.ttf", 12))

Local port:Int = 50000
If(FileType("port.txt") = 1)
	port = Int(LoadText("port.txt"))
EndIf

Global server:TServer = New TServer
server.start(port)

Local char:Short


While Not(KeyHit(KEY_ESCAPE) Or AppTerminate())

	server.update()
	
	char = GetChar()
	If(char <> 0) Then server.sendToAll(Chr(char))
	
	DrawText("Port: " + port + ", online: " + server.onlineCount, 20, 50)
	DrawText("Info: " + info, 20, 70)
	
	server.drawPos()
	
	Flip()
	Cls()
	
Wend

server.stop()

End


Function socketSend(sock:TSocket, text:String)
	
	text:+"~r~n"
	sock.Send(text, Len(text))
	DebugLog("send: " + text)
	
End Function


Function socketRecv:String(sock:TSocket, bufData:Byte[], bufSize:Int)
	
	If(sock.ReadAvail() = 0) Then Return Null
	
	strTmp = ""
	Local size:Int = 0
	
	While(sock.ReadAvail() > 0)
		flushBuf(bufData, bufSize)
		size = sock.ReadAvail()
		If(size > bufSize) size = bufSize
		sock.Recv(bufData, size)
		strTmp:+String.FromCString(bufData).Trim()
	Wend
	
	strTmp = convertFromAnsi(strTmp)
		
	Return strTmp
	
End Function



Type TServer
	Field socket:TSocket
	Field connect:TSocket

	Const bufSize:Int = 255
	Field bufData:Byte[] = New Byte[bufSize]
	Field bufStr:String[] = Null

	Field listMsg:TList = New TList

	Field listClients:TList = New TList
	
	Field cmd:String
	
	Field onlineCount:Int
	
	
	
	'запуск
	Method start(port:Int = 5000)
		socket = CreateTCPSocket()
		BindSocket(socket, port)
		SocketListen(socket)
	End Method

	
	'остановка
	Method stop()
		CloseSocket(socket)
	End Method


	'добавление нового клиента в список
	Method clientAdd:TClient(socket:TSocket)
		Local cli:TClient = New TClient
		cli.socket = socket
		listClients.addLast(cli)
		onlineCount:+1
		Return cli
	End Method
	
	
	'удаление клиента из списка
	Method clientRemove(client:TClient)
		listClients.Remove(client)
		onlineCount:-1
		DebugLog("disconnect " + client.getId() + " at " + CurrentTime())
	End Method
	
	
	'обработка клиентов
	Method update()
	
		'подключение новых клиентов
		If(connect = Null)
			connect = SocketAccept(socket)
			If(connect <> Null)
				clientAdd(connect)
				socketSend(connect, "ID")
				connect = Null
			EndIf
		EndIf
		
	
		'прослушивание клиентских сокетов
		If(listClients.IsEmpty()) Return
		
		Local params:String[]
		
		For Local cli:TClient = EachIn listClients
			
			'клиент отключился
			If(cli.socket.Connected() = False)
				clientRemove(cli)
				Continue
			EndIf
			
			'получение и обработка данных
			strTmp = socketRecv(cli.socket, bufData, bufSize)
			If(strTmp <> "")
				bufStr = strTmp.Split("~r~n")
				For Local k:Int = 0 Until bufStr.Length
					cmd = bufStr[k].Trim()
					If(cmd = "") Continue
					'разделение на параметры
					params = commandParseCommand(cli, cmd)
					'выполнение
					commandProcessCommand(cli, params)
					'добавление в чат
					strTmp = cli.getId() + ": " + cmd
					listMsg.AddLast(strTmp)
					If(listMsg.Count() > maxLines) Then listMsg.Remove(listMsg.First())
					DebugLog(strTmp)
				Next
			EndIf
		Next
	
	End Method
	
	
	'разбор команды
	Method commandParseCommand:String[] (client:TClient, cmd:String)
		Local mas:String[] = cmd.Split(" ")
		Return mas
	End Method
	
	
	'выполнение команд
	Method commandProcessCommand(client:TClient, params:String[])
		Local cmd:String = params[0]
		If(cmd = "ID")
			client.setId(Int(params[1]))
		Else If(cmd = "POS")
			client.setPos(Int(params[1]), Int(params[2]))
		Else
			info = cmd
		EndIf
	End Method
	
	
	'массовая рассылка всем, кроме указанного
	Method sendToOther(client:TClient, text:String)
		If(listClients.IsEmpty()) Then Return
	
		For Local cli:TClient = EachIn listClients
			If(cli <> client)
				socketSend(cli.socket, text)
			EndIf
		Next
		
	End Method
	
	'массовая рассылка всем, кроме указанного
	Method sendToAll(text:String)
		sendToOther(Null, text)
	End Method
	
	'рисование позиций на карте
	Method drawPos()
		If(listClients.IsEmpty()) Then Return
		Local px:Int, py:Int, sz:Int = 20
		Local name:String
		
		For Local cli:TClient = EachIn listClients
			px = cli.getX()
			py = cli.getY()
			
			cli.adjustColor()
			DrawRect(px, py, sz, sz)
			
			SetColor(200, 200, 200)
			name = "" + cli.getId()
			DrawText(name, px - (TextWidth(name) - sz) * 0.5, py - 17)
		Next
		
	End Method
	
End Type


Type TClient
	Field socket:TSocket
	Field obj:TObj = New TObj
	
	Field red:Int, green:Int, blue:Int
	
	
	Method setId(id:Int)
		obj.id = id
		red = Rand(100, 255)
		green = Rand(100, 255)
		blue = Rand(100, 255)
	End Method
	
	Method adjustColor()
		SetColor(red, green, blue)
	End Method
	
	Method getId:Int()
		Return obj.id
	End Method
	
	Method setPos(x:Int, y:Int)
		obj.x = x
		obj.y = y
	End Method
	
	Method getX:Int()
		Return obj.x
	End Method
	
	Method getY:Int()
		Return obj.y
	End Method
	
End Type


Type TObj
	Field name:String
	Field id:Int
	Field x:Int, y:Int
End Type


tcpClient.bmx
SuperStrict

SeedRnd(MilliSecs())

AppTitle = "tcpClient"

Include "utils.bmx"

Local remotePort:Int = 50000
If(FileType("port.txt") = 1)
	remotePort = Int(LoadText("port.txt"))
EndIf
Local remoteIp:Int = HostIp("127.0.0.1")

Global cliSocket:TSocket = CreateTCPSocket()
ConnectSocket(cliSocket, remoteIp, remotePort)

Global bufSize:Int = 255
Global bufData:Byte[] = New Byte[bufSize]

Global bufStr:String[] = Null

Global listMsg:TList = New TList

Global maxLines:Int = 20

Global screenW:Int = 800
Global screenH:Int = 600

Graphics screenW, screenH
SetImageFont(LoadImageFont("arial.ttf", 16))
   
Global text:String
Global strTmp:String

text = ""

Global ch:Int = 0

Global onConnect:Int = True

Global posX:Float, posY:Float, spdX:Float, spdY:Float
Global timeMoveStart:Int, timeMoveInterval:Int = 100

posX = Rand(screenW)
posY = Rand(screenH)
spdX = Rnd(-1, 1)
spdY = Rnd(-1, 1)

Global timeSendPosStart:Int, timeSendPosInterval:Int = 1000


If(Not(SocketConnected(cliSocket)))
	DrawText "Server is not run. Try to connecting later.", 20, 20
	DrawText "Press any key to exit.", 40, 50
	WaitKey()
	End
EndIf

Local rez:Int = False

Global onRunning:Int = True


While(onRunning = True)
	
	If(KeyHit(KEY_ESCAPE) Or AppTerminate())
		onRunning = False
		Exit
	EndIf
	
	 
	drawMessages()
		
	SetColor 155, 155, 155
	DrawLine 10, 10, screenW - 10, 10
	DrawLine 10, screenH - 50, screenW - 10, screenH - 50
	DrawLine 10, screenH - 10, screenW - 10, screenH - 10
	
	SetColor 255, 255, 255
	DrawText "> " + text, 10, screenH - 40
	
	If(MilliSecs() Mod 1000 < 500)
		DrawRect 10 + TextWidth("> " + text) + 2, screenH - 40, 2, 20
	EndIf
	
	DrawRect(posX, posY, 20, 20)
	 
	Flip()
	Cls()
	
	rez = checkInput()
	
	If(rez = True) 'нажали Enter
		send(text)
		text = ""
	EndIf
	
	recv()
	
	If(Not(SocketConnected(cliSocket)))
		Exit
	EndIf
	
	moveObject()
	
	If(MilliSecs() - timeSendPosStart >= timeSendPosInterval)
		send("POS " + Int(posX) + " " + Int(posY))
		timeSendPosStart = MilliSecs()
	EndIf
	
Wend


CloseSocket(cliSocket)
   
End



Function moveObject()
	If(MilliSecs() - timeMoveStart < timeMoveInterval) Then Return
	
	posX:+spdX * 3
	posY:+spdY * 3
	
	If((posX <= 0 And spdX < 0) Or (posX >= screenW And spdX > 0))
		spdX:*- 1
	EndIf
	If((posY <= 0 And spdY < 0) Or (posY >= screenH And spdY > 0))
		spdY:*- 1
	EndIf
	
	If(Rand(100) < 5)
		spdX = Rnd(-1, 1)
		spdY = Rnd(-1, 1)
	EndIf
	
	timeMoveStart = MilliSecs()
End Function


Function drawMessages(x0:Int = 20, y0:Int = 20)
	
	If(listMsg.IsEmpty()) Then Return
	
	Local px:Int = x0, py:Int = y0
	For Local k:Int = 0 Until listMsg.count()
		px = x0
		strTmp = String(listMsg.ValueAtIndex(k))
		If(Mid(strTmp, 1, 2) = "я:")
			SetColor 255, 255, 0
		ElseIf(Mid(strTmp, 1, 7) = "сервер:")
			SetColor 255, 0, 0
			'px:+5
		Else
			SetColor 255, 255, 255
		EndIf
		DrawText strTmp, px, py
		py:+20
	Next

End Function


Function checkInput:Int()
	ch = GetChar()
	If(ch <> 0)
		text = text + Chr(ch)
		If(ch <> 32) text = text.Trim()
		
		If(ch = 8) 'backspace
			If(Len(text) > 0)
				text = Left(text, Len(text) - 1)
			EndIf
		EndIf
	EndIf
	
	If(ch = 13)
		Return True
	Else
		Return False
	EndIf
End Function


Function send(msg:String)
		
	listMsg.AddLast("я: " + MSG)
	If(listMsg.Count() > maxLines) Then listMsg.Remove(listMsg.First())
	msg:+"~r~n"
	cliSocket.send(msg, Len(msg))
	
End Function


Function recv()
	If(cliSocket.ReadAvail() = 0) Then Return
	
	strTmp = ""
	
	While(cliSocket.ReadAvail() > 0)
		flushBuf(bufData, bufSize)
		cliSocket.Recv(bufData, bufSize)
		strTmp:+String.FromCString(bufData).Trim()
	Wend
	
	strTmp = convertFromAnsi(strTmp)
	
	bufStr = strTmp.Split(Chr(10))
	For Local k:Int = 0 Until bufStr.Length
		If(bufStr[k].Trim() = "") Then Continue
		listMsg.AddLast(bufStr[k].Trim())
		If(listMsg.Count() > maxLines) Then listMsg.Remove(listMsg.First())
	Next

	'разбор команды
	If(strTmp = "ID")
		text = "ID " + String(Rand(1000000))
		send(text)
	Else If(strTmp = "QUIT")
		onRunning = False
	EndIf
	
End Function


utils.bmx
Function flushBuf(buf:Byte Ptr, size:Int)
	For Local k:Int = 0 Until size
		buf[k] = 0
	Next
End Function


Function convertToAnsi:String(text:String)
	Local str:String = ""
	Local count:Int = Len(text)
	For Local k:Int = 0 Until count
		If(text[k] >= 1040 And text[k] <= 1103)
			str:+Chr(text[k] - 1040 + 192)
		ElseIf(text[k] = 1025) 'Ё
			str:+Chr(168)
		ElseIf(text[k] = 1105) 'ё
			str:+Chr(184)
		Else
			str:+Chr(text[k])
		EndIf
	Next
	Return str
End Function


Function convertFromAnsi:String(text:String)
	Local str:String = ""
	Local count:Int = Len(text)
	For Local k:Int = 0 Until count
		If(text[k] >= 192 And text[k] <= 255)
			str:+Chr(text[k] - 192 + 1040)
		ElseIf(text[k] = 168) 'Ё
			str:+Chr(1025)
		ElseIf(text[k] = 184) 'ё
			str:+Chr(1105)
		Else
			str:+Chr(text[k])
		EndIf
	Next
	Return str
End Function


Прилагаю код и скомпиленные файлы.

ПС: Возможно, стоит оформить этот пост в раздел "FAQ и уроки".
Миниатюры
Нажмите на изображение для увеличения
Название: tcp_example.png
Просмотров: 852
Размер:	99.4 Кб
ID:	21170  
Вложения
Тип файла: zip tcp_bmx.zip (1.54 Мб, 402 просмотров)
(Offline)
 
Ответить с цитированием